#pragma once
#include "../../Auxiliary/types.hpp"
#include "../cd_file_types.hpp"

namespace JabyEngine {
    namespace FileProcessor {
        class State {
        public:
            struct Reserved {
                uint32_t reserved[8];     
            };

            struct CDDataProcessor;

            template<typename T>
            using GenericProcessRoutine = Progress (*)(CDDataProcessor&, T&);

            typedef GenericProcessRoutine<Reserved> ProcessRoutine;

            struct CDDataProcessor {
                ProcessRoutine process_routine = nullptr;
                const uint8_t* data_adr        = nullptr;
                size_t         data_bytes      = 0ull;

                template<typename T>
                static __always_inline CDDataProcessor from(GenericProcessRoutine<T> process_routine, const uint8_t* data_adr) {
                    return {reinterpret_cast<ProcessRoutine>(process_routine), data_adr};
                }
                
                template<typename T>
                T simple_read_r() {
                    static constexpr size_t T_SIZE = sizeof(T);

                    T value = *reinterpret_cast<const T*>(this->data_adr);
                    CDDataProcessor::processed(T_SIZE);
                    return value;
                }

                constexpr void processed(size_t bytes) {
                    this->data_adr   += bytes;
                    this->data_bytes -= bytes;
                }

                constexpr void skip(size_t bytes) {
                    CDDataProcessor::processed(bytes);
                }
            };

            CDDataProcessor data_proc;
            Reserved        reserved;

            template<typename T>
            static __always_inline State from(const T& state, const uint32_t* data_adr, GenericProcessRoutine<T> process_routine) {
                return {CDDataProcessor::from(process_routine, reinterpret_cast<const uint8_t*>(data_adr)), *reinterpret_cast<const Reserved*>(&state)};
                static_assert(sizeof(T) <= sizeof(Reserved));
            }

        public:
            Progress process(size_t bytes_ready) {
                this->data_proc.data_bytes += bytes_ready;
                return (*this->data_proc.process_routine)(this->data_proc, this->reserved);
            }
        };

        // The nothing state
        State create(const uint32_t* data_adr, const Nothing& nothing);
        State create(const uint32_t* data_adr, const SimpleTIM& file);
        State create(const uint32_t* data_adr, const TIM& file);
        State create(const uint32_t* data_adr, const VAG& file);

        State create_custom(const uint32_t* data_adr, const CDFileType_t& file_type, const CDFile::Payload& payload);
    }
}