#include <iostream>
#include <fstream>
#include <ctime>
#include <algorithm>

#ifdef _WIN32
#include <direct.h>
#define mkdir(dir, mode) _mkdir(dir)
#endif

#include "datfile.h"
using namespace LOTRO_DAT;

// Change these variables to true if you want export category to files.
bool exportTextsToFiles = false;
bool exportImagesToFiles = false;
bool exportFontsToFiles = false;
bool exportSoundsToFiles = false;
bool exportTexturesToFiles = false;
bool exportUnknownToFiles = false;

bool exportTextsToDb = false;
bool exportImagesToDb = false;
bool exportFontsToDb = false;
bool exportSoundsToDb = false;
bool exportTexturesToDb = false;
bool exportUnknownToDb = false;

void InitializeFileLoop(DatFile& file) {
    while (!file.Initialized()) {
        std::cout << "Please, tell, where the .dat file is\n";
        std::cout << "Enter path to file (including filename): ";
        std::string filename;
        std::getline(std::cin, filename);

        // std::cout << "Enter file id: ";
        // std::string file_id_str;
        // std::getline(std::cin, file_id_str);
        // int file_id = 0;
        // try {
        //     file_id = stoi(file_id_str);
        // } catch (std::invalid_argument) {
        //     std::cout << "Invalid dat file id entered!\n\n";
        //     continue;
        // }

        std::cout << "Opening file " << filename << std::endl;

        bool result = file.Init(filename); // TODO: INIT WITH DAT FILE ID
        if (!result) {
            std::cout << "Dat initialisation failed.\nPlease enter path to .dat file once more\n";
        }
    }
}

int main() {
    std::cout.precision(1);
    std::cout << std::fixed;
    std::cout << "Endevir's LotRO .dat extractor ver. 1.0.0" << std::endl;

    std::cout << "Hello! I'm a basic shell version of .dat file extractor. I can open .dat file directly, "
            "if you write path to it (with name of file) in file \"dat_file_path.txt\"\n";
    
    DatFile file(0);

    std::ifstream in_stream("dat_file_path.txt");
    if (!in_stream.fail()) {
        std::string filename;
        std::getline(in_stream, filename);

        // std::string file_id_str;
        // std::getline(in_stream, file_id_str);
        // int file_id = 0;
        // try {
        //     file_id = stoi(file_id_str);
        // } catch (std::invalid_argument) {
        //     file_id = -1;
        //     std::cout << "Invalid file_id on second line of file dat_file_path.txt!\n\n";
        // }

        // if (file_id != -1) {
            std::cout << "Using .dat file from dat_file_path.txt...\n";
            std::cout << "Opening file " << filename << std::endl;
            bool result = file.Init(filename);
            if (!result) {
                std::cout << "Dat initialisation failed!\n";
            }
        // }
    }

    InitializeFileLoop(file);
    std::cout << "Great! File initialised successfully!\n";
    while (true) {
        std::cout << "Please, choose, what should I do. I can extract .dat file to files (press 1), "
                    "open another .dat file (press 2), or configure, what should I extract. Choose, what to do or exit (press -1)\n";

        std::cout << "Enter number of command (1-3): ";
        std::string command;
        std::getline(std::cin, command);

        int cmd = -10;
        try {
            cmd = stoi(command);
        } catch (std::invalid_argument) {
            std::cout << "Invalid command entered!\n\n";
            continue;
        }

        if (cmd == -1) {
            std::cout << "Exiting. Thanks for using me!\n";
            break;
        }

        if (cmd == 1) {
            const clock_t begin_time = clock();

            mkdir("Extracted data", 744);

            std::time_t result = std::time(nullptr);
            char *ttime = std::asctime(std::localtime(&result));
            auto *out_time = new char[25];
            memcpy(out_time, ttime, 24);
            out_time[24] = '\0';

            std::string filename = file.GetFilename();
            std::cout << "FILENAME = " << filename << std::endl;
            std::string output_dir = std::string("Extracted data\\") + filename.substr(std::max(filename.length() - 16, 0u), 16) + " " + std::string(out_time) + "\\";
            std::replace(output_dir.begin(), output_dir.end(), ':', '-');

            mkdir(output_dir.c_str(), 744);
            
            std::cout << "Beginning unpacking... Please, wait for some minutes."
                    "\nMaybe it's a good idea to have a cup of tea, while unpacker is working...\n" << std::flush;

            Database output_db;

            if (exportImagesToDb) {
                output_db.InitDatabase(output_dir + std::string("Images.db"));
                int extracted_jpg_files_num = file.ExportFilesByType(JPG, output_db);
                std::cout << "Extracted " << extracted_jpg_files_num << " .jpg files to Images.db" << std::endl << std::flush;
                output_db.CloseDatabase();
            }

            if (exportSoundsToDb) {
                output_db.InitDatabase(output_dir + std::string("Sounds.db"));
                int extracted_wav_files_num = file.ExportFilesByType(WAV, output_db);
                int extracted_ogg_files_num = file.ExportFilesByType(OGG, output_db);
                std::cout << "Extracted " << extracted_wav_files_num << " .wav files to Sounds.db" << std::endl << std::flush;
                std::cout << "Extracted " << extracted_ogg_files_num << " .ogg files to Sounds.db" << std::endl << std::flush;
                output_db.CloseDatabase();
            }

            if (exportTextsToDb) {
                output_db.InitDatabase(output_dir + std::string("Texts.db"));
                int extracted_text_files_num = file.ExportFilesByType(TEXT, output_db);
                std::cout << "Extracted " << extracted_text_files_num << " text files to Texts.db" << std::endl << std::flush;
                output_db.CloseDatabase();
            }

            if (exportFontsToDb) {
                output_db.InitDatabase(output_dir + std::string("Fonts.db"));
                int extracted_font_files_num = file.ExportFilesByType(FONT, output_db);
                std::cout << "Extracted " << extracted_font_files_num << " font files to Fonts.db" << std::endl << std::flush;
                output_db.CloseDatabase();
            }

            if (exportTexturesToDb) {
                output_db.InitDatabase(output_dir + std::string("Textures.db"));
                int extracted_dds_files_num = file.ExportFilesByType(DDS, output_db);
                std::cout << "Extracted " << extracted_dds_files_num << " .dds files to Textures.db" << std::endl << std::flush;
                output_db.CloseDatabase();
            }

            if (exportImagesToFiles) {
                mkdir((output_dir + "jpg").c_str(), 744);
                int extracted_jpg_files_num = file.ExportFilesByType(JPG, output_dir + "jpg\\");
                std::cout << "Extracted " << extracted_jpg_files_num << " .jpg files to directory" << std::endl << std::flush;
            }

            if (exportTexturesToFiles) {
                mkdir((output_dir + "dds").c_str(), 744);
                int extracted_dds_files_num = file.ExportFilesByType(DDS, output_dir + "dds\\");
                std::cout << "Extracted " << extracted_dds_files_num << " .dds files to directory" << std::endl << std::flush;
            }

            if (exportSoundsToFiles) {
                mkdir((output_dir + "wav").c_str(), 744);
                int extracted_wav_files_num = file.ExportFilesByType(WAV, output_dir + "wav\\");
                std::cout << "Extracted " << extracted_wav_files_num << " .wav files to directory" << std::endl << std::flush;
                mkdir((output_dir + "ogg").c_str(), 744);
                int extracted_ogg_files_num = file.ExportFilesByType(OGG, output_dir + "ogg\\");
                std::cout << "Extracted " << extracted_ogg_files_num << " .ogg files to directory" << std::endl << std::flush;
            }

            if (exportFontsToFiles) {
                mkdir((output_dir + "fonts").c_str(), 744);
                int extracted_font_files_num = file.ExportFilesByType(FONT, output_dir + "fonts\\");
                std::cout << "Extracted " << extracted_font_files_num << " font files to directory" << std::endl << std::flush;
            }

            if (exportUnknownToFiles) {
                mkdir((output_dir + "unknown").c_str(), 744);
                int extracted_unknown_files_num = file.ExportFilesByType(UNKNOWN, output_dir + "unknown\\");
                std::cout << "Extracted " << extracted_unknown_files_num << " unknown files to directory" << std::endl << std::flush;
            }

            if (exportTextsToFiles) {
                mkdir((output_dir + "texts").c_str(), 744);
                int extracted_text_files_num = file.ExportFilesByType(TEXT, output_dir + "texts\\");
                std::cout << "Extracted " << extracted_text_files_num << " text files to directory" << std::endl << std::flush;
            }

            fprintf(stdout, "Spent %f seconds on running unpacker! Thank you for your patience!\n",
                    float(clock() - begin_time) / CLOCKS_PER_SEC);
        }

        if (cmd == 2) {
            std::cout << "Closing file...\n";
            file.Deinit();
            InitializeFileLoop(file);
        }

        if (cmd == 3) {
            std::cout << "Current parameters:\n";
            std::cout << "(01) Export texts to files - " << (exportTextsToFiles ? "true\n" : "false\n");
            std::cout << "(02) Export texts to database - " << (exportTextsToDb ? "true\n" : "false\n");

            std::cout << "(11) Export images to files - " << (exportImagesToFiles ? "true\n" : "false\n");
            std::cout << "(12) Export images to database - " << (exportImagesToDb ? "true\n" : "false\n");

            std::cout << "(21) Export fonts to files - " << (exportFontsToFiles ? "true\n" : "false\n");
            std::cout << "(22) Export fonts to database - " << (exportFontsToDb ? "true\n" : "false\n");

            std::cout << "(31) Export sounds to files - " << (exportSoundsToFiles ? "true\n" : "false\n");
            std::cout << "(32) Export sounds to database - " << (exportSoundsToDb ? "true\n" : "false\n");

            std::cout << "(41) Export textures to files - " << (exportTexturesToFiles ? "true\n" : "false\n");
            std::cout << "(42) Export textures to database - " << (exportTexturesToDb ? "true\n" : "false\n");

            std::cout << "(51) Export unknown files to files - " << (exportUnknownToFiles ? "true\n" : "false\n");
            std::cout << "(52) Export unknown files to database - " << (exportUnknownToDb ? "true\n" : "false\n");

            std::cout << "Enter number of parameter to change (or -1 to exit this menu): ";

            int number = 0;

            std::cin >> number;
            std::string tmp;
            std::getline(std::cin, tmp);

            if (number == -1) {
                std::cout << "Returning to main menu..\n";
                cmd = 0;
            } else {
                switch (number) {
                    case 01:
                        exportTextsToFiles = !exportTextsToFiles;
                        break;
                    case 02:
                        exportTextsToDb = !exportTextsToDb;
                        break;
                    case 11:
                        exportImagesToFiles = !exportImagesToFiles;
                        break;
                    case 12:
                        exportImagesToDb = !exportImagesToDb;
                        break;
                    case 21:
                        exportFontsToFiles = !exportFontsToFiles;
                        break;
                    case 22:
                        exportFontsToDb = !exportFontsToDb;
                        break;
                    case 31:
                        exportSoundsToFiles = !exportSoundsToFiles;
                        break;
                    case 32:
                        exportSoundsToDb = !exportSoundsToDb;
                        break;
                    case 41:
                        exportTexturesToFiles = !exportTexturesToFiles;
                        break;
                    case 42:
                        exportTexturesToDb = !exportTexturesToDb;
                        break;
                    case 51:
                        exportUnknownToFiles = !exportTexturesToFiles;
                        break;
                    case 52:
                        exportUnknownToDb = !exportTexturesToDb;
                        break;
                    default:
                        std::cout << "Incorrect number. Returning to main menu..\n";
                }
            }
        }
    }

    file.Deinit();
    system("pause");
    return 0;
}
