#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import base64
import cgi
from datetime import datetime
import os

from .. import base

from collections import defaultdict

html = None
raw = None

base_path = os.path.split(__file__)[0]

def do_defered_imports():
    global html
    global raw

    from py.xml import html, raw


class HTMLFormatter(base.BaseFormatter):
    """Formatter that produces a simple HTML-formatted report."""
    def __init__(self):
        do_defered_imports()
        self.suite_name = None
        self.result_rows = []
        self.test_count = defaultdict(int)
        self.start_times = {}
        self.suite_times = {"start": None,
                            "end": None}
        self.head = None
        self.env = {}

    def suite_start(self, data):
        self.suite_times["start"] = data["time"]
        self.suite_name = data["source"]
        with open(os.path.join(base_path, "style.css")) as f:
            self.head = html.head(
                html.meta(charset="utf-8"),
                html.title(data["source"]),
                html.style(raw(f.read())))

        date_format = "%d %b %Y %H:%M:%S"
        version_info = data.get("version_info")
        if version_info:
            self.env["Device identifier"] = version_info.get("device_id")
            self.env["Device firmware (base)"] = version_info.get("device_firmware_version_base")
            self.env["Device firmware (date)"] = (
                datetime.utcfromtimestamp(int(version_info.get("device_firmware_date"))).strftime(date_format) if
                "device_firmware_date" in version_info else None)
            self.env["Device firmware (incremental)"] = version_info.get("device_firmware_version_incremental")
            self.env["Device firmware (release)"] = version_info.get("device_firmware_version_release")
            self.env["Gaia date"] = (
                datetime.utcfromtimestamp(int(version_info.get("gaia_date"))).strftime(date_format) if
                "gaia_date" in version_info else None)
            self.env["Gecko version"] = version_info.get("application_version")
            self.env["Gecko build"] = version_info.get("application_buildid")

            if version_info.get("application_changeset"):
                self.env["Gecko revision"] = version_info.get("application_changeset")
                if version_info.get("application_repository"):
                    self.env["Gecko revision"] = html.a(
                        version_info.get("application_changeset"),
                        href="/".join([version_info.get("application_repository"),
                                       version_info.get("application_changeset")]),
                        target="_blank")

            if version_info.get("gaia_changeset"):
                self.env["Gaia revision"] = html.a(
                    version_info.get("gaia_changeset")[:12],
                    href="https://github.com/mozilla-b2g/gaia/commit/%s" % version_info.get("gaia_changeset"),
                    target="_blank")

        device_info = data.get("device_info")
        if device_info:
            self.env["Device uptime"] = device_info.get("uptime")
            self.env["Device memory"] = device_info.get("memtotal")
            self.env["Device serial"] = device_info.get("id")

    def suite_end(self, data):
        self.suite_times["end"] = data["time"]
        return self.generate_html()

    def test_start(self, data):
        self.start_times[data["test"]] = data["time"]

    def test_end(self, data):
        self.make_result_html(data)

    def make_result_html(self, data):
        tc_time = (data["time"] - self.start_times.pop(data["test"])) / 1000.
        additional_html = []
        debug = data.get("extra", {})
        links_html = []

        status = status_name = data["status"]
        expected = data.get("expected", status)

        if status != expected:
            status_name = "UNEXPECTED_" + status
        elif status != "PASS":
            status_name = "EXPECTED_" + status

        self.test_count[status_name] += 1

        if status in ['SKIP', 'FAIL', 'ERROR']:
            if debug.get('screenshot'):
                screenshot = 'data:image/png;base64,%s' % debug['screenshot']
                additional_html.append(html.div(
                    html.a(html.img(src=screenshot), href="#"),
                    class_='screenshot'))
            for name, content in debug.items():
                if 'screenshot' in name:
                    href = '#'
                else:
                    # use base64 to avoid that some browser (such as Firefox, Opera)
                    # treats '#' as the start of another link if the data URL contains.
                    # use 'charset=utf-8' to show special characters like Chinese.
                    href = 'data:text/plain;charset=utf-8;base64,%s' % base64.b64encode(content.encode('utf-8'))
                links_html.append(html.a(
                    name.title(),
                    class_=name,
                    href=href,
                    target='_blank'))
                links_html.append(' ')

            log = html.div(class_='log')
            output = data.get('stack', '').splitlines()
            output.extend(data.get('message', '').splitlines())
            for line in output:
                separator = line.startswith(' ' * 10)
                if separator:
                    log.append(line[:80])
                else:
                    if line.lower().find("error") != -1 or line.lower().find("exception") != -1:
                        log.append(html.span(raw(cgi.escape(line)), class_='error'))
                    else:
                        log.append(raw(cgi.escape(line)))
                log.append(html.br())
            additional_html.append(log)

        self.result_rows.append(
            html.tr([html.td(status_name, class_='col-result'),
                     html.td(data['test'], class_='col-name'),
                     html.td('%.2f' % tc_time, class_='col-duration'),
                     html.td(links_html, class_='col-links'),
                     html.td(additional_html, class_='debug')],
                    class_=status_name.lower() + ' results-table-row'))

    def generate_html(self):
        generated = datetime.utcnow()
        with open(os.path.join(base_path, "main.js")) as main_f:
            doc = html.html(
                self.head,
                html.body(
                    html.script(raw(main_f.read())),
                    html.p('Report generated on %s at %s' % (
                        generated.strftime('%d-%b-%Y'),
                        generated.strftime('%H:%M:%S'))),
                    html.h2('Environment'),
                    html.table(
                        [html.tr(html.td(k), html.td(v)) for k, v in sorted(self.env.items()) if v],
                        id='environment'),

                    html.h2('Summary'),
                    html.p('%i tests ran in %.1f seconds.' % (sum(self.test_count.itervalues()),
                                                              (self.suite_times["end"] -
                                                               self.suite_times["start"]) / 1000.),
                           html.br(),
                           html.span('%i passed' % self.test_count["PASS"], class_='pass'), ', ',
                           html.span('%i skipped' % self.test_count["SKIP"], class_='skip'), ', ',
                           html.span('%i failed' % self.test_count["UNEXPECTED_FAIL"], class_='fail'), ', ',
                           html.span('%i errors' % self.test_count["UNEXPECTED_ERROR"], class_='error'), '.',
                           html.br(),
                           html.span('%i expected failures' % self.test_count["EXPECTED_FAIL"],
                                     class_='expected_fail'), ', ',
                           html.span('%i unexpected passes' % self.test_count["UNEXPECTED_PASS"],
                                     class_='unexpected_pass'), '.'),
                    html.h2('Results'),
                    html.table([html.thead(
                        html.tr([
                            html.th('Result', class_='sortable', col='result'),
                            html.th('Test', class_='sortable', col='name'),
                            html.th('Duration', class_='sortable numeric', col='duration'),
                            html.th('Links')]), id='results-table-head'),
                        html.tbody(self.result_rows, id='results-table-body')], id='results-table')))

        return u"<!DOCTYPE html>\n" + doc.unicode(indent=2)
