#!/usr/bin/env python
# coding: utf-8
from __future__ import division, print_function, unicode_literals
import re
import timeit
import codecs
import argparse
import sys
from builtins import str
from commonmark.render.html import HtmlRenderer
from commonmark.main import Parser, dumpAST


class colors(object):
    HEADER = '\033[95m'
    OKBLUE = '\033[94m'
    OKGREEN = '\033[92m'
    WARNING = '\033[93m'
    FAIL = '\033[91m'
    ENDC = '\033[0m'


def trace_calls(frame, event, arg):
    co = frame.f_code
    func_name = co.co_name
    if func_name == "write":
        return
    line_no = frame.f_lineno
    filename = co.co_filename
    if event == "call" and not re.match("__", func_name) and \
       re.search("CommonMark.py", filename) \
       and func_name != "dumpAST":
        print("-> " + frame.f_back.f_code.co_name +
              " at " + str(frame.f_back.f_lineno) +
              " called " + func_name + " at " + str(line_no) +
              " in " + filename)
        return trace_calls


def main():
    parser = argparse.ArgumentParser(
        description="script to run the CommonMark specification tests " +
        "against the CommonMark.py parser")
    parser.add_argument(
        '-t',
        help="Single test to run or comma separated list " +
        "of tests (-t 10 or -t 10,11,12,13)")
    parser.add_argument(
        '-p',
        action="store_true",
        help="Print passed test information")
    parser.add_argument(
        '-f',
        action="store_true",
        help="Print failed tests (during -np...)")
    parser.add_argument(
        '-i',
        action="store_true",
        help="Interactive Markdown input mode")
    parser.add_argument(
        '-d',
        action="store_true",
        help="Debug, trace calls")
    parser.add_argument(
        '-np',
        action="store_true",
        help="Only print section header, tick, or cross")
    parser.add_argument(
        '-s',
        action="store_true",
        help="Print percent of tests passed by category")
    args = parser.parse_args()

    if args.d:
        sys.settrace(trace_calls)

    renderer = HtmlRenderer()
    parser = Parser()

    f = codecs.open("spec.txt", encoding="utf-8")
    datalist = []
    for line in f:
        datalist.append(line)
    data = "".join(datalist)
    passed = 0
    failed = 0
    catStats = {}
    examples = []
    example_number = 0
    current_section = ""
    tabChar = '\u2192'
    spaceChar = '\u2423'
    nbspChar = '\u00A0'

    def showSpaces(t):
            t = re.sub("\\t", tabChar, t)
            t = re.sub(" ", spaceChar, t)
            t = re.sub(nbspChar, spaceChar, t)
            return t

    t = re.sub("\r\n", "\n", data)

    tests = re.sub(
        re.compile("^<!-- END TESTS -->(.|[\n])*", flags=re.M), '', t)
    testMatch = re.findall(
        re.compile(
            "^`{32} example\n"
            "([\s\S]*?)^\.\n([\s\S]*?)"
            "^`{32}$"
            "|^#{1,6} *(.*)$",
            re.M),
        tests)

    for match in testMatch:
        if not match[2] == "":
            current_section = match[2]
        else:
            example_number += 1
            examples.append({
                'markdown': match[0],
                'html': match[1],
                'section': current_section,
                'number': example_number})

    current_section = ""

    startTime = timeit.default_timer()

    if args.i:
        print(
            colors.OKGREEN +
            "(To end input of Markdown block enter 'end' on " +
            "it's own line, to quit enter 'quit')" +
            colors.ENDC)
        while True:
            s = ""
            while True:
                if sys.version_info >= (3, 0):
                    inp = input(colors.OKBLUE + 'Markdown: ' + colors.ENDC)
                else:
                    inp = raw_input(colors.OKBLUE + 'Markdown: ' + colors.ENDC)  # noqa

                if not inp == "end" and inp != "quit":
                    s += inp + "\n"
                elif inp == "end":
                    s = s[:-1]
                    break
                elif inp == "quit":
                    print(colors.HEADER+"bye!"+colors.ENDC)
                    exit(0)
            ast = parser.parse(s)
            html = renderer.render(ast)
            print(colors.WARNING+"="*10+"AST====="+colors.ENDC)
            dumpAST(ast)
            print(colors.WARNING+"="*10+"HTML===="+colors.ENDC)
            print(html)

    # some tests?
    if args.t:
        tests = args.t.split(",")
        choice_examples = []
        for t in tests:
            if not t == "" and len(examples) > int(t):
                choice_examples.append(examples[int(t)-1])
        examples = choice_examples

    # all tests

    for i, example in enumerate(examples):  # [0,examples[0]]
        if not example['section'] == "" and \
           not current_section == example['section']:
            print('\n' + colors.HEADER + '[' + example['section'] + ']' +
                  colors.ENDC + ' ', end='')
            current_section = example['section']
            catStats.update({current_section: [0, 0, 0]})

        catStats[current_section][2] += 1
        if args.d:
            print(colors.HEADER+"[Parsing]"+colors.ENDC)
        ast = parser.parse(re.sub(tabChar, "\t", example['markdown']))
        if args.d:
            print(colors.HEADER+"[Rendering]"+colors.ENDC)
        actual = renderer.render(ast)
        if re.sub('\t', tabChar, actual) == example['html']:
            passed += 1
            catStats[current_section][0] += 1
            if not args.f:
                print(colors.OKGREEN + '✓' + colors.ENDC, end='')
            if args.d:
                dumpAST(ast)
            if args.p or args.d and not args.np:
                print(
                    colors.OKBLUE +
                    "=== markdown ===============\n" +
                    colors.ENDC + showSpaces(example['markdown']) +
                    colors.OKBLUE +
                    "\n=== expected ===============\n" +
                    colors.ENDC + showSpaces(example['html']) +
                    colors.OKBLUE +
                    "\n=== got ====================\n" +
                    colors.ENDC + showSpaces(actual))
        else:
            failed += 1
            catStats[current_section][1] += 1
            if args.t:
                print("Test #" + str(args.t.split(",")[i]), end='')
            else:
                print("Test #" + str(i+1), end='')
            print(' ' + colors.FAIL + "✗" + colors.ENDC)
            if args.d:
                dumpAST(ast)
            if not args.np or args.f:
                print(
                    colors.WARNING +
                    "=== markdown ===============\n" +
                    colors.ENDC + showSpaces(example['markdown']) +
                    colors.WARNING +
                    "\n=== expected ===============\n" +
                    colors.ENDC + showSpaces(example['html']) +
                    colors.WARNING +
                    "\n=== got ====================\n" +
                    colors.ENDC + showSpaces(actual))

    print('\n' + str(passed) + ' tests passed, ' + str(failed) + ' failed')

    endTime = timeit.default_timer()
    runTime = endTime - startTime

    if args.s:
        for i in catStats.keys():
            per = catStats[i][0]/catStats[i][2]
            print(colors.HEADER + "[" + i + "]" + colors.ENDC +
                  "\t" + str(per*100) + "% Passed")

    print("runtime: " + str(runTime) + "s")

    if (failed > 0):
        sys.exit(1)


if __name__ == '__main__':
    main()
