#include "sort_controller.hpp"
#include "basic_sorts.hpp"
#include "trees.hpp"
#include <algorithm>
#include <chrono>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

// Initialization function for SortController class
SortController::SortController()
{
    lineCount = 0;
    currentType = INSERTION;
    defaultFile = 0;
    defaultOnly = 1;
    fileGiven = 0;
    allLists = 0;
    sortGiven = 0;
}

// Checks for command line arguments
void SortController::CheckArguments(int argc, char* arguments[])
{
    std::string tempStr;
    for (int i = 0; i < argc; i++)
    {
        tempStr = arguments[i];
        if ((tempStr == "-a") || (tempStr == "--all"))
        {
            defaultOnly = 0;
            allLists = 1;
        }
        if ((tempStr == "-f") || (tempStr == "--filename"))
        {
            filename = arguments[i + 1];
            defaultOnly = 0;
            fileGiven = 1;
        }
        if ((tempStr == "-l") || (tempStr == "--locate"))
        {
            wordToLocate = arguments[i + 1];
            std::transform(wordToLocate.begin(), wordToLocate.end(), wordToLocate.begin(), ::toupper);
            locate = 1;
        }
        if ((tempStr == "-d") || (tempStr == "--default"))
        {
            filename = "test/PERM/perm15K.txt";
            defaultFile = 1;
        }
        if ((tempStr == "-s") || (tempStr == "--sort-type"))
        {
            sortGiven = 1;
            tempStr = arguments[i + 1];
            if (tempStr == "bst")
                currentType = BST;
            if (tempStr == "rbt")
                currentType = RBT;
            if (tempStr == "insertion")
                currentType = INSERTION;
            if (tempStr == "merge")
                currentType = MERGE;
            if (tempStr == "heap")
                currentType = HEAP;
            if (tempStr == "all")
                sortGiven = 0;
        }
    }
    if (defaultOnly)
        filename = "test/PERM/perm15K.txt";
    return;
}

// Sets word list found in file into vector 
void SortController::ReadWordFile(void)
{
    std::string bufferStr;
    std::ifstream file(this->filename);
    if (!file.is_open())
    {
        std::cout << "Failed opening file: " << this->filename << '\n';
        exit(1);
    }
    while (getline(file, bufferStr))
    {
        originalWordList.push_back(bufferStr);
        lineCount++;
    }
    return;
}

// Main function for calling all sort categories
void SortController::RunBenchmarks(void)
{
    if (defaultOnly || fileGiven)
    {
        ReadWordFile();
        Benchmarking();
    }
    if (allLists)
        BenchmarkingAll();
    return;
}


// Function for starting sort functions
void SortController::Benchmarking(void)
{
    if (!sortGiven)
        currentType = INSERTION;
    if (currentType == INSERTION)
    {
        newWordList = originalWordList;
        auto start = std::chrono::system_clock::now();
        basic_sorts::InsertionSort(&newWordList);
        auto end = std::chrono::system_clock::now();
        sortTime = end - start;
        OutputResult();
    }
    if (!sortGiven)
        currentType = MERGE;
    if (currentType == MERGE)
    {
        newWordList = originalWordList;
        auto start = std::chrono::system_clock::now();
        basic_sorts::MergeSort(&newWordList);
        auto end = std::chrono::system_clock::now();
        sortTime = end - start;
        OutputResult();
    }
    if (!sortGiven)
        currentType = HEAP;
    if (currentType == HEAP)
    {
        newWordList = originalWordList;
        auto start = std::chrono::system_clock::now();
        basic_sorts::HeapSort(&newWordList);
        auto end = std::chrono::system_clock::now();
        sortTime = end - start;
        OutputResult();
    }
    if (currentType == BST)
    {
        newWordList = originalWordList;
        tree_implementation::BinarySearchTree newTree;
        auto start = std::chrono::system_clock::now();
        newTree.InsertWordList(&newWordList);
        auto end = std::chrono::system_clock::now();
        sortTime = end - start;
        OutputResult();
        start = std::chrono::system_clock::now();
        newTree.Search(wordToLocate);
        end = std::chrono::system_clock::now();
        sortTime = end - start;
        EchoSortTime("BST");
        newTree.PrintPathToRoot(wordToLocate);
    }
    if (currentType == RBT)
    {
        newWordList = originalWordList;
        tree_implementation::RedBlackTree newTree;
        auto start = std::chrono::system_clock::now();
        newTree.InsertWordList(&newWordList);
        auto end = std::chrono::system_clock::now();
        sortTime = end - start;
        OutputResult();
        start = std::chrono::system_clock::now();
        newTree.Search(wordToLocate);
        end = std::chrono::system_clock::now();
        sortTime = end - start;
        EchoSortTime("RBT");
        newTree.PrintPathToRoot(wordToLocate);
    }
}

// Sorts all default files if allLists is set
void SortController::BenchmarkingAll(void)
{
    int fileCount = 10;
    std::string newFilename;
    for (int i = 1; i <= fileCount; i++)
    {
        lineCount = 0;
        newFilename = "test/PERM/perm";
        newFilename += std::to_string(15*i);
        newFilename += "K.txt";
        filename = newFilename;
        ReadWordFile();
        Benchmarking();
        originalWordList.clear();
    } 
}


// Main function for printing results
void SortController::OutputResult(void)
{
    switch(currentType)
    {
        case INSERTION:
            EchoSortTime("IS");
            WriteOutputToFile("IS");
            break;
        case MERGE:
            EchoSortTime("MS");
            WriteOutputToFile("MS");
            break;
        case HEAP:
            EchoSortTime("HS");
            WriteOutputToFile("HS");
            break;
        case BST:
            EchoSortTime("BST");
            WriteOutputToFile("BST");
            break;
        case RBT:
            EchoSortTime("RBT");
            WriteOutputToFile("RBT");
            break;
        default:
            break;
    }
}

// Prints sort time to the screen
void SortController::EchoSortTime(std::string outputFilename)
{
    std::string sortSizeString = std::to_string(lineCount / 1000);
    std::cout << outputFilename << sortSizeString;
    std::cout << " took " << sortTime.count() << " s" << std::endl;
    return;
}

// Prints result of sort operation to file
void SortController::WriteOutputToFile(std::string outputFilename)
{
    std::string sortSizeString = std::to_string(lineCount / 1000);
    std::string outputPath = "test/OUTPUT/";
    outputPath += outputFilename + sortSizeString + "K.txt";
    std::ofstream file(outputPath);
    if (!file.is_open())
    {
        std::cout << "Failed opening file\n";
        exit(1);
    }
    for (unsigned int i = 0; i < lineCount; i++)
        file << newWordList[i] << '\n';
    file.close();
}
