#!/usr/bin/env python

import hotshot
import hotshot.stats
import os
import sys
import unittest

from test import pystone
import time

import Cheetah.NameMapper
import Cheetah.Template
from Cheetah.compat import unicode

# This can be turned on with the `--debug` flag when running the test
# and will cause the tests to all just dump out how long they took
# insteasd of asserting on duration
DEBUG = False

# TOLERANCE in Pystones
kPS = 1000
TOLERANCE = 0.5*kPS  # noqa: E226 missing whitespace around operator


class DurationError(AssertionError):
    pass


_pystone_calibration_mark = None


def _pystone_calibration():
    global _pystone_calibration_mark
    if not _pystone_calibration_mark:
        _pystone_calibration_mark = pystone.pystones(loops=pystone.LOOPS)
    return _pystone_calibration_mark


def perftest(max_num_pystones, current_pystone=None):
    '''
        Performance test decorator based off the 'timedtest'
        decorator found in this Active State recipe:
            http://code.activestate.com/recipes/440700/
    '''
    if not isinstance(max_num_pystones, float):
        max_num_pystones = float(max_num_pystones)

    if not current_pystone:
        current_pystone = _pystone_calibration()

    def _test(function):
        def wrapper(*args, **kw):
            start_time = time.time()
            try:
                return function(*args, **kw)
            finally:
                total_time = time.time() - start_time
                if total_time == 0:
                    pystone_total_time = 0
                else:
                    pystone_rate = current_pystone[0] / current_pystone[1]
                    pystone_total_time = total_time / pystone_rate
                global DEBUG
                if DEBUG:
                    print('The test "%s" took: %s pystones' % (
                        function.__name__, pystone_total_time))
                else:
                    if pystone_total_time > (max_num_pystones + TOLERANCE):
                        raise DurationError(
                            'Test too long (%.2f Ps, need at most %.2f Ps)'
                            % (pystone_total_time, max_num_pystones))
        return wrapper
    return _test


class DynamicTemplatePerformanceTest(unittest.TestCase):
    loops = 10
    # @perftest(1200)

    def test_BasicDynamic(self):
        template = '''
            #def foo(arg1, arg2)
                #pass
            #end def
        '''
        for i in range(self.loops):
            klass = Cheetah.Template.Template.compile(template)
            assert klass
    test_BasicDynamic = perftest(1200)(test_BasicDynamic)


class PerformanceTest(unittest.TestCase):
    iterations = 100000
    display = False
    save = False

    def runTest(self):
        self.prof = hotshot.Profile('%s.prof' % self.__class__.__name__)
        self.prof.start()
        for i in range(self.iterations):
            if hasattr(self, 'performanceSample'):
                self.display = True
                self.performanceSample()
        self.prof.stop()
        self.prof.close()
        if self.display:
            print('>>> %s (%d iterations) ' % (self.__class__.__name__,
                                               self.iterations))
            stats = hotshot.stats.load('%s.prof' % self.__class__.__name__)
            # stats.strip_dirs()
            stats.sort_stats('time', 'calls')
            stats.print_stats(50)

        if not self.save:
            os.unlink('%s.prof' % self.__class__.__name__)


class DynamicMethodCompilationTest(PerformanceTest):
    def performanceSample(self):
        template = '''
            #import sys
            #import os
            #def testMethod()
                #set foo = [1, 2, 3, 4]
                #return $foo[0]
            #end def
        '''
        template = Cheetah.Template.Template.compile(
            template, keepRefToGeneratedCode=False)
        template = template()
        value = template.testMethod()  # noqa: F841


class BunchOfWriteCalls(PerformanceTest):
    iterations = 1000

    def performanceSample(self):
        template = '''
            #import sys
            #import os
            #for i in range(1000)
                $i
            #end for
        '''
        template = Cheetah.Template.Template.compile(
            template, keepRefToGeneratedCode=False)
        template = template()
        value = template.respond()
        del value


class DynamicSimpleCompilationTest(PerformanceTest):
    def performanceSample(self):
        template = '''
            #import sys
            #import os
            #set foo = [1,2,3,4]

            Well hello there! This is basic.

            Here's an array too: $foo
        '''
        template = Cheetah.Template.Template.compile(
            template, keepRefToGeneratedCode=False)
        template = template()
        template = unicode(template)


class FilterTest(PerformanceTest):
    template = None

    def setUp(self):
        super(FilterTest, self).setUp()
        template = '''
            #import sys
            #import os
            #set foo = [1, 2, 3, 4]

            $foo, $foo, $foo
        '''
        template = Cheetah.Template.Template.compile(
            template, keepRefToGeneratedCode=False)
        self.template = template()

    def performanceSample(self):
        value = unicode(self.template)  # noqa: F841


class LongCompileTest(PerformanceTest):
    ''' Test the compilation on a sufficiently large template '''
    def compile(self, template):
        return Cheetah.Template.Template.compile(
            template, keepRefToGeneratedCode=False)

    def performanceSample(self):
        template = '''
            #import sys
            #import Cheetah.Template

            #extends Cheetah.Template.Template

            #def header()
                <center><h2>This is my header</h2></center>
            #end def

            #def footer()
                #return "Huzzah"
            #end def

            #def scripts()
                #pass
            #end def

            #def respond()
                <html>
                    <head>
                        <title>${title}</title>

                        $scripts()
                    </head>
                    <body>
                        $header()

                        #for $i in $range(10)
                            This is just some stupid page!
                            <br/>
                        #end for

                        <br/>
                        $footer()
                    </body>
                    </html>
            #end def

        '''
        return self.compile(template)


class LongCompile_CompilerSettingsTest(LongCompileTest):
    def compile(self, template):
        return Cheetah.Template.Template.compile(
            template, keepRefToGeneratedCode=False,
            compilerSettings={'useStackFrames': True, 'useAutocalling': True})


class LongCompileAndRun(LongCompileTest):
    def performanceSample(self):
        template = super(LongCompileAndRun, self).performanceSample()
        template = template(searchList=[{'title': 'foo'}])
        template = template.respond()


if __name__ == '__main__':
    if '--debug' in sys.argv:
        DEBUG = True
        sys.argv = [arg for arg in sys.argv if not arg == '--debug']
    unittest.main()
