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

#include "Subfile.h"
#include "BinaryData.h"
#include "DatFile.h"
#include "DatException.h"
#include "SubfileData.h"

#include <algorithm>

const long long MAXSIZE = 50ll * 1024ll * 1024ll; // Setting maximal file size 50 MB; Files with size more than 50 mb
                                                  // will be recognized as incorrect and passed.
                                                  // This should be done because of not completely correct implementation
                                                  // of Subfiles and Subdirectories search in DatFile.
namespace LOTRO_DAT {

    Subfile::Subfile() = default;

    Subfile::Subfile(DatFile *dat, const BinaryData &header) {
        category = 0;

        dat_ = dat;

        fragments_count_ = header.ToNumber<4>(0); // fragments_count
        unknown1_ = header.ToNumber<4>(4); // unknown1
        file_id_ = header.ToNumber<4>(8); // file_id
        file_offset_ = header.ToNumber<4>(12); // file_offset
        file_size_ = header.ToNumber<4>(16); // block_size
        timestamp_ = header.ToNumber<4>(20); // timestamp
        version_ = header.ToNumber<4>(24); // version
        block_size_ = header.ToNumber<4>(28); // block_size
    }

    Subfile::Subfile(DatFile *dat, long long dictionary_offset, 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) :
            category(0), dat_(dat), dictionary_offset_(dictionary_offset), fragments_count_(fragments_count), unknown1_(unknown1), file_id_(file_id),
            file_offset_(file_offset),
            file_size_(file_size), timestamp_(timestamp), version_(version), block_size_(block_size) {

        if (file_size_ > MAXSIZE)
            throw DatException("Bad Subfile::Subfile() - File size is too much... Maybe it's incorrect..?",
                               SUBFILE_EXCEPTION);
    }

    /// Typical getters of private fields, if there's need for getting information about SubFile from outside class.


    long long Subfile::dictionary_offset() const {
        return dictionary_offset_;
    }

    long long Subfile::fragments_count() const {
        return fragments_count_;
    }

    long long Subfile::unknown1() const {
        return unknown1_;
    }

    long long Subfile::file_id() const {
        return file_id_;
    }

    long long Subfile::file_offset() const {
        return file_offset_;
    }

    long long Subfile::file_size() const {
        return file_size_;
    }

    long long Subfile::timestamp() const {
        return timestamp_;
    }

    long long Subfile::version() const {
        return version_;
    }

    long long Subfile::block_size() const {
        return block_size_;
    }

    /// bool Subfile::FileType(...);
    /// Virtual function, can (and should) be redefined in child class, otherwise an exception will be thrown while exporting/importing file.
    /// Returns enum FILE_TYPE value, which is declared in DatFile.h

    FILE_TYPE Subfile::FileType() const {
        throw DatException("Bad Subfile::FileType() - function is not implemented for this type.", SUBFILE_EXCEPTION);
    }

    /// std::string Subfile::Extension()
    /// Virtual function, can (and should) be redefined in child class, otherwise an exception will be thrown while exporting/importing file.
    /// Returns std::string with extension, beggined with '.', ex. ".jpg", ".txt" and so on;

    std::string Subfile::Extension() const {
        throw DatException("Bad Subfile::Extension() - function is not implemented for this type.", SUBFILE_EXCEPTION);
    }

    /// bool Subfile::PrepareForExport(...);
    /// Virtual function, can be redefined in child class, otherwise an exception will be thrown while exporting file.
    /// Takes constant BinaryData& file_data, which contains all file data in .dat file, except first 8 bytes before file_id.
    /// Takes references to export_size - amount of exported files/strings, and content of exported data such as:
    ///  1) field binary_data - exported as RAW data
    ///  2) field text_data - UTF-16 text, exporting in UTF-8
    ///  3) field options - YAML field, which consists of some parameters of file such as file_id, extension and so on.
    /// Returns true if preparation was success. Otherwise returns false;

    SubfileData Subfile::PrepareForExport(const BinaryData &file_data) {
        throw DatException("Bad Subfile::PrepareForExport() - function is not implemented for this type.", EXPORT_EXCEPTION);
    }


    /// BinaryData Subfile::PrepareForImport(...);
    /// Virtual function, can be redefined in child class, otherwise an exception will be thrown while importing file.
    /// Takes constant BinaryData& file_data, which contains all file data in .dat file, including first 8 bytes befire file_id.
    ///  1) const field binary_data - exported as RAW data
    ///  2) const field text_data - UTF-16 text, exporting in UTF-8
    ///  3) const field options - YAML field, which consists of some parameters of file such as file_id, extension and so on.
    /// Returns BinaryData - bytes array, prepared for writing in .dat file

    BinaryData Subfile::MakeForImport(const BinaryData &old_data, const SubfileData &data) {
        throw DatException("Bad Subfile::MakeForImport() - function is not implemented for this type.", IMPORT_EXCEPTION);
    }

    BinaryData Subfile::MakeHeaderData() const {
        BinaryData header = BinaryData::FromNumber<4>(fragments_count_)
                            + BinaryData::FromNumber<4>(unknown1_)
                            + BinaryData::FromNumber<4>(file_id_)
                            + BinaryData::FromNumber<4>(file_offset_)
                            + BinaryData::FromNumber<4>(file_size_)
                            + BinaryData::FromNumber<4>(timestamp_)
                            + BinaryData::FromNumber<4>(version_)
                            + BinaryData::FromNumber<4>(block_size_);
        return header;
    }
};