#include "tim_helper.hpp"

namespace JabyEngine {
    namespace TIMFileProcessor {
        namespace {
            void set_gpu_receive(const uint32_t* src, uint16_t x, uint16_t y, uint16_t w, uint16_t h) {
                GPU::internal::DMA::Receive::prepare();
                GPU::internal::DMA::Receive::set_dst(PositionU16::create(x, y), SizeU16::create(w, h)); 
                GPU::internal::DMA::Receive::set_src(reinterpret_cast<const uintptr_t>(src));
            }

            size_t set_gpu_receive_data(const uint32_t* src, const AreaU16& dst) {
                const auto width  = dst.size.width;
                const auto height = dst.size.height;

                set_gpu_receive(src, dst.position.x, dst.position.y, width, height);
                return (width*height)/2;
            }

            Progress parse_data(State::CDDataProcessor& data_proc, GenericTIM& generic_tim) {
                const auto [words_to_use, is_last] = Helper::DMA::WordsReady::calculate(data_proc, generic_tim.words_left);
                const auto words_used              = Helper::DMA::send_words<GPU::internal::DMA>(words_to_use, is_last);

                generic_tim.words_left -= words_used;
                data_proc.processed(words_used*sizeof(uint32_t));
                return is_last ? Progress::Done : Progress::InProgress;
            }

            Progress switch_state_parse_data(State::CDDataProcessor& data_proc, GenericTIM& generic_tim) {
                const auto result = generic_tim.pre_data_parsing(data_proc);
                if(result == Progress::Done) {
                    generic_tim.words_left = TIMFileProcessor::set_gpu_receive_data(reinterpret_cast<const uint32_t*>(data_proc.data_adr), generic_tim.tex_area);
                    return Helper::exchange_and_execute_process_function<TIMFileProcessor::GenericTIM>(TIMFileProcessor::parse_data, data_proc, generic_tim);
                }

                return result;
            }

            Progress parse_clut(State::CDDataProcessor& data_proc, GenericTIM& generic_tim) {
                if(const auto result = TIMFileProcessor::parse_data(data_proc, generic_tim); result != Progress::Done) {
                    return result;
                }

                else {
                    return switch_state_parse_data(data_proc, generic_tim);
                }
            }
        }

        Progress parse_header(State::CDDataProcessor& data_proc, GenericTIM& generic_tim) {
            const auto result = generic_tim.parse_header(data_proc);
            if(result == Progress::Done) {
                //Check if we have a clut to care about
                if(generic_tim.has_clut()) {
                    //CLUTs are 16bit full color anyway
                    generic_tim.words_left = TIMFileProcessor::set_gpu_receive_data(reinterpret_cast<const uint32_t*>(data_proc.data_adr), generic_tim.clut_area);
                    return Helper::exchange_and_execute_process_function(parse_clut, data_proc, generic_tim);
                }

                else {
                    return switch_state_parse_data(data_proc, generic_tim);
                }
            }
            return result;
        }
    }
}