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

#include "TextSubfile.h"
#include "../BinaryData.h"
#include "../DatFile.h"
#include "../Common/DatException.h"

namespace LOTRO_DAT {
    TextSubfile::TextSubfile() = default;

    TextSubfile::TextSubfile(DatFile *dat, long long fragments_count, long long unknown1,
                           long long file_id, long long file_offset, long long file_size,
                           long long timestamp,
                           long long version, long long block_size)
            : Subfile(dat, fragments_count, unknown1, file_id, file_offset, file_size, timestamp, version, block_size) {
    }

    FILE_TYPE TextSubfile::FileType() const {
        return TEXT;
    }

    std::string TextSubfile::Extension() const {
        return std::string(".txt");
    }

    bool TextSubfile::PrepareForExport(const BinaryData &file_data, long long &export_size, std::vector<BinaryData> &binary_data,
                                       std::vector<std::u16string> &text_data, std::vector<YAML::Node> &options) {
        export_size = 0;
        binary_data.clear();
        text_data.clear();
        options.clear();

        if (file_size() <= 10) // File is empty, nothing to do;
            return false;

        long long offset = 9; // first 4 bytes - file_id, then 4 bytes - unknown, then 1 byte - unknown

        long long text_fragment_num = file_data.ToNumber<1>(offset);
        if ((text_fragment_num & 0x80) != 0) {
            text_fragment_num = (((text_fragment_num ^ 0x80) << 8) | file_data.ToNumber<1>(offset + 1));
            offset += 1;
        }
        offset += 1;

        for (long long i = 0; i < text_fragment_num; i++) {
            long long fragment_id = file_data.ToNumber<8>(offset);
            offset += 8;

            std::vector<std::u16string> text_pieces = MakePieces(file_data, offset);
            std::vector<long long> arg_references = MakeArgumentReferences(file_data, offset);
            std::vector<std::vector<BinaryData>> arg_strings = MakeArgumentStrings(file_data, offset);

            std::u16string text = u"[";
            for (size_t j = 0; j + 1 < text_pieces.size(); j++)
                text += text_pieces[j] + u"<--DO_NOT_TOUCH!-->";
            text += text_pieces[text_pieces.size() - 1] + u"]";

            std::string arguments;
            for (size_t j = 0; j + 1 < arg_references.size(); j++)
                arguments += std::to_string(arg_references[j]) + "-";
            if (!arg_references.empty())
                arguments += std::to_string(arg_references[arg_references.size() - 1]);

            binary_data.emplace_back(BinaryData());
            text_data.emplace_back(text);
            options.emplace_back(YAML::Node());

            options[export_size]["file_id"] = file_id();
            options[export_size]["gossip_id"] = fragment_id;
            options[export_size]["extension"] = Extension();
            if (!arg_references.empty())
                options[export_size]["arguments"] = arguments;

            ++export_size;
        }
        return true;
    }

    BinaryData TextSubfile::MakeForImport(const BinaryData &old_data, const BinaryData &binary_data, const std::u16string &text_data,
                               const YAML::Node &options) {
        return Subfile::MakeForImport(old_data, binary_data, text_data, options);
    }

    std::vector<std::u16string> TextSubfile::MakePieces(const BinaryData &data, long long &offset) {
        long long num_pieces = data.ToNumber<4>(offset);
        offset += 4;

        std::vector<std::u16string> text_pieces;

        for (long long j = 0; j < num_pieces; j++) {
            long long piece_size = data.ToNumber<1>(offset);
            if ((piece_size & 128) != 0) {
                piece_size = (((piece_size ^ 128) << 8) | data.ToNumber<1>(offset + 1));
                offset += 1;
            }
            offset += 1;

            BinaryData piece_data = data.CutData(offset, offset + piece_size * 2);
            std::u16string piece;

            for (long long k = 0; k < piece_size; k++) {
                char16_t c = char16_t(((short(piece_data[2 * k + 1])) << 8) | (short(piece_data[2 * k])));
                piece += c;
            }

            text_pieces.push_back(piece);
            offset += piece_size * 2;
        }
        return text_pieces;
    }

    std::vector<long long> TextSubfile::MakeArgumentReferences(const BinaryData &data, long long &offset) {
        std::vector<long long> arg_references;

        long long num_references = data.ToNumber<4>(offset);
        offset += 4;

        for (long long j = 0; j < num_references; j++) {
            arg_references.emplace_back(data.ToNumber<4>(offset));
            offset += 4;
        }

        return arg_references;
    }

    std::vector<std::vector<BinaryData>> TextSubfile::MakeArgumentStrings(const BinaryData &data, long long &offset) {
        std::vector<std::vector<BinaryData> > arg_strings;
        long long num_arg_strings = data.ToNumber<1>(offset);
        offset += 1;

        for (long long j = 0; j < num_arg_strings; j++) {
            long long num_args = data.ToNumber<4>(offset);
            offset += 4;

            arg_strings.emplace_back();
            for (long long k = 0; k < num_args; k++) {
                long long string_size = data.ToNumber<1>(offset);
                if ((string_size & 0x80) != 0) {
                    string_size = (((string_size ^ 0x80) << 8) | data.ToNumber<1>(offset + 1));
                    offset += 1;
                }
                offset += 1;

                arg_strings[j].emplace_back(data.CutData(offset, offset + string_size * 2));
                offset += string_size * 2;
            }
        }
        return arg_strings;
    }
};