import subprocess
import sys
import re
from platforms.stm32.stm32 import set_stm32_platform
from platforms.avr.avr import set_avr_platform
from platforms.mips.mips import set_mips_platform
from platforms.mipsel.mipsel import set_mipsel_platform
from platforms.riscv64.riscv64 import set_riscv64_platform

platforms = {
    'STM32': set_stm32_platform,
    'AVR': set_avr_platform,
    'MIPS': set_mips_platform,
    'MIPSEL': set_mipsel_platform,
    'RISCV64': set_riscv64_platform,
}

try:
    # Make terminal colors work on windows
    import colorama
    colorama.init()
except ImportError:
    pass

# UTF-8 support on Python 2
if sys.version_info.major == 2:
    import codecs
    open = codecs.open

def add_nanopb_builders(env):
    '''Add the necessary builder commands for nanopb tests.'''

    # Build command that runs a test program and saves the output
    def run_test(target, source, env):
        if len(source) > 1:
            infile = open(str(source[1]), 'rb')
        else:
            infile = None
        
        if "COMMAND" in env:
            args = [env["COMMAND"]]
        else:
            args = [str(source[0])]
        
        if 'ARGS' in env:
            args.extend(env['ARGS'])

        if "TEST_RUNNER" in env:
            args = [env["TEST_RUNNER"]] + args
        
        print('Command line: ' + str(args))
        pipe = subprocess.Popen(args,
                                stdin = infile,
                                stdout = open(str(target[0]), 'w'),
                                stderr = sys.stderr)
        result = pipe.wait()
        if result == 0:
            print('\033[32m[ OK ]\033[0m   Ran ' + args[0])
        else:
            print('\033[31m[FAIL]\033[0m   Program ' + args[0] + ' returned ' + str(result))
        return result
        
    run_test_builder = Builder(action = run_test,
                               suffix = '.output')
    env.Append(BUILDERS = {'RunTest': run_test_builder})

    # Build command that decodes a message using protoc
    def decode_actions(source, target, env, for_signature):
        esc = env['ESCAPE']
        dirs = ' '.join(['-I' + esc(env.GetBuildPath(d)) for d in env['PROTOCPATH']])
        return '$PROTOC $PROTOCFLAGS %s --decode=%s %s <%s >%s' % (
            dirs, env['MESSAGE'], esc(str(source[1])), esc(str(source[0])), esc(str(target[0])))

    decode_builder = Builder(generator = decode_actions,
                             suffix = '.decoded')
    env.Append(BUILDERS = {'Decode': decode_builder})    

    # Build command that encodes a message using protoc
    def encode_actions(source, target, env, for_signature):
        esc = env['ESCAPE']
        dirs = ' '.join(['-I' + esc(env.GetBuildPath(d)) for d in env['PROTOCPATH']])
        return '$PROTOC $PROTOCFLAGS %s --encode=%s %s <%s >%s' % (
            dirs, env['MESSAGE'], esc(str(source[1])), esc(str(source[0])), esc(str(target[0])))

    encode_builder = Builder(generator = encode_actions,
                             suffix = '.encoded')
    env.Append(BUILDERS = {'Encode': encode_builder})    

    # Build command that asserts that two files be equal
    def compare_files(target, source, env):
        data1 = open(str(source[0]), 'rb').read()
        data2 = open(str(source[1]), 'rb').read()
        if data1 == data2:
            print('\033[32m[ OK ]\033[0m   Files equal: ' + str(source[0]) + ' and ' + str(source[1]))
            return 0
        else:
            print('\033[31m[FAIL]\033[0m   Files differ: ' + str(source[0]) + ' and ' + str(source[1]))
            return 1

    compare_builder = Builder(action = compare_files,
                              suffix = '.equal')
    env.Append(BUILDERS = {'Compare': compare_builder})

    # Build command that checks that each pattern in source2 is found in source1.
    def match_files(target, source, env):
        data = open(str(source[0]), 'r', encoding = 'utf-8').read()
        patterns = open(str(source[1]), 'r', encoding = 'utf-8')
        for pattern in patterns:
            if pattern.strip():
                invert = False
                if pattern.startswith('! '):
                    invert = True
                    pattern = pattern[2:]
                
                status = re.search(pattern.strip(), data, re.MULTILINE)
                
                if not status and not invert:
                    print('\033[31m[FAIL]\033[0m   Pattern not found in ' + str(source[0]) + ': ' + pattern)
                    return 1
                elif status and invert:
                    print('\033[31m[FAIL]\033[0m   Pattern should not exist, but does in ' + str(source[0]) + ': ' + pattern)
                    return 1
        else:
            print('\033[32m[ OK ]\033[0m   All patterns found in ' + str(source[0]))
            return 0

    match_builder = Builder(action = match_files, suffix = '.matched')
    env.Append(BUILDERS = {'Match': match_builder})