//
// Created by Иван_Архипов on 30.10.2017.
//
#include <iostream>
#include <ctime>
#include <algorithm>

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

#include "LotroDat.h"
using namespace LOTRO_DAT;
using namespace std;

// 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;

// Change these variables to true if you want export category to databases.
bool exportTextsToDb = false;
bool exportImagesToDb = false;
bool exportFontsToDb = false;
bool exportSoundsToDb = false;
bool exportTexturesToDb = false;
bool exportUnknownToDb = false;
// There is no need to change anything else

int main() {
    std::cout << "Gi1dor's LotRO .dat extractor ver. 7.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;

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

        std::cout << "Using .dat file from dat_file_path.txt...\n";
        std::cout << "Opening file " << filename << std::endl;

        auto operation = file.Initialise(filename, 0);
        if (operation.result == false)
            std::cout << "Dat initialisation failed. Error message: " << operation.msg << "\n";
    }

    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 << "Opening file " << filename << std::endl;

        auto operation = file.Initialise(filename, 0);
        if (operation.result == false)
            std::cout << "Dat initialisation failed. Error message: " << operation.msg << "\nTo try again enter path to .dat file\n";
    }

    std::cout << "Great! File initialised successfully!\n";
    std::cout << "Gathering file information...";
    file.GatherInformation("dat_info.log");

    int cmd = 0;
    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";

        if (cmd != 3) {
            std::cout << "Enter number of command (1-3): ";
            std::cin >> cmd;

            std::string tmp;
            std::getline(std::cin, tmp);
        }

        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.getIO().GetFilename().value;
            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"));
                std::cout << "Extracted " << file.getExporter().ExtractAllFilesByType(JPG, &output_db).value << " .jpg files to Images.db" << std::endl << std::flush;
                output_db.CloseDatabase();
            }

            if (exportSoundsToDb) {
                output_db.InitDatabase(output_dir + std::string("Sounds.db"));
                std::cout << "Extracted " << file.getExporter().ExtractAllFilesByType(WAV, &output_db).value << " .wav files to Sounds.db" << std::endl << std::flush;
                std::cout << "Extracted " << file.getExporter().ExtractAllFilesByType(OGG, &output_db).value << " .ogg files to Sounds.db" << std::endl << std::flush;
                output_db.CloseDatabase();
            }

            if (exportTextsToDb) {
                output_db.InitDatabase(output_dir + std::string("Texts.db"));
                std::cout << "Extracted " << file.getExporter().ExtractAllFilesByType(TEXT, &output_db).value << " text files to Texts.db" << std::endl << std::flush;
                output_db.CloseDatabase();
            }

            if (exportFontsToDb) {
                output_db.InitDatabase(output_dir + std::string("Fonts.db"));
                std::cout << "Extracted " << file.getExporter().ExtractAllFilesByType(FONT, &output_db).value << " font files to Fonts.db" << std::endl << std::flush;
                output_db.CloseDatabase();
            }

            if (exportTexturesToDb) {
                output_db.InitDatabase(output_dir + std::string("Textures.db"));
                std::cout << "Extracted " << file.getExporter().ExtractAllFilesByType(DDS, &output_db).value << " .dds files to Textures.db" << std::endl << std::flush;
                output_db.CloseDatabase();
            }

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

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

            if (exportSoundsToFiles) {
                mkdir((output_dir + "wav").c_str(), 744);
                std::cout << "Extracted " << file.getExporter().ExtractAllFilesByType(WAV, output_dir + "wav\\").value << " .wav files to directory" << std::endl << std::flush;
                mkdir((output_dir + "ogg").c_str(), 744);
                std::cout << "Extracted " << file.getExporter().ExtractAllFilesByType(OGG, output_dir + "ogg\\").value << " .ogg files to directory" << std::endl << std::flush;
            }

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

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

            if (exportTextsToFiles) {
                mkdir((output_dir + "texts").c_str(), 744);
                std::cout << "Extracted " << file.getExporter().ExtractAllFilesByType(TEXT, output_dir + "texts\\").value << " 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.Deinitialize();

            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 << "Opening file " << filename << std::endl;

                auto operation = file.Initialise(filename, 0);
                if (operation.result == false) {
                    std::cout << "Some error caused while opening the file... "
                            "Could you enter .dat filename once more?" << std::endl;
                    std::cout << "Error message: " << operation.msg << std::endl;
                    file.Deinitialize();
                }
            }
        }

        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.Deinitialize();
    system("pause");
    return 0;
}

