#pragma once
#include "Processor/file_processor.hpp"
#include "cd_file_types.hpp"
#include <limits.hpp>

namespace JabyEngine {
    namespace FileProcessor {
        namespace Helper {
            template<typename T>
            static Progress exchange_and_execute_process_function(State::GenericProcessRoutine<T> process_routine, State::CDDataProcessor& data_proc, T& state) {
                data_proc.process_routine = reinterpret_cast<State::ProcessRoutine>(process_routine);
                return process_routine(data_proc, state);
            }

            namespace DMA {
                struct WordsReady {
                    uint32_t words_to_use;
                    bool     is_last;

                    static constexpr WordsReady calculate(const State::CDDataProcessor& data_proc, size_t words_left) {
                        const auto config_data_words = (data_proc.data_bytes/sizeof(uint32_t));
                        const auto words_to_use      = (config_data_words > words_left) ? words_left : config_data_words;
            
                        return {
                            .words_to_use = words_to_use,
                            .is_last      = words_to_use == words_left
                        };
                    }
                };

                template<typename T>
                static size_t send_words(size_t words_to_send, bool send_all) {
                    auto blocks_to_send = words_to_send/16;
                    while(blocks_to_send > 0) {
                        const auto block_send = (blocks_to_send > UI16_MAX) ? UI16_MAX : blocks_to_send;

                        // Send data!
                        T::wait();
                        T::Receive::start(blocks_to_send);
                        blocks_to_send -= block_send;
                    }

                    if(send_all) {
                        const auto last_words_to_send = (words_to_send & 0b1111);
                        if(last_words_to_send > 0) {
                            T::wait();
                            T::Receive::start(1, last_words_to_send);
                        }
                        
                        T::wait();
                        T::end();
                        return words_to_send;
                    }

                    else {
                        return (words_to_send & ~0b1111);
                    }
                }
            }  
        }
    }
}