#!/usr/bin/env python
# -*- coding: utf-8 -*-
# <steadymark - markdown-based test runner for python>
# Copyright (C) <2012>  Gabriel Falcão <gabriel@nacaolivre.org>
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following
# conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
from __future__ import unicode_literals

import os
import sys
import traceback
import codecs

try:
    from couleur import SUPPORTS_ANSI
except ImportError:
    SUPPORTS_ANSI = False

from steadymark.core import (
    SteadyMark,
    DocTestFailure,
)
from steadymark.six import text_type, PY3

if not PY3:
    # We do this so that redirecting to pipes will work
    sys.stdout = codecs.getwriter('utf8')(sys.stdout)


class Runner(object):
    def __init__(self, filename=None, text=u''):
        if filename and not os.path.exists(filename):
            print('steadymark could not find {0}'.format(filename))
            sys.exit(1)

        if filename:
            raw_md = codecs.open(filename, 'rb', 'utf-8').read()
            text = text_type(raw_md)

        self.steadymark = SteadyMark.inspect(text)
        self.filename = filename
        self.text = text

    def print_white(self, text, indentation=0):
        white = {
            True: u'\033[1;37m',
            False: u'',
        }
        for line in text.splitlines():
            print("{1}{2}{0}\033[0m".format(
                line, ' ' * indentation, white[SUPPORTS_ANSI]))

    def __getattr__(self, attr):
        if attr not in (
            'print_white',
            'print_green',
            'print_red',
            'print_yellow',
        ):
            return super(Runner, self).__getattribute__(attr)

        color_for = {
            'print_white': u'\033[1;37m',
            'print_red': u'\033[1;31m',
            'print_green': u'\033[1;32m',
            'print_yellow': u'\033[1;33m',
        }
        ansi = color_for[attr]
        if SUPPORTS_ANSI:
            color = ansi
            no_color = '\033[0m'
        else:
            no_color = color = ''

        def printer(text, indentation=0):
            for line in text.splitlines():
                print("{1}{2}{0}{3}".format(
                    line, ' ' * indentation, color, no_color))

        return printer

    def format_ms(self, ms):
        ms = int(ms)
        base = '{0}ms'.format(ms)
        if SUPPORTS_ANSI:
            return "\033[1;33m{0}\033[0m".format(base)
        else:
            return base

    def format_traceback(self, test, failure):
        exc, exc_instance, tb = failure
        # formatted_tb = traceback.format_exc(exc_instance).strip()
        # if 'None' == formatted_tb:
        formatted_tb = ''.join(traceback.format_tb(tb))
        formatted_tb = formatted_tb.replace(
            u'File "{0}"'.format(test.title),
            u'In the test "{0}"'.format(test.title),
        )
        formatted_tb = formatted_tb.replace(
            u'@STEADYMARK@', text_type(test.title))

        if SUPPORTS_ANSI:
            color = '\033[1;36m'
        else:
            color = ''
        return u'{0} {3}{1}\n{2}\n'.format(
            exc.__name__,
            exc_instance,
            formatted_tb,
            color,
        )

    def report_success(self, test, shift, ms):
        self.print_green('\xe2\x9c\x93 {0}'.format(ms))
        print

    def report_failure(self, test, failure, shift, ms):
        self.print_red('\xe2\x9c\x97 {0}'.format(ms))
        exc_type, exc_val, exc_tb = failure

        if exc_type is DocTestFailure:
            formatted_tb = u"the line {0}: {1}\n".format(
                exc_val.example.lineno,
                exc_val.example.source,
            )
            if exc_val.example.exc_msg:
                formatted_tb += "{0}\n".format(
                    exc_val.example.exc_msg)
            else:
                formatted_tb += ("resulted in:\n{0}\n"
                                 "when expecting:\n{1}\n".format(
                                     exc_val.got, exc_val.example.want))

        else:
            formatted_tb = self.format_traceback(test, failure)

        self.print_red(formatted_tb, indentation=2)

        header = "original code:"
        header_length = len(header)
        self.print_white("*" * header_length)
        self.print_white(header)
        self.print_white("*" * header_length)

        self.print_yellow(test.raw_code, indentation=2)
        print

    def report_test_result(self, test, failure, before, after):
        shift = before - after
        ms = self.format_ms(shift.microseconds / 1000)

        if not failure:
            return self.report_success(test, shift, ms)

        return self.report_failure(test, failure, shift, ms)

    def run(self):
        if self.filename:
            print("Running tests from {0}".format(self.filename))

        exit_status = 0
        for test in self.steadymark.tests:
            title = "{0} ".format(test.title)
            title_length = len(title)
            print("." * title_length)
            sys.stdout.write(title)
            result, failure, before, after = test.run()
            if failure:
                exit_status = 1
            self.report_test_result(test, failure, before, after)

        if exit_status is not 0:
            sys.exit(exit_status)

        return self.steadymark