# -*- coding: UTF-8 -*-

"""
    This file is Free Software under the GNU GPL, Version >=2;
    and comes with NO WARRANTY!

    Version 1.3

    usage example:

    {{{
    #!burndown
    points:180
    start:2015-03-01
    end:2015-03-07
    2015-03-01:180
    2015-03-02:100
    2015-03-04:0
    }}}

    explanation:
    'points' represents the size of the value of the y-Axis
    'start' represents the start-point of a project.
    'end' represents the end point.
    the other values representing actual measured dates. The format is:
    <year>-<date>-<month>:<remaining points>

    the 'ideal line' will be plotted from the startpoint to the endpoint
    the 'actual line' will be plotted over all remaining points

    Initial Version 2015-03-05
    @copyright: 2015 by Intevation GmbH Osnabrueck
    @author: Sean Engelhardt <sean.engelhardt@intevation.de>
    @license: GNU GPLv>=2.
"""

import math

# a class named parser is needed my the moinmo.in plugin interface
class Parser:
    def __init__(self, raw, request, **kw):
        self.pagename = request.page.page_name
        self.raw = raw
        self.request = request
        self.formatter = request.formatter
        self.kw = kw
        self.d3js_source = self.request.cfg.url_prefix_static + "/common/js/d3.v3.min.js"
        self.chart = self.html_code()


    #get month-numbers between 1-12 innstead 0-11
    def normalize_month_number(self, month):
        return str(int(month) - 1)


    #cut of "start" or end, normalize the month, number and return as well-formed date array for d3.js
    def refract_parameters(self, parameters):
        #cut of the "start"
        date_array = parameters.split(':')

        #save the date as array
        date_array = date_array[1].split('-')

        date_array[1] = self.normalize_month_number(date_array[1])
        return date_array


    def is_valid_number(self, entry):
        if math.isnan(int(entry)):
            # raise ValueError("The entry: '" + entry + "' does not seem to be a proper number!")
            raise ValueError(entry)


    def validate_date_numbers(self, entry):
        if math.isnan(int(entry[0])) or math.isnan(int(entry[1])) or math.isnan(int(entry[2])):
            # raise ValueError('The entry: "' + entry + '" does not seem to be a proper date!')
            raise ValueError(entry)


    # the format methode is used by the plugin interface automaticly.
    # format is also called for each !# command.
    # the formatter (object) is not documented well. See moinmoin/formatter/base.py
    # half of the methods raise a 'not implemented' error.
    # however, the formater object is mostly used to print out lines on the webpage
    # e.g. formatter.text(plain text) / formatter.rawHTML(htmlcode) / formatter.paragraph(int)
    def format(self, formatter):

        try:

            parameters = self.raw.split()

            ideal = []
            actual = []
            points = ""

            for line in parameters:

                if line.startswith("points"):
                    points = line.split(":")[1]
                    self.is_valid_number(points)

                elif line.startswith("start"):

                    splitted_line = self.refract_parameters(line)
                    self.validate_date_numbers(splitted_line)

                    ideal.append("{date : new Date(%s), points: %s}" % (", ".join(splitted_line), points))

                elif line.startswith("end"):
                    splitted_line = self.refract_parameters(line)
                    self.validate_date_numbers(splitted_line)

                    ideal.append("{date : new Date(%s), points: 0}," % (", ".join(splitted_line)))

                elif line.startswith("20"):

                    # get the points
                    points_of_date = line.split(':')[1]
                    self.is_valid_number(points_of_date)

                    # Cut the points of the line
                    splitted_line = line.split(':')[0]

                    # get the date as array
                    splitted_line = splitted_line.split('-')
                    self.validate_date_numbers(splitted_line)

                    splitted_line[1] = self.normalize_month_number(splitted_line[1])

                    actual.append("{date : new Date(%s), points : %s}" % (", ".join(splitted_line), points_of_date))

            self.chart = (self.chart.replace("var ideal=[];", "var ideal=[%s];" % (", ".join(ideal),))
                                    .replace("var actual=[];", "var actual=[%s];" % (", ".join(actual))))

            # print("REQUESR\n-----\n" + self.request)

            self.request.write(formatter.rawHTML(self.chart))

        except ValueError as err:
            error_output = """
                <div style="border:thin solid red;">
                    An Error Occured! Did you used the burn down chart parser in the wrong way? <br />
                    {0}
                </div>
                """.format(err)
            self.request.write(formatter.rawHTML(error_output))

    #this is not the compressed version!
    #this will look inside your moinmoin dir for the CSS and JS sources.
    #makes development easier
    def html_code(self):

        return """
            <!DOCTYPE html><html><head>
            <link rel="stylesheet" type="text/css" href="{css}"></head>
            <body>
            <script type="text/javascript" src="{d3js}"></script>
            <script type="text/javascript">
                var ideal=[]; var actual=[];
            </script>
            <script type="text/javascript" src="{js}"></script>
            </body></html>
        """.format(css = self.request.cfg.url_prefix_static + "/common/js/style.css",
                    d3js = self.d3js_source,
                    js = self.request.cfg.url_prefix_static + "/common/js/make_chart.js" )
