import os
import sys

import pyutils.config as conf
from pyutils import ioutils
from .testresult import TSResult, UTResult
from pyutils.filesfinder import FilesFinder, FilterType
from .cliprinter import CLIPrinter
from .utestsimplebinary import UTestSimpleBinary
from .utestdir import UTestDir
from .filesdumper import FilesDumper

'''
Can build and run all tests given only the root dir
It uses file/dir names matching and conf files to figure out how to run tests

- root: Root source directory of the project
- output_dir: Directory where all files generated by obts are saved
- build_dir: Directory where all files are build by the buildtool. 
  Optional. Used to locate binary files
- binary_dir: Directory where all binaries are build by the buildtool. 
  Optional. Used to locate binary files

Global config file:
The global config file (at <root>/obdev.config.json) contains many properties used by TSAuto:

- build_dir: 
Same as cons param, but cons param as precedence. Relative to <root>

- binary_dir: 
Same as cons param, but cons param as precedence. Relative to <root>

- filebuilder-cc (<string>, default="clang")
C Compiler used to build binary files

- filebuilder-cxx (<string>, default="clang++")
C Compiler used to build binary files

- filebuilder-cc-flags (<string[]>, default = ['-Wall', '-Wextra', '-Werror', '-std=c99'])
C Compiler flags used to build binary files

- filebuilder-cxx-flags (<string[]>, default = ['-Wall', '-Wextra', '-Werror', '-std=c++17'])
C++ Compiler flags used to build binary files



Unit Test Rules:

Standalone binaries:
Check in recursive dirs for all binaries of the form utest_<name>.bin
Run the binarry, must finish with return status 0

UnitTest directory:
Check in recursive dirs for all dirs of the form utest_<name>
There can be a config.json file at the root of this directory, to change default options.

Basic scenario:

1) load test-app, this is the actual test to be run. Execute it without args, and capture stdout
test-app is built using the FileBuilder, more infos in filebuilder.py.
Build chain is test_<name>.<appext> <= test_<name>.<srcext>
Look for files in: current utest dir, binaries dir, build dir, '<out_obtsdir>/tmp/utest_<name>'
Files built by the the build chain are saved into <out_obtsdir> for reuse by future calls.
Try to load first item of the chain, if not found try to build it from second element, and so on.
Can configure paths using config.json. Eg: add entry test_<name>.c = /other/path/to/c/input.txt
<dataext>: .data
<appext>: binary (.bin), python (.py)
<srcext>: C (.c), C++ (.cc/.cpp)

2) load ref-data, this is the expected output of the test. 
Build chain is ref_<name>.<dataext> <= ref_<name>.<appext> <= ref_<name>.<srcext>
Works exactly like test-app

3) Check if output of test-app and ref-data content are the same.

Modiying one item of the chain doesn't discard files built with earlier versions of the files.
The solution is to manually remove entries in <out_obtsdir>
[TODO] Makes the Buildchain detect and discard files build by edited files.
[TODO] Config files are not used yet


Input files scenario:

Input files are in the utest dir, all files with extension .input.
One independant unit test is run for each input file, where the input file is given to the test and ref programs.
The Build chain for the test program is the same, but the build chain for the ref output is duifferent:

There is one build chain for every input file <inputname>.input:
ref_<name>__in_<inputname>.<dataext> <= ref_<name>.<appext> <= ref_<name>.<srcext>


'''
class TSAuto:


    def __init__(self, root, output_dir=None, build_dir=None, binary_dir=None):
        if output_dir is None:
            output_dir = './obts'
        
        self.root = os.path.abspath(root)
        self.ts = TSResult(proj_name='app')
        self.cli = CLIPrinter(self.ts)
        self.out_dir = os.path.abspath(output_dir)
        self.build_dir = None if build_dir is None else os.path.abspath(self.build_dir)
        self.binary_dir = None if binary_dir is None else os.path.abspath(self.binary_dir)
        self.search_dirs = [os.path.abspath('.')]

    '''
    Run the whole testsuite.
    Returns 0 if all tests passed, 1 otherwhise
    '''
    def run(self):
        cf_load = conf.push_file(os.path.join(self.root, './obdev.config.json'))
        self._read_config()
        
        self._run_standalone_bins()
        self._run_dirs()

        valid = self.ts.valid()
        self.cli.summary()

        fd = FilesDumper(self.ts, self.out_dir)
        fd.dump()

        if cf_load:
            conf.pop_file()        
        
        return 0 if valid else 1

    def _read_config(self):
        conf_binary_dir = conf.get('binary_dir')
        conf_build_dir = conf.get('build_dir')
        
        if self.binary_dir is None and conf_binary_dir is not None:
            self.binary_dir = os.path.join(self.root, conf_binary_dir)
        if self.build_dir is None and conf_build_dir is not None:
            self.build_dir = os.path.join(self.root, conf_build_dir)

        if self.build_dir is not None:
            self.search_dirs.append(self.build_dir)
        if self.binary_dir is not None:
            self.search_dirs.append(self.binary_dir)

    def _run_standalone_bins(self):
        bin_exts = conf.get('bin-extensions')
        
        ff = FilesFinder()
        ff.filter_begin_with("utest_", FilterType.File)
        ff.filter_ext_is_any(bin_exts, FilterType.File)
        files, _ = ff.run(self.root)
        
        for f in files:
            self._run_simple_bin(f)

    def _run_simple_bin(self, bin_path):
        name = os.path.basename(bin_path)
        name = name[6:-4]
        ut = UTestSimpleBinary(self.cli, name, bin_path)
        ut.run()


    def _run_dirs(self):
        ff = FilesFinder()
        ff.filter_begin_with("utest_", FilterType.Dir)
        ff.ignore_dir("CMakeFiles")
        _, dirs = ff.run(self.root)
        
        for d in dirs:
            self._run_dir(d)


    def _run_dir(self, dir_path):
        ut = UTestDir(self.cli, dir_path, self.out_dir, self.search_dirs)
        ut.run()
        


        
        
        
