# -*- coding: utf-8 -*-
"""
Test MOOSE installation with moose-examples.

"""

from __future__ import print_function, division

__author__           = "Dilawar Singh"
__copyright__        = "Copyright 2016, Dilawar Singh"
__credits__          = ["NCBS Bangalore"]
__license__          = "GNU GPL"
__version__          = "1.0.0"
__maintainer__       = "Dilawar Singh"
__email__            = "dilawars@ncbs.res.in"
__status__           = "Development"

import sys
import os
import tempfile
import re
import subprocess
import threading
import signal
import logging
from collections import defaultdict
import time

logging.basicConfig(
        level=logging.DEBUG,
        format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
        datefmt='%m-%d %H:%M'
        )
console = logging.StreamHandler()
console.setLevel(logging.WARNING)
formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
console.setFormatter(formatter)
_logger = logging.getLogger('moose.test')
_logger.addHandler(console)

test_data_url_ = 'https://github.com/BhallaLab/moose-examples/archive/master.zip'
test_repo_url_ = 'https://github.com/BhallaLab/moose-examples'
test_dir_ = os.path.join( tempfile.gettempdir( ), 'moose-examples' )

ignored_dict_ = defaultdict( list )
test_status_ = defaultdict( list )
total_ = 0

matplotlibrc_ = '''
backend : agg
interactive : True
'''

# Handle CTRL+C
def signal_handler(signal, frame):
    print( 'You pressed Ctrl+C!' )
    print_test_stat( )
    quit(-1)

signal.signal(signal.SIGINT, signal_handler)

# Credit: https://stackoverflow.com/a/4825933/1805129
class Command(object):
    def __init__(self, cmd):
        self.cmd = cmd
        self.process = None
        self.fnull = open( os.devnull, 'w' )

    def __repr__( self ):
        return ' '.join( self.cmd )

    def run(self, timeout):
        def target():
            _logger.info( "Running %s" % self )
            self.process = subprocess.Popen(
                    self.cmd, shell=False
                    , stdout = self.fnull, stderr = subprocess.STDOUT
                    )
            self.process.communicate()

        thread = threading.Thread( target = target )
        thread.start()
        thread.join(timeout)
        if thread.is_alive():
            self.process.terminate()
            thread.join()

        if self.process.stderr is not None:
            _logger.warn( '%s' % self.process.stderr.read() )

        return self.process.returncode

def init_test_dir( ):
    global test_dir_
    global test_url_
    if( not os.path.exists( test_dir_ ) ):
        os.makedirs( test_dir_ )
        _logger.info( "Donwloading test repository" )
        subprocess.call(
                [ 'git', 'clone', '--depth=10', test_repo_url_, test_dir_ ]
                )
    os.chdir( test_dir_ )

def suitable_for_testing( script ):
    with open(script, 'r', encoding='utf8') as f:
        txt = f.read()
        if not re.search( r'main\(\s*\)', txt ):
            _logger.debug( 'Script %s does not contain main( )' % script )
            return False, 'main( ) not found'
        if re.search( r'raw_input\(\s*\)', txt ):
            _logger.debug( 'Script %s requires user input' % script )
            return False, 'waits for user input'
    return True, 'OK'

def run_test( index, testfile, timeout,  **kwargs):
    """Run a given test
    """
    global test_status_
    global total_
    pyExec = os.environ.get( 'PYTHON_EXECUTABLE', sys.executable )
    cmd = Command( [ pyExec, testfile ] )

    ti = time.time( )
    name = os.path.basename( testfile )
    out = (name + '.' * 50)[:40]
    print( '[TEST %3d/%d] %41s ' % (index, total_, out), end='' )
    sys.stdout.flush( )

    # Run the test.
    status = cmd.run( timeout = timeout )
    t = time.time( ) - ti
    print( '% 7.2f ' % t, end='' )
    sys.stdout.flush( )

    # Change to directory and copy before running then test.
    cwd = os.path.dirname( testfile )
    os.chdir( cwd )
    with open( os.path.join( cwd, 'matplotlibrc' ), 'w' ) as f:
        _logger.debug( 'Writing matplotlibrc to %s' % cwd )
        f.write( matplotlibrc_ )

    if status != 0:
        if status == -15:
            msg = '% 4d TIMEOUT' % status
            test_status_[ 'TIMED-OUT' ].append( testfile )
        else:
            msg = '% 4d FAILED' % status
            test_status_[ 'FAILED' ].append( testfile )
        print( msg )
    else:
        print( '% 4d PASSED' % status )
        test_status_[ 'PASSED' ].append( testfile )

    sys.stdout.flush( )

def print_test_stat( ):
    global test_status_
    for status in test_status_:
        print( 'Total %d tests %s' % (len( test_status_[status] ), status ) )

def test_all( timeout, **kwargs ):
    global test_dir_
    global total_
    scripts = [ ]
    for d, ds, fs in os.walk( test_dir_ ):
        for f in fs:
            if not re.match( r'.+\.py$', f):
                continue
            filepath = os.path.join( d, f  )
            isOK, msg = suitable_for_testing( filepath )
            if isOK:
                scripts.append( filepath )
            else:
                ignored_dict_[ msg ].append( filepath )

    for k in ignored_dict_:
        _logger.debug( '[INFO] Ignored due to %s' % k )
        _logger.debug( '\n\t'.join( ignored_dict_[ k ] ) )

    _logger.info( 'Total %d valid tests found' % len( scripts ) )
    total_ = len( scripts )
    for i, s in enumerate( scripts ):
        _logger.info( 'Running test (timeout=%s) : %s' % (timeout,s))
        run_test(i, s, timeout, **kwargs )


def test( timeout = 60, **kwargs ):
    """Download and run tests.

    """
    print( '[INFO] Running test with timeout %d sec' % timeout )
    try:
        init_test_dir( )
    except Exception as e:
        print( '[INFO] Failed to clone moose-examples. Error was %s' % e )
        quit( )

    test_all( timeout = timeout, **kwargs  )
    print_test_stat( )

if __name__ == '__main__':
    test( )
