//
// Created by Иван_Архипов on 31.10.2017.
//

#ifndef LOTRO_DAT_PATCHER_DATFILE_H
#define LOTRO_DAT_PATCHER_DATFILE_H

#ifdef LOTRO_DAT_EXPORTS
#define LOTRO_DAT_API __declspec(dllexport)
#else
#define LOTRO_DAT_API __declspec(dllimport)
#endif

#include <fstream>
#include <map>
#include <unordered_map>
#include <set>
#include <vector>
#include <yaml-cpp/node/node.h>
#include "Database.h"

// Dat file names definitions

#define CLIENT_LOCAL_ENGLISH 0
#define CLIENT_GENERAL 1
#define CLIENT_SOUND 2
#define CLIENT_SURFACE 3
#define CLIENT_HIGHRES 4

extern  "C++"
{
namespace LOTRO_DAT
{
    class BinaryData;
    class DatException;
    class SubDirectory;
    class Subfile;
    class SubfileData;

    enum FILE_TYPE : int{
        TEXT,
        JPG,
        DDS,
        WAV,
        OGG,
        FONT,
        UNKNOWN
    };

    enum DAT_STATE {
        CLOSED,
        SUCCESS_OPENED,
        SUCCESS_SUPERBLOCK,
        SUCCESS_DIRECTORIES,
        SUCCESS_DICTIONARY,
        READY
    };

    class DatFile {
        friend class SubDirectory;
    public:
        DatFile();
        explicit DatFile(const char* filename, int dat_id);
        ~DatFile();

        bool ExtractFile(long long file_id, const std::string &path = "");
        bool ExtractFile(long long file_id, Database *db);

        int ExtractAllFilesByType(FILE_TYPE type, std::string path = "");
        int ExtractAllFilesByType(FILE_TYPE type, Database *db);

        bool PatchFile(const char *filename, YAML::Node options);
        bool PatchFile(const SubfileData &data);
        bool PatchAllDatabase(Database *db);

        void WriteUnorderedDictionary(std::string path) const;

        long long constant1() const;
        long long constant2() const;
        long long file_size() const;
        long long version1() const;
        long long version2() const;

        long long files_number() const;

        BinaryData GetFileData(const Subfile* file, long long offset = 0);

        void UpdateSubdirectories();

    private:
        void OpenDatFile(const char* dat_name);
        void ReadSuperBlock();
        void MakeDirectories();
        void MakeDictionary();

        void ReadData(BinaryData &data, long long size, long long offset = 0, long long data_offset = 0);
        void WriteData(const BinaryData &data, long long size, long long offset = 0, long long data_offset = 0);

        void ApplyFilePatch(Subfile *file, const BinaryData &data);
        std::vector<std::pair<long long, long long> > GetFragmentationJournal();
        void UpdateHeader();
        void UpdateFragmentationJournal(const std::vector<std::pair<long long, long long> > &journal);

    private:
        FILE *file_handler_;
        SubDirectory *root_directory_;
        std::unordered_map<long long, Subfile*> dictionary_;
        std::unordered_map<long long, BinaryData *> patched_list;

        long long constant1_;
        long long constant2_;
        long long file_size_;
        long long version1_;
        long long version2_;
        long long root_directory_offset_;
        long long fragmentation_journal_offset_;

        DAT_STATE dat_state_;

        int dat_id_;
        bool patched_;
    };
}
}

#endif //LOTRO_DAT_PATCHER_DATFILE_H
