/*global $ require */

$("[data-toggle=popover]").popover({
    container: "body"
});

//Set defaults
var data,
    dataType = "Small Integers",
    dataSize = "Small (1K)",
    dataCondition = "Random";

var quickSortIterations = 0,
    mergeSortIterations = 0,
    radixSortIterations = 0;

var dataSetSizes = {
        "Small (1K)": 1000,
        "Less Small (10K)": 10000,
        "Least Small (100K)": 100000
    }

//jQuery event handling:
$("#dataTypeDropdown li a").on("click", function(){
    $("#boldElement").removeAttr("id", "boldElement");
    $(this).attr("id", "boldElement");
    dataType = $(this).text();
    $("#dataTypeDropdownButton").text($("#dataTypeDropdownButton").text().slice(0,12));
    $("#dataTypeDropdownButton").text($("#dataTypeDropdownButton").text() + dataType);
});

$("#dataSizeDropdown li a").on("click", function(){
    $("#boldElement").removeAttr("id", "boldElement");
    $(this).attr("id", "boldElement");
    dataSize = $(this).text();
    $("#dataSizeDropdownButton").text($("#dataSizeDropdownButton").text().slice(0, 12));
    $("#dataSizeDropdownButton").text($("#dataSizeDropdownButton").text() + dataSize);
});

$("#dataConditionDropdown li a").on("click", function(){
    $("#boldElement").removeAttr("id", "boldElement");
    $(this).attr("id", "boldElement");
    dataCondition = $(this).text();
    $("#dataConditionDropdownButton").text($("#dataConditionDropdownButton").text().slice(0, 16));
    $("#dataConditionDropdownButton").text($("#dataConditionDropdownButton").text() + dataCondition);
});

$(".generateSubmitButton").on("click", function(){
    data = generateDataSet(dataType, dataSize, dataCondition)
    $("#generatedDataText").text(data.join(" "));
    $("#generatedDataText").removeClass("green");
    $("#generatedDataText").addClass("blue");
    $(".testResult").removeClass("green");
    $(".testResult").addClass("gray");
    $("#sortSubmitButton").removeAttr("disabled");
    $("#regenerateDataButton").removeAttr("disabled");
    $(".timeResult").text("-");
    $(".iterationResult").text("-");
    $(".timeResult").removeClass("green");
    $(".iterationResult").removeClass("green");
})

$("#sortSubmitButton").on("click", function(){
    var mergeSortCopy = data.slice();
        radixSortCopy = data.slice();
        
    if($("#quickSortCheckbox").is(":checked")){
        quickSortIterations = 0;
        var quickSortStartTime = performance.now();
        var randomizePivot = $("#randomizePivot").prop("checked");
        var quickSortTermination = $("#quickSortTermination").prop("checked");
        var quickSortResult = quickSort(data, 0, data.length - 1, randomizePivot, quickSortTermination) - quickSortStartTime;
        $("#quickSortTime").text(Math.floor(quickSortResult) + " ms");
        $("#quickSortTime").addClass("green");
        $("#quickSortIterations").text(quickSortIterations.toLocaleString());
        $("#quickSortIterations").addClass("green");
        mergeSort(data);
        $("#generatedDataText").text(data.join(" "));
        $("#generatedDataText").addClass("green");
    }   
    if ($("#radixSortCheckbox").is(":checked")){
        radixSortIterations = 0;
        var radixSortStartTime = performance.now();
        if ($("#radixSortBaseN").prop("checked")){
            var radixSortResult = radixSort(radixSortCopy, 1, true);
        }

        else {
            var radixSortResult = radixSort(radixSortCopy, 1, false);
        }
        window.setTimeout(function(){
            $("#radixSortTime").text(Math.floor(radixSortResult["completionTime"] - radixSortStartTime) + " ms");
            $("#radixSortTime").addClass("green");
            $("#radixSortIterations").text(radixSortIterations.toLocaleString());
            $("#radixSortIterations").addClass("green");
            if (!$("#quickSortCheckbox").is(":checked")){
                $("#generatedDataText").text(radixSortResult["result"].join(" "));
                $("#generatedDataText").addClass("green");
            }
        }, 1);
    }
    if ($("#mergeSortCheckbox").is(":checked")){
        mergeSortIterations = 0;
        var mergeSortStartTime = performance.now();
        var mergeSortTermination = $("#mergeSortTermination").prop("checked");
        mergeSortResult = mergeSort(mergeSortCopy, 1, mergeSortTermination);
        window.setTimeout(function(){
            $("#mergeSortTime").text(Math.floor(mergeSortResult["completionTime"] - mergeSortStartTime) + " ms");
            $("#mergeSortTime").addClass("green");
            $("#mergeSortIterations").text(mergeSortIterations.toLocaleString());
            $("#mergeSortIterations").addClass("green");
            if (!$("#quickSortCheckbox").is(":checked") && !$("#radixSortCheckbox").is(":checked")){
                $("#generatedDataText").text(mergeSortResult["result"].join(" "));
                $("#generatedDataText").addClass("green");
            }
        }, 1);
    }
    $(this).attr("disabled", "disabled");
})

$("#quickSortCheckbox").on("click", function(){
    $("#quickSortResultActive").toggleClass("orange");
    if ($(this).is(":checked")){
        $("#randomizePivot").removeAttr("disabled");
        $("#quickSortTermination").removeAttr("disabled");
    } else {
        $("#randomizePivot").attr("disabled", "disabled");
        $("#quickSortTermination").attr("disabled", "disabled");
    }
    $("#randomizePivotText").toggleClass("selectedAlgorithmOption");
    $("#quickSortTerminationText").toggleClass("selectedAlgorithmOption");
});

$("#mergeSortCheckbox").on("click", function(){
   $("#mergeSortResultActive").toggleClass("orange");
   if ($(this).is(":checked")){
        $("#mergeSortTermination").removeAttr("disabled");
    } else {
        $("#mergeSortTermination").attr("disabled", "disabled");
    }
    $("#mergeSortTerminationText").toggleClass("selectedAlgorithmOption");
});

$("#radixSortCheckbox").on("click", function(){
   $("#radixSortResultActive").toggleClass("orange");
   if ($(this).is(":checked")){
        $("#radixSortBaseN").removeAttr("disabled");
    } else {
        $("#radixSortBaseN").attr("disabled", "disabled");
    }
    $("#radixSortBaseNText").toggleClass("selectedAlgorithmOption");
});

$("#lotsOfRepeatsOption").on("click", function(){
    if (dataSize == "Least Small (100K)"){
        $("#randomizePivot").removeAttr("disabled");
        $("#randomizePivot").prop("checked", "checked");
        $("#randomizePivot").attr("disabled", "disabled");
    }
})

$(".sizeOption").on("click", function(){
    $("#randomizePivot").prop("checked", false);
    if ($("#quickSortCheckbox").prop("checked")){
        $("#randomizePivot").removeAttr("disabled");
    }
})

$("#leastSmallOption").on("click", function(){
    if (dataCondition == "Repetitive"){
        $("#randomizePivot").removeAttr("disabled");
        $("#randomizePivot").prop("checked", "checked");
        $("#randomizePivot").attr("disabled", "disabled");
    }
})

$(".conditionOption").on("click", function(){
    $("#randomizePivot").prop("checked", false);
    if ($("#quickSortCheckbox").prop("checked")){
        $("#randomizePivot").removeAttr("disabled");
    }
})

function radixSort(data, byDigit, base2){
    if (base2 && byDigit == 1){
        for (var element = 0; element < data.length; element++){
            data[element] = data[element].toString(2);
            radixSortIterations++;
        }
    }

    var maxDigits = 0;
    for (var element = 0; element < 100; element++){
        if (String(data[element]).length > maxDigits){
            maxDigits = String(data[element]).length;
        }
    }

    var nextIteration = [];

    if (base2) {
        for (var placeValue = 0; placeValue < 2; placeValue++){
            for (var elementIndex = 0; elementIndex < data.length; elementIndex++){
                if (data[elementIndex].length < byDigit && placeValue == 0){
                    nextIteration.push(data[elementIndex]);
                }
                else if (data[elementIndex][data[elementIndex].length - byDigit] == placeValue.toString(2)){
                    nextIteration.push(data[elementIndex]);
                }
                radixSortIterations++;
            }
        }
    } 
    else {
        for (var placeValue = 0; placeValue < 10; placeValue++){
            for (var elementIndex = 0; elementIndex < data.length; elementIndex++){
                if ((Math.floor(data[elementIndex] / Math.pow(10, byDigit - 1))) % 10 == placeValue){
                    nextIteration.push(data[elementIndex]);
                }
                radixSortIterations++;
            }
        }
    }

    if (byDigit == maxDigits){
        if (base2){
            for (var elementIndex = 0; elementIndex < data.length; elementIndex++){
                nextIteration[elementIndex] = parseInt(nextIteration[elementIndex], 2);
                radixSortIterations++;
            }
            radixSortIterations++;
        }
        return {
            "completionTime": performance.now(),
            "result": nextIteration.slice()
        }
    } 
    else {
        nextIteration = radixSort(nextIteration, byDigit + 1, base2)["result"];
        return {
            "completionTime": performance.now(),
            "result": nextIteration.slice()
        }
    }
}

function mergeSort(data, direction, terminationSubroutine){
    if (data.length == 1){
        mergeSortIterations++;
        return {
            "completionTime": performance.now(),
            "result": data
        }
    } 
    if (data.length <= 4 && terminationSubroutine){
        insertionSort(data, 0, data.length - 1);
        return {
            "completionTime": performance.now(),
            "result": data
        }
    }
    var firstHalf = data.slice(0, Math.floor(data.length/2)),
        secondHalf  = data.slice(Math.floor(data.length/2), data.length);
    firstHalf = mergeSort(firstHalf, direction, terminationSubroutine)["result"];
    secondHalf = mergeSort(secondHalf, direction, terminationSubroutine)["result"];
    var result = merge(firstHalf, secondHalf, direction);
    return {
        "completionTime": performance.now(),
        "result": result
    }
}

function merge(set1, set2, direction){
    var result = [], 
        index1 = 0,
        index2 = 0;
    while (index1 < set1.length && index2 < set2.length){
        if (compare(set1[index1], set2[index2], direction)) {
            result.push(set1[index1]);
            index1++;
        } else {
            result.push(set2[index2]);
            index2++;
        }
        mergeSortIterations++;
    }
    if (index1 >= set1.length - 1){
        while (index2 < set2.length){
            result.push(set2[index2]);
            index2++;
            mergeSortIterations++;
        }
    } else {
        while (index1 < set1.length){
            result.push(set1[index1]);
            index1++;
            mergeSortIterations++;
        }
    }
    return result;
}

function quickSort(data, startIndex, endIndex, randomize, terminationSubroutine){
    
    if (endIndex == startIndex){
        return;
    }
    else if (endIndex - startIndex <= 4 && terminationSubroutine){
        insertionSort(data, startIndex, endIndex);
        return;
    }

    else {
        var frontIterator = startIndex,
            backIterator = endIndex,
            pivot = data[frontIterator],
            sortedBack = true, 
            sortedFront = true;

        if (randomize) {
            var lowerEnd = Math.min(data[startIndex], data[endIndex]),
                higherEnd = Math.max(data[endIndex], data[startIndex]);
            pivot = Math.floor(Math.random() * (higherEnd - lowerEnd)) + lowerEnd;
            if (data[backIterator] == lowerEnd && higherEnd > lowerEnd && pivot == data[backIterator]){
                pivot++;
            }
        }
            
        while (backIterator >= frontIterator + 2){
            while (data[backIterator] >= pivot && backIterator > frontIterator + 1){
                backIterator --;
                if (data[backIterator] > data[backIterator + 1]){
                    sortedBack = false;
                }
                quickSortIterations++;
            }
            while (data[frontIterator] < pivot && frontIterator < backIterator - 1){
                frontIterator ++;
                if (data[frontIterator] < data[frontIterator - 1]){
                    sortedFront = false;
                }
                quickSortIterations++;
            }
            if (data[frontIterator] >= pivot && data[backIterator] < pivot){
                swap(data, frontIterator, backIterator);
                if (data[backIterator] > data[backIterator + 1]){
                    sortedBack = false;
                }
                if (data[frontIterator] < data[frontIterator - 1]){
                    sortedFront = false;
                }
            }
        }
        if (backIterator == frontIterator + 1){
            while (data[backIterator] < pivot && data[frontIterator] < pivot){
                backIterator++;
                frontIterator++;
            }
        }

        if (!sortedFront && startIndex < frontIterator){
            quickSort(data, startIndex, frontIterator, randomize, terminationSubroutine);
        }
        if (!sortedBack && endIndex > backIterator){
            quickSort(data, backIterator, endIndex, randomize, terminationSubroutine);
        }
        
        return performance.now();
    }
}

function insertionSort(data, startIndex, endIndex){
    if (startIndex == endIndex){
        return;
    }
    for (var mainIterator = startIndex; mainIterator < endIndex; mainIterator++){
        var minIndex = mainIterator;
        for (var secondIterator = mainIterator + 1; secondIterator <= endIndex; secondIterator++){
            minIndex = data[secondIterator] < data[minIndex] ? secondIterator : minIndex;
            quickSortIterations++;
        }
        swap(data, mainIterator, minIndex);
    }
    return;
}

function generateDataSet(dataType, dataSize, dataCondition){
    var result = [];
    var maxSize = dataType == "Small Integers" ? 10000 : 100000000;

    if (dataCondition == "Random"){
        for (var i = 0; i < dataSetSizes[dataSize]; i++){
            result.push(Number(Math.floor(Math.random() * maxSize)));
        }
    }
    else if (dataCondition == "Semi-sorted"){
        result = generateDataSet(dataType, dataSize, "Random");
        quickSort(result, 0, result.length - 1, false, false);
        insertRandomElements(result, 20, dataType, dataSize);
    }
    else if (dataCondition == "Semi-reverse-sorted"){
        result = generateDataSet(dataType, dataSize, "Semi-sorted");
        for (var elementIndex = 0; elementIndex < Math.floor(result.length / 2); elementIndex++){
            swap(result, elementIndex, result.length - elementIndex - 1);
        }
    }
    // else if (dataCondition == "Repetitive"){
    //     result = generateDataSet(dataType, dataSize, "Random");
    //     var repeatedValue = Number(Math.floor(Math.random() * maxSize));
    //     for (var i = 0; i < (dataSetSizes[dataSize] / 2); i++){
    //         result[Math.floor(Math.random() * dataSetSizes[dataSize])] = repeatedValue;
    //     }
    // }
    return result;
}

function insertRandomElements(data, frequency, dataType, dataSize){
    var maxSize = dataType == "Small Integers" ? 10000 : 100000000;
    for (var i = 0; i < (dataSetSizes[dataSize] / frequency); i++){
        data[Math.floor(Math.random() * dataSetSizes[dataSize])] = Math.floor(Math.random() * maxSize);
    }
}

function compare(input1, input2, direction){
    if (direction == -1){
        return input1 >= input2;
    }
    return input1 <= input2
}

function swap(data, index1, index2){
    var tempVal = data[index1];
    data[index1] = data[index2];
    data[index2] = tempVal;
}
