/*=========================================================================================

    File Name: line.js

    Description: D3 simple line chart

    ----------------------------------------------------------------------------------------

    Item Name: Modern Admin - Clean Bootstrap 4 Dashboard HTML Template

    Author: PIXINVENT

    Author URL: http://www.themeforest.net/user/pixinvent

==========================================================================================*/



// Line chart

// ------------------------------

$(window).on("load", function(){



    // Revealing module pattern to store some global data that will be shared between different functions.

    var d3CalendarGlobals = function() {

        var ele = d3.select("#pie-calendar"),

            // calendarWidth = ele.node().getBoundingClientRect().width,

            calendarWidth = 1380,

            calendarHeight = 820,

            gridXTranslation = 10,

            gridYTranslation = 40,

            cellColorForCurrentMonth = '#EAEAEA',

            cellColorForPreviousMonth = '#FFFFFF',

            counter = 0, // Counter is used to keep track of the number of "back" and "forward" button presses and to calculate the month to display.

            currentMonth = new Date().getMonth(),

            monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],

            datesGroup;



        function publicCalendarWidth() {

            return calendarWidth;

        }



        function publicCalendarHeight() {

            return calendarHeight;

        }



        function publicGridXTranslation() {

            return gridXTranslation;

        }



        function publicGridYTranslation() {

            return gridYTranslation;

        }



        function publicGridWidth() {

            return calendarWidth - 10;

        }



        function publicGridHeight() {

            return calendarHeight - 40;

        }



        function publicCellWidth() {

            return publicGridWidth() / 7;

        }



        function publicCellHeight() {

            return publicGridHeight() / 5;

        }



        function publicGetDatesGroup() {

            return datesGroup;

        }



        function publicSetDatesGroup(value) {

            datesGroup = value;

        }



        function publicIncrementCounter() {

            counter = counter + 1;

        }



        function publicDecrementCounter() {

            counter = counter - 1;

        }



        function publicMonthToDisplay() {

            var dateToDisplay = new Date();

            // We use the counter that keep tracks of "back" and "forward" presses to get the month to display.

            dateToDisplay.setMonth(currentMonth + counter);

            return dateToDisplay.getMonth();

        }



        function publicMonthToDisplayAsText() {

            return monthNames[publicMonthToDisplay()];

        }



        function publicYearToDisplay() {

            var dateToDisplay = new Date();

            // We use the counter that keep tracks of "back" and "forward" presses to get the year to display.

            dateToDisplay.setMonth(currentMonth + counter);

            return dateToDisplay.getFullYear();

        }



        function publicGridCellPositions() {



            // We store the top left positions of a 7 by 5 grid. These positions will be our reference points for drawing

            // various objects such as the rectangular grids, the text indicating the date etc.

            var cellPositions = [];

            for (y = 0; y < 5; y++) {

                for (x = 0; x < 7; x++) {

                    cellPositions.push([x * publicCellWidth(), y * publicCellHeight()]);

                }

            }



            return cellPositions;

        }



        // This function generates all the days of the month. But since we have a 7 by 5 grid, we also need to get some of

        // the days from the previous month and the next month. This way our grid will have all its cells filled. The days

        // from the previous or the next month will have a different color though. 

        function publicDaysInMonth() {

            var daysArray = [];



            var firstDayOfTheWeek = new Date(publicYearToDisplay(), publicMonthToDisplay(), 1).getDay();

            var daysInPreviousMonth = new Date(publicYearToDisplay(), publicMonthToDisplay(), 0).getDate();



            // Lets say the first week of the current month is a Wednesday. Then we need to get 3 days from 

            // the end of the previous month. But we can't naively go from 29 - 31. We have to do it properly

            // depending on whether the last month was one that had 31 days, 30 days or 28.

            for (i = 1; i <= firstDayOfTheWeek; i++) {

                daysArray.push([daysInPreviousMonth - firstDayOfTheWeek + i, cellColorForCurrentMonth]);

            }



            // These are all the days in the current month.

            var daysInMonth = new Date(publicYearToDisplay(), publicMonthToDisplay() + 1, 0).getDate();

            for (i = 1; i <= daysInMonth; i++) {

                daysArray.push([i, cellColorForPreviousMonth]);

            }



            // Depending on how many days we have so far (from previous month and current), we will need

            // to get some days from next month. We can do this naively though, since all months start on

            // the 1st.

            var daysRequiredFromNextMonth = 35 - daysArray.length;



            for (i = 1; i <= daysRequiredFromNextMonth; i++) {

                daysArray.push([i, cellColorForCurrentMonth]);

            }



            return daysArray.slice(0, 35);

        }



        return {

            calendarWidth: publicCalendarWidth(),

            calendarHeight: publicCalendarHeight(),

            gridXTranslation: publicGridXTranslation(),

            gridYTranslation: publicGridYTranslation(),

            gridWidth: publicGridWidth(),

            gridHeight: publicGridHeight(),

            cellWidth: publicCellWidth(),

            cellHeight: publicCellHeight(),

            getDatesGroup: publicGetDatesGroup,

            setDatesGroup: publicSetDatesGroup,

            incrementCounter: publicIncrementCounter,

            decrementCounter: publicDecrementCounter,

            monthToDisplay: publicMonthToDisplay(),

            monthToDisplayAsText: publicMonthToDisplayAsText,

            yearToDisplay: publicYearToDisplay,

            gridCellPositions: publicGridCellPositions(),

            daysInMonth: publicDaysInMonth

        }

    }();



    $(document).ready(function() {

        renderCalendarGrid();

        renderDaysOfMonth();

        $('#back').on('click',displayPreviousMonth);

        $('#forward').on('click',displayNextMonth);

    });



    function displayPreviousMonth() {

        // We keep track of user's "back" and "forward" presses in this counter

        d3CalendarGlobals.decrementCounter();

        renderDaysOfMonth();

    }



    function displayNextMonth() {

        // We keep track of user's "back" and "forward" presses in this counter

        d3CalendarGlobals.incrementCounter();

        renderDaysOfMonth();

    }



    // This function is responsible for rendering the days of the month in the grid.

    function renderDaysOfMonth(month, year) {

        $('#currentMonth').text(d3CalendarGlobals.monthToDisplayAsText() + ' ' + d3CalendarGlobals.yearToDisplay());

        // We get the days for the month we need to display based on the number of times the user has pressed

        // the forward or backward button.

        var daysInMonthToDisplay = d3CalendarGlobals.daysInMonth();

        var cellPositions = d3CalendarGlobals.gridCellPositions;



        // All text elements representing the dates in the month are grouped together in the "datesGroup" element by the initalizing

        // function below. The initializing function is also responsible for drawing the rectangles that make up the grid.

        d3CalendarGlobals.datesGroup

            .selectAll("text")

            .data(daysInMonthToDisplay)

            .attr("x", function(d, i) {

                return cellPositions[i][0];

            })

            .attr("y", function(d, i) {

                return cellPositions[i][1];

            })

            .attr("dx", 20) // right padding

            .attr("dy", 20) // vertical alignment : middle

            .attr("transform", "translate(" + d3CalendarGlobals.gridXTranslation + "," + d3CalendarGlobals.gridYTranslation + ")")

            .text(function(d) {

                return d[0];

            }); // Render text for the day of the week



        d3CalendarGlobals.calendar

            .selectAll("rect")

            .data(daysInMonthToDisplay)

            // Here we change the color depending on whether the day is in the current month, the previous month or the next month.

            // The function that generates the dates for any given month will also specify the colors for days that are not part of the

            // current month. We just have to use it to fill the rectangle

            .style("fill", function(d) {

                return d[1];

            });



        drawGraphsForMonthlyData();



    }



    function drawGraphsForMonthlyData() {

        // Get some random data

        var data = getDataForMonth();

        // Set up variables required to draw a pie chart

        var outerRadius = d3CalendarGlobals.cellWidth / 3;

        var innerRadius = 0;

        var pie = d3.layout.pie();

        // var color = d3.scale.category10();

        var color = d3.scale.ordinal()

        .range(["#99B898", "#FECEA8", "#FF847C", "#E84A5F", "#F8B195", "#F67280", "#C06C84"]);

        var arc = d3.svg.arc().innerRadius(innerRadius).outerRadius(outerRadius);



        // We need to index and group the pie charts and slices generated so that they can be rendered in

        // the appropriate cells. To do that, we call D3's 'pie' function of each of the data elements.

        var indexedPieData = [];

        for (i = 0; i < data.length; i++) {

            var pieSlices = pie(data[i]);

            // This loop is to store an index (j) for each of the slices of a given pie chart. Two different charts

            // on two different days will have the the same set of numbers for slices (eg: 0,1,2). This will help us

            // pick the same colors for the slices for two independent charts. Otherwise, the colors of the slices

            // will be different each day.

            for (j = 0; j < pieSlices.length; j++) {

                indexedPieData.push([pieSlices[j], i, j]);

            }

        }



        var cellPositions = d3CalendarGlobals.gridCellPositions;



        d3CalendarGlobals.chartsGroup

            .selectAll("g.arc")

            .remove();



        var arcs = d3CalendarGlobals.chartsGroup.selectAll("g.arc")

            // use the indexed data so that each pie chart can be draw in a different cell and therefore for a different day

            .data(indexedPieData)

            .enter()

            .append("g")

            .attr("class", "arc")

            .attr("transform", function(d) {

                // This is where we use the index here to translate the pie chart and rendere it in the appropriate cell. 

                // Normally, the chart would be squashed up against the top left of the cell, obscuring the text that shows the day of the month.

                // We use the gridXTranslation and gridYTranslation and multiply it by a factor to move it to the center of the cell. There is probably

                // a better way of doing this though.

                var currentDataIndex = d[1];

                return "translate(" + (outerRadius + d3CalendarGlobals.gridXTranslation * 5 + cellPositions[currentDataIndex][0]) + ", " + (outerRadius + d3CalendarGlobals.gridYTranslation * 1.25 + cellPositions[currentDataIndex][1]) + ")";

            });



        arcs.append("path")

            .attr("fill", function(d, i) {

                // The color is generated using the second index. Each slice of the pie is given a fixed number. This applies to all charts (see the indexing loop above).

                // This way, by using the index we can generate teh same colors for each of the slices for different charts on different days.

                return color(d[2]);

            })

            .attr("d", function(d, i) {

                // Standard functions for drawing a pie charts in D3.

                return arc(d[0]);

            });



        arcs.append("text")

            .attr("transform", function(d, i) {

                // Standard functions for drawing a pie charts in D3.

                return "translate(" + arc.centroid(d[0]) + ")";

            })

            .attr("text-anchor", "middle")

            .text(function(d, i) {

                return d[0].value;

            })

            .style("fill", "#FFF");



    }



    // Generates some random data that can be used to draw pie charts.

    function getDataForMonth() {

        var randomData = [];

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

            randomData.push([Math.floor(Math.random() * 100), Math.floor(Math.random() * 100), Math.floor(Math.random() * 100)]);

        }

        return randomData;

    }



    // This is the initializing function. It adds an svg element, draws a set of rectangles to form the calendar grid,

    // puts text in each cell representing the date and does the initial rendering of the pie charts.

    function renderCalendarGrid(month, year) {



        // Add the svg element.

        d3CalendarGlobals.calendar = d3.select("#pie-calendar")

            .append("svg")

            .attr("class", "calendar")

            .attr("width", d3CalendarGlobals.calendarWidth)

            .attr("height", d3CalendarGlobals.calendarHeight)

            .append("g");



        // Cell positions are generated and stored globally because they are used by other functions as a reference to render different things.

        var cellPositions = d3CalendarGlobals.gridCellPositions;



        // Draw rectangles at the appropriate postions, starting from the top left corner. Since we want to leave some room for the heading and buttons,

        // use the gridXTranslation and gridYTranslation variables.

        d3CalendarGlobals.calendar.selectAll("rect")

            .data(cellPositions)

            .enter()

            .append("rect")

            .attr("x", function(d) {

                return d[0];

            })

            .attr("y", function(d) {

                return d[1];

            })

            .attr("width", d3CalendarGlobals.cellWidth)

            .attr("height", d3CalendarGlobals.cellHeight)

            .style("stroke", "#555")

            .style("fill", "white")

            .attr("transform", "translate(" + d3CalendarGlobals.gridXTranslation + "," + d3CalendarGlobals.gridYTranslation + ")");



        var daysOfTheWeek = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];

        // This adds the day of the week headings on top of the grid

        d3CalendarGlobals.calendar.selectAll("headers")

            .data([0, 1, 2, 3, 4, 5, 6])

            .enter().append("text")

            .attr("x", function(d) {

                return cellPositions[d][0];

            })

            .attr("y", function(d) {

                return cellPositions[d][1];

            })

            .attr("dx", d3CalendarGlobals.gridXTranslation + 5) // right padding

            .attr("dy", 30) // vertical alignment : middle

            .text(function(d) {

                return daysOfTheWeek[d]

            });



        // The intial rendering of the dates for the current mont inside each of the cells in the grid. We create a named group ("datesGroup"),

        // and add our dates to this group. This group is also stored globally. Later on, when the the user presses the back and forward buttons

        // to navigate between the months, we clear and re add the new text elements to this group

        d3CalendarGlobals.datesGroup = d3CalendarGlobals.calendar.append("svg:g");

        var daysInMonthToDisplay = d3CalendarGlobals.daysInMonth();

        d3CalendarGlobals.datesGroup

            .selectAll("daysText")

            .data(daysInMonthToDisplay)

            .enter()

            .append("text")

            .attr("x", function(d, i) {

                return cellPositions[i][0];

            })

            .attr("y", function(d, i) {

                return cellPositions[i][1];

            })

            .attr("dx", 20) // right padding

            .attr("dy", 20) // vertical alignment : middle

            .attr("transform", "translate(" + d3CalendarGlobals.gridXTranslation + "," + d3CalendarGlobals.gridYTranslation + ")")

            .text(function(d) {

                return d[0];

            });



        // Create a new svg group to store the chart elements and store it globally. Again, as the user navigates through the months by pressing 

        // the "back" and "forward" buttons on the page, we clear the chart elements from this group and re add them again.

        d3CalendarGlobals.chartsGroup = d3CalendarGlobals.calendar.append("svg:g");

        // Call the function to draw the charts in the cells. This will be called again each time the user presses the forward or backward buttons.

        drawGraphsForMonthlyData();

    }



    // Resize chart

    // ------------------------------



    // Call function on window resize

    $(window).on('resize', resize);



    // Call function on sidebar width change

    $('.menu-toggle').on('click', resize);



    // Resize function

    // ------------------------------

    function resize() {



        width = ele.node().getBoundingClientRect().width - margin.left - margin.right;



        // Main svg width

        container.attr("width", width + margin.left + margin.right);



        // Width of appended group

        svg.attr("width", width + margin.left + margin.right);





        // Axis

        // -------------------------

        x.range([0, width]);

        svg.selectAll('.d3-xaxis').call(xAxis);





        svg.selectAll('.d3-line').attr("d", line);

    }

});