#pragma once
#include "../TypeDef.h"
// #include "../game/Log.h"

#include "bx/string.h"
#include <bx/file.h>
#include <cstdint>

#ifndef LOG_ERROR
#define LOG_ERROR(...)
#endif

namespace Generated
{
    struct EmbeddedTypeDef
    {
        uint32_t Size = sizeof(Def::DefinitionFile);
        Def::DefinitionFile Data;
    };

    struct Serializer
    {
        bx::Error Err;
        bx::FilePath Path;
        bx::FileWriter Writer;

        bool Init(const bx::FilePath& path)
        {
            Path = path;
            Writer.open(path, false, &Err);
            return Err.isOk();
        }

        template <typename T> bool WriteT(const char* _4cc, const T& data)
        {
            if (!Write(_4cc, 4)) return false;

            uint32_t hash = data.Hash;
            if (!Write(&hash, sizeof(hash))) return false;

            uint32_t size = sizeof(T);
            if (!Write(&size, sizeof(size))) return false;

            return Save(&data, 1, *this);
        }

        bool Write(const void* data, uint32_t size)
        {
            Writer.write(data, size, &Err);
            if (!Err.isOk()) LOG_ERROR("Write error: %s", Err.getMessage().getCPtr());
            return Err.isOk();
        }

        void Finish()
        {
            Writer.close();
        }
    };

    struct Deserializer
    {
        bx::Error Err;
        bx::FilePath Path;
        bx::FileReader Reader;

        bool Init(const bx::FilePath& path)
        {
            Path = path;
            Reader.open(path, &Err);
            return Err.isOk();
        }

        bool Read(void* data, uint32_t size)
        {
            Reader.read(data, size, &Err);
            if (!Err.isOk()) LOG_ERROR("Read error: %s", Err.getMessage().getCPtr());
            return Err.isOk();
        }

        template <typename T> bool ReadT(const char* _4cc, T& data)
        {
            char magic[5]{0};
            if (!Read(magic, 4)) return false;

            bx::StringView given{_4cc, 4};
            bx::StringView loaded{magic, 4};
            if (bx::strCmp(given, loaded) != 0)
            {
                LOG_ERROR("Magic mismatch! %s != %s", _4cc, magic);
                return false;
            }

            uint32_t hash = 0;
            if (!Read(&hash, sizeof(hash))) return false;
            if (data.Hash != hash)
            {
                LOG_ERROR("Hash mismatch! %u != %u", data.Hash, hash);
                return false;
            }

            uint32_t size = 0;
            if (!Read(&size, sizeof(size))) return false;
            if (sizeof(T) != size)
            {
                LOG_ERROR("Size mismatch! %u != %u", sizeof(T), size);
                return false;
            }

            if (!Load(&data, 1, *this))
            {
                LOG_ERROR("Failed to load: %s", Err.getMessage().getCPtr());
                return false;
            }
            return true;
        }

        void Finish()
        {
            Reader.close();
        }
    };

    template <typename T> bool Save(const T* obj, uint32_t count, Serializer& serializer);
    template <typename T> bool Load(T* obj, uint32_t count, Deserializer& serializer);
} // namespace Generated
