var labelVar = 'year';

var parseDate = d3.time.format("%y");

var chartMargin = { top: 20, right: 30, bottom: 40, left: 80 },
    // width = (d3.select(resultsVizEl).node().getBoundingClientRect().width) - chartMargin.left - chartMargin.right,
    chartWidth = 620 - chartMargin.left - chartMargin.right,
    chartHeight = 250 - chartMargin.top - chartMargin.bottom;

var chartColours = d3.scale.ordinal()
    .range(["#999", "#ccc", "#CE2A23"]);

var x,
    y,
    xAxis,
    yAxis,
    stack,
    area,
    svg,
    tooltip,
    varNames,
    seriesArr = [],
    series = {},
    selection,
    selectionPaths,
    yMaxNumberPadding = 20000;


function d3ChartInit(vizElement) {

    // console.log("d3ChartInit ---> ");
    // console.log("vizElement: %o ", vizElement);

    x = d3.scale.ordinal()
        .rangeRoundBands([0, chartWidth], .2);

    y = d3.scale.linear()
        .rangeRound([chartHeight, 0]);

    xAxis = d3.svg.axis()
        .scale(x)
        .ticks(d3.time.years, 5)
        .orient("bottom");

    yAxis = d3.svg.axis()
        .scale(y)
        .orient("left")
        // .tickFormat("")
        .tickSize(-chartWidth, 0, 0);



    stack = d3.layout.stack()
        .offset("zero")
        .values(function(d) { return d.values; })
        .x(function(d) { return x(d.label) + x.rangeBand() / 2; })
        .y(function(d) { return d.value; });

    area = d3.svg.area()
        // .interpolate("step-before")
        .x(function(d) { return x(d.label) + x.rangeBand() / 2; })
        .y0(function(d) { return y(d.y0); })
        .y1(function(d) {
          // console.log("d %o", d);
          return y(d.y0 + d.y); });

    svg = d3.select(vizElement).append("svg")
              .attr('class', 'gi-viz')
              .attr("width", chartWidth + chartMargin.left + chartMargin.right)
              .attr("height", chartHeight + chartMargin.top + chartMargin.bottom)
              .append("g")
                .attr("class", "plot-area")
                .attr("transform", "translate(" + chartMargin.left + "," + chartMargin.top + ")");

    svg.append("g")
        .attr("class", "axis x-axis")
        .attr("transform", "translate(0," + chartHeight + ")")
        .call(xAxis);

    svg.append("g")
        .attr("class", "axis y-axis gridline")
        .call(yAxis)
        .append("g")
        .attr("class", "qualifier")
        .attr("x", 10)
        .attr("y", 6)
        .attr("dy", ".71em")
        .append("text")
          .style("text-anchor", "start")
          .text("Total value ($)");

}

function d3PrepareData(obj) {

    data = obj;

    data.forEach(function(d) {

        d.year = d.year;
        d.contributionLimitUnadjusted = +d.contributionLimitUnadjusted;
        d.contributionLimit = +d.contributionLimit;
        d.startingValueToDate = +d.startingValueToDate;
        d.thisYearInterest = +d.thisYearInterest;
        d.currentContributionAmount = +d.currentContributionAmount;

        d.principalToDate = +d.principalToDate;
        d.endValueToDate = +d.endValueToDate;

    });

    varNames = d3.keys(data[0])
        .filter(function(key) {

            if (key === 'currentContributionAmount' || key === 'thisYearInterest' || key === 'startingValueToDate') {
                return key !== labelVar;
            }
        });

    chartColours.domain(varNames);


    seriesArr = [];
    varNames.forEach(function(name) {
        series[name] = { name: name, values: [] };
        seriesArr.push(series[name]);
    });

    data.forEach(function(d) {
        varNames.map(function(name) {
            series[name].values.push({ name: name, label: d[labelVar], value: +d[name] });
        });
    });

    stack(seriesArr);

}

function d3UpdateAxis() {

    x.domain(data.map(function(d) { return d.year; }));

    y.domain([0, d3.max(seriesArr, function(c) {

        var yMax = d3.max(c.values, function(d) {
            return d.y0 + d.y;
        })

        return yMax + yMaxNumberPadding;

    })]);

    svg.select(".x-axis")
        .transition()
        .duration(500)
        .call(xAxis)
        .selectAll("text")
        .style("text-anchor", "end")
        .attr("dx", "-.8em")
        .attr("dy", ".15em")
        .attr("class", "axisLabel")
        .attr("transform", "rotate(-65)");

    svg.select(".y-axis")
        .transition()
        .duration(500)
        .call(yAxis);

}

function d3ChartRender(obj, updateState) {

    // console.log("d3ChartRender ---> ");
    // console.log("obj %o", obj);
    // console.log("updateState: " + updateState);

    d3PrepareData(obj);
    d3UpdateAxis();

    // console.log("seriesArr %o", seriesArr);
    // var thisStack = stack(seriesArr);
    // console.log("thisStack %o", thisStack);

    selection = svg.selectAll(".series")
                    .data(seriesArr)
                    .enter()
                    .append("g")
                    .attr("class", "series")
                    .attr("id", function(d) {
                      // console.log(d.name);
                      return "series_" + d.name; })
                    .append("path")
                    .attr("class", function(d) { return "seriesAreaPath"; })
                    .attr("d", function(d) {
                      // var areaValues = area(d.values); console.log("areaValues: %o", areaValues);
                      return area(d.values); })
                    .style("fill", function(d) { return chartColours(d.name); });

    selectionPaths = svg.selectAll(".seriesAreaPath")
            // .data(seriesArr)
            .transition()
            .duration(750)
            .attr("d", function(d) { return area(d.values); })
            .style("fill", function(d) { return chartColours(d.name); });


}

function d3ChartUpdate(obj, updateState) {

    // console.log("d3ChartUpdate ---> ");
    // console.log("obj %o", obj);
    // console.log("updateState: " + updateState);

    d3PrepareData(obj);
    d3UpdateAxis();

    // console.log("seriesArr %o", seriesArr);

    // var thisStack = stack(seriesArr);
    // console.log("thisStack %o", thisStack);

    selection = svg.selectAll(".series")
                    .data(seriesArr)
                    .enter()
                    .append("g")
                    .attr("class", "series")
                    .attr("id", function(d) {
                        return "series_" + d.name; })
                    .append("path")
                    .attr("class", function(d) {
                        return "seriesAreaPath"; })
                    .attr("d", function(d) {
                        return area(d.values); })
                    .style("fill", function(d) {
                        return chartColours(d.name); });

    selectionPaths = svg.selectAll(".seriesAreaPath")
        .data(seriesArr)
        .transition()
        .duration(1000)
        .attr("d", function(d) {
            return area(d.values); })
        .style("fill", function(d) {
            return chartColours(d.name); });

}




(function() {

    'use strict';

    /* No need for ractive in production */

    var calculatorEl = 'tfsa-calculator',
        calculatorContainer = document.getElementById(calculatorEl),

        resultsVizEl = 'tfsa-results-viz',
        resultsVizContainer = document.getElementById(resultsVizEl),

        resultsTableContainerEl = 'tfsa-results-table',
        resultsTableContainer = document.getElementById(resultsTableContainerEl),

        resultsTableEl = 'tableInvestmentGrowth',
        resultsTable = document.getElementById(resultsTableEl);

    var btnShowData = document.getElementById('btn_showData');
        btnShowData.addEventListener("click", toggleDataTable, false);

    var btnShowData2 = document.getElementById('btn_showDataTable');
        btnShowData2.addEventListener("click", toggleDataTable, false);


    var useIncreaseIntervalTime = false;
    var useRactive = false;
    var vizDrawn = false;

    var labelVar = 'year';

    var thisDate = new Date();
    var thisYearStarting = thisDate.getFullYear();
    var thisYear = thisYearStarting;

    var inputFields,
        formFields = {},
        calcVariables = {};

    var hiddenFields = [
        'fld-grp_contribution_annual',
        'fld-grp_contribution_annual_percentage_increase'
    ]

    var calculatedResults;
    var data = [];

    var tableHead = '' + '<thead>' + ' <tr>' + '   <th>Year</th>' + '   <th>Limit</th>' + '   <th>Start of year <br />value</th>' + '   <th>Contribution</th>' + '   <th>Investment return</th>' + '   <th>End of year <br />value</th>' + ' </tr>' + '</thead>';




    function init() {

        if (useRactive === false) {
            registerFieldEvents();
            checkDependancyToggle();
        }
        else {
            var ractive = new Ractive({
                el: calculatorEl,
                template: template,
                data: {
                    sections: fieldsConfig
                },
                onrender: function() {
                    registerFieldEvents();
                    checkDependancyToggle();
                }

            });

        }

        d3ChartInit(resultsVizContainer);

    }

    function registerFieldEvents() {

        inputFields = document.querySelectorAll("input");
        for (var i = 0; i < inputFields.length; i++) {
            live('#' + inputFields[i].id, 'change', checkUserInput);
        }

    }

    function checkUserInput() {

        checkDependancyToggle();

        var j = 0;

        /* Loop through the inputs, gather up the user values */
        for (var key in fieldsConfig) {

            if (fieldsConfig.hasOwnProperty(key)) {

                var sectionFieldsArray = fieldsConfig[key].fields;

                sectionFieldsArray.forEach(function(field) {

                    var fieldName = field.name,
                        fieldValue = '',
                        fieldEl = document.getElementById('fld_' + fieldName),
                        fieldType = field.type;

                    if (fieldType === 'radio' || fieldType === 'checkbox') {

                        fieldValue = fieldEl.value;

                    } else {

                        if (fieldEl.value !== '' || fieldEl.value !== undefined) {

                            var tmpValue = fieldEl.value;
                                tmpValue = tmpValue.replace('$','');
                                tmpValue = tmpValue.replace('%','');
                                tmpValue = tmpValue.replace(',','');

                            fieldValue = +tmpValue;

                        }

                    }

                    calcVariables[fieldName] = fieldValue;

                    j++;

                });

            }

        }

        // console.log("calcVariables: %o", calcVariables );

        calculateInvestmentGrowth(calcVariables);

        if(vizDrawn === false){
          d3ChartRender(calculatedResults, false);
          vizDrawn = true;
        }
        else{
          d3ChartUpdate(calculatedResults, true);
        }

        showInvestmentGrowthTable();

        if(calculatorContainer.hasClass('initial')){
          calculatorContainer.removeClass('initial')
          resultsVizContainer.removeClass('hidden');
        }

        window.location.hash = '#' + resultsVizEl;

    }

    function checkDependancyToggle() {

        var maximizeContributionEl = document.getElementById('fld_contribution_annual_max');

        hiddenFields.forEach(function(field) {

            var hiddenField = document.getElementById(field);

            if (maximizeContributionEl.checked) {
                hiddenField.addClass('inactive');
            } else {
                hiddenField.removeClass('inactive');
            }

        });

    }

    function calculateInvestmentGrowth(calcVariables) {

        if (calcVariables.contribution_years.value !== '') {

            var _contribution_annual_limit_initial = +calcVariables.contribution_annual_limit_initial;
            var _contribution_increase_interval_time = +calcVariables.contribution_increase_interval_time;
            var _contribution_increase_interval_inflationrate = +(calcVariables.contribution_increase_interval_inflationrate / 100);
            var _contribution_increase_amount = +calcVariables.contribution_increase_amount;

            var _contribution_existing = +calcVariables.contribution_existing;
            var _contribution_years = +calcVariables.contribution_years;
            var _contribution_expected_ror = (calcVariables.contribution_expected_ror / 100);

            var _contribution_annual = +calcVariables.contribution_annual;
            var _contribution_annual_percentage_increase = +calcVariables.contribution_annual_percentage_increase;
            var _contribution_maximize = calcVariables.contribution_annual_max;

            if (_contribution_maximize === 'on') {
                _contribution_maximize = true;
            } else {
                _contribution_maximize = false;
            }

            /* Set to initial limit */
            var currentContributionLimit = +_contribution_annual_limit_initial;

            var contributionLimitUnadjustedInitial = currentContributionLimit,
                contributionLimitUnadjusted = contributionLimitUnadjustedInitial;

            var principalToDate = _contribution_existing,
                thisYearInterest,
                currentContributionAmount,
                endValueToDate = principalToDate;

            var j = 1;
            calculatedResults = [];

            for (var i = 0; i < _contribution_years; i++) {

                /* What is the max contribution this year? */
                if (useIncreaseIntervalTime === true) {

                    if (j < _contribution_increase_interval_time) {
                        j++;
                    } else {
                        currentContributionLimit += _contribution_increase_amount;
                        j = 1;
                    }

                } else {

                    if (i !== 0) {
                        contributionLimitUnadjusted = contributionLimitUnadjusted * (1 + _contribution_increase_interval_inflationrate);
                        contributionLimitUnadjusted = contributionLimitUnadjusted.toFixed(0);
                    }

                    if (contributionLimitUnadjusted > currentContributionLimit + 250) {
                        currentContributionLimit += _contribution_increase_amount;
                        contributionLimitUnadjusted = currentContributionLimit;
                    }
                }

                if (_contribution_maximize !== true) {

                    if (i === 0) {
                        currentContributionAmount = _contribution_existing;
                    } else {
                        currentContributionAmount = currentContributionAmount * _contribution_annual_percentage_increase;
                    }

                } else {

                    currentContributionAmount = currentContributionLimit;
                    principalToDate = principalToDate + currentContributionAmount;

                }


                if (i === 0) {

                    thisYear = thisYearStarting;

                    if (_contribution_existing !== '') {
                        var startingValueToDate = _contribution_existing;
                    } else {
                        var startingValueToDate = 0;
                    }

                } else {

                    thisYear = thisYear + 1;
                    var lastYearKey = i - 1;
                    var startingValueToDate = calculatedResults[lastYearKey].endValueToDate
                }

                var currentPrincipal = (+startingValueToDate + +currentContributionAmount);

                thisYearInterest = currentPrincipal * _contribution_expected_ror;
                endValueToDate = +currentPrincipal + +thisYearInterest;

                thisYearInterest = thisYearInterest.toFixed(2);
                endValueToDate = endValueToDate.toFixed(2);

                calculatedResults[i] = {};
                calculatedResults[i].yearNumber = i;
                calculatedResults[i].year = thisYear;
                calculatedResults[i].contributionLimit = currentContributionLimit;
                calculatedResults[i].contributionLimitUnadjusted = contributionLimitUnadjusted;
                calculatedResults[i].startingValueToDate = startingValueToDate;
                calculatedResults[i].currentContributionAmount = currentContributionAmount;
                calculatedResults[i].principalToDate = principalToDate;
                calculatedResults[i].thisYearInterest = thisYearInterest;
                calculatedResults[i].endValueToDate = endValueToDate;

            }

        }

    }


    function showInvestmentGrowthTable() {

        var tableBody = '<tbody>';

        var investmentReturn = 0;

        for (var key in calculatedResults) {

            if (calculatedResults.hasOwnProperty(key)) {

                var thisYear = calculatedResults[key];

                var thisTableRow = '<tr>' + '<td>' + calculatedResults[key].year + '</td>' + '<td>' + numberWithCommas(calculatedResults[key].contributionLimit) + '</td>' + '<td>' + numberWithCommas(calculatedResults[key].startingValueToDate) + '</td>' + '<td>' + numberWithCommas(calculatedResults[key].currentContributionAmount) + '</td>' + '<td>' + numberWithCommas(calculatedResults[key].thisYearInterest) + '</td>' + '<td>' + numberWithCommas(calculatedResults[key].endValueToDate) + '</td>' + '</tr>';

                tableBody += thisTableRow;

                /* We just need the last value for these */
                var totalContributionTD = calculatedResults[key].principalToDate;
                var endValueTD = calculatedResults[key].endValueToDate;

                /* */
                investmentReturn = +investmentReturn + +calculatedResults[key].thisYearInterest;

            }



        }

        investmentReturn = investmentReturn.toFixed(2);

        var thisTableRowTotals = '<tr class="rowTotals">' + '<td>Total</td>' + '<td>&nbsp;</td>' + '<td>&nbsp;</td>' + '<td>$' + numberWithCommas(totalContributionTD) + ' </td>' + '<td>$' + numberWithCommas(investmentReturn)  + '</td>' + '<td>$' + numberWithCommas(endValueTD) + '</td>' + '</tr>';

        tableBody += thisTableRowTotals;

        tableBody += '</tbody>';

        var tableContent = tableHead + tableBody;

        resultsTable.innerHTML = tableContent;

        /* Show totals above viz */
        var detailTotalContribution     = document.getElementById('detail_total_contribution');
        var detailTotalInvestmentReturn = document.getElementById('detail_total_investment_return');
        var detailTotalValue            = document.getElementById('detail_total_value');

        detailTotalContribution.innerHTML     =  '$' + numberWithCommas(totalContributionTD);
        detailTotalInvestmentReturn.innerHTML = '$' + numberWithCommas(investmentReturn);
        detailTotalValue.innerHTML            = '$' + numberWithCommas(endValueTD);

    }



    function toggleDataTable(){

      if(resultsTableContainer.hasClass('hidden')){
        resultsTableContainer.removeClass('hidden');
        btnShowData.innerHTML = 'Hide data';
        window.location.hash = '#' + resultsTableEl;
      }
      else{
        resultsTableContainer.addClass('hidden');
        btnShowData.innerHTML = 'Show data';
        window.location.hash = '#' + resultsVizEl;
      }

    }

    /* Utilities */
    HTMLElement.prototype.removeClass = function(remove) {
        var newClassName = "";
        var i;
        var classes = this.className.split(" ");
        for (i = 0; i < classes.length; i++) {
            if (classes[i] !== remove) {
                newClassName += classes[i] + " ";
            }
        }
        this.className = newClassName;
    }

    // live binding helper using matchesSelector
    function live(selector, event, callback, context) {
        addEvent(context || document, event, function(e) {
            var found, el = e.target || e.srcElement;
            while (el && el.matches && el !== context && !(found = el.matches(selector))) el = el.parentElement;
            if (found) callback.call(el, e);
        });
    }

    // helper for enabling IE 8 event bindings
    function addEvent(el, type, handler) {
        if (el.attachEvent) el.attachEvent('on' + type, handler);
        else el.addEventListener(type, handler);
    }

    function numberWithCommas(n) {
        var parts = n.toString().split(".");
        return parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",") + (parts[1] ? "." + parts[1] : "");
    }

    Node.prototype.hasClass = function(className) {
        if (this.classList) {
            return this.classList.contains(className);
        } else {
            return (-1 < this.className.indexOf(className));
        }
    };

    Node.prototype.addClass = function(className) {
        if (this.classList) {
            this.classList.add(className);
        } else if (!this.hasClass(className)) {
            var classes = this.className.split(" ");
            classes.push(className);
            this.className = classes.join(" ");
        }
        return this;
    };

    Node.prototype.removeClass = function(className) {
        if (this.classList) {
            this.classList.remove(className);
        } else {
            var classes = this.className.split(" ");
            classes.splice(classes.indexOf(className), 1);
            this.className = classes.join(" ");
        }
        return this;
    };
    /* Utilities */

    init();

})();