# ------------------------------------------------------------------------------
# This holds all the meta information for a test script.  It contains the
# name of the inner class and an array of CollectedTests.  This does not parse
# anything, it just holds the data about parsed scripts and tests.  The
# TestCollector is responsible for populating this object.
#
# This class also facilitates all the exporting and importing of tests.
# ------------------------------------------------------------------------------
var CollectedTest = load('res://addons/gut/collected_test.gd')

var _utils = null
var _lgr = null

# One entry per test found in the script.  Added externally by TestCollector
var tests = []
# One entry for before_all and after_all (maybe add before_each and after_each).
# These are added by Gut when running before_all and after_all for the script.
var setup_teardown_tests = []
var inner_class_name:StringName
var path:String


# Set externally by test_collector after it can verify that the script was
# actually loaded.  This could probably be changed to just hold the GutTest
# script that was loaded, cutting down on complexity elsewhere.
var is_loaded = false

# Set by Gut when it decides that a script should be skipped.
# Right now this is whenever the script has the variable skip_script declared.
# the value of skip_script is put into skip_reason.
var was_skipped = false
var skip_reason = ''
var was_run = false


var name = '' :
    get: return path
    set(val):pass


func _init(utils=null,logger=null):
    _utils = utils
    _lgr = logger


func get_new():
    return load_script().new()


func load_script():
    var to_return = load(path)

    if(inner_class_name != null and inner_class_name != ''):
        # If we wanted to do inner classes in inner classses
        # then this would have to become some kind of loop or recursive
        # call to go all the way down the chain or this class would
        # have to change to hold onto the loaded class instead of
        # just path information.
        to_return = to_return.get(inner_class_name)

    return to_return

# script.gd.InnerClass
func get_filename_and_inner():
    var to_return = get_filename()
    if(inner_class_name != ''):
        to_return += '.' + String(inner_class_name)
    return to_return


# res://foo/bar.gd.FooBar
func get_full_name():
    var to_return = path
    if(inner_class_name != ''):
        to_return += '.' + String(inner_class_name)
    return to_return


func get_filename():
    return path.get_file()


func has_inner_class():
    return inner_class_name != ''


# Note:  although this no longer needs to export the inner_class names since
#        they are pulled from metadata now, it is easier to leave that in
#        so we don't have to cut the export down to unique script names.
func export_to(config_file, section):
    config_file.set_value(section, 'path', path)
    config_file.set_value(section, 'inner_class', inner_class_name)
    var names = []
    for i in range(tests.size()):
        names.append(tests[i].name)
    config_file.set_value(section, 'tests', names)


func _remap_path(source_path):
    var to_return = source_path
    if(!_utils.file_exists(source_path)):
        _lgr.debug('Checking for remap for:  ' + source_path)
        var remap_path = source_path.get_basename() + '.gd.remap'
        if(_utils.file_exists(remap_path)):
            var cf = ConfigFile.new()
            cf.load(remap_path)
            to_return = cf.get_value('remap', 'path')
        else:
            _lgr.warn('Could not find remap file ' + remap_path)
    return to_return


func import_from(config_file, section):
    path = config_file.get_value(section, 'path')
    path = _remap_path(path)
    # Null is an acceptable value, but you can't pass null as a default to
    # get_value since it thinks you didn't send a default...then it spits
    # out red text.  This works around that.
    var inner_name = config_file.get_value(section, 'inner_class', 'Placeholder')
    if(inner_name != 'Placeholder'):
        inner_class_name = inner_name
    else: # just being explicit
        inner_class_name = StringName("")


func get_test_named(name):
    return _utils.search_array(tests, 'name', name)


func mark_tests_to_skip_with_suffix(suffix):
    for single_test in tests:
        single_test.should_skip = single_test.name.ends_with(suffix)


func get_ran_test_count():
    var count = 0
    for t in tests:
        if(t.was_run):
            count += 1
    return count


func get_assert_count():
    var count = 0
    for t in tests:
        count += t.pass_texts.size()
        count += t.fail_texts.size()
    for t in setup_teardown_tests:
        count += t.pass_texts.size()
        count += t.fail_texts.size()
    return count


func get_pass_count():
    var count = 0
    for t in tests:
        count += t.pass_texts.size()
    for t in setup_teardown_tests:
        count += t.pass_texts.size()
    return count


func get_fail_count():
    var count = 0
    for t in tests:
        count += t.fail_texts.size()
    for t in setup_teardown_tests:
        count += t.fail_texts.size()
    return count


func get_pending_count():
    var count = 0
    for t in tests:
        count += t.pending_texts.size()
    return count


func get_passing_test_count():
    var count = 0
    for t in tests:
        if(t.is_passing()):
            count += 1
    return count


func get_failing_test_count():
    var count = 0
    for t in tests:
        if(t.is_failing()):
            count += 1
    return count


func get_risky_count():
    var count = 0
    if(was_skipped):
        count = 1
    else:
        for t in tests:
            if(t.is_risky()):
                count += 1
    return count


func to_s():
    var to_return = path
    if(inner_class_name != null):
        to_return += str('.', inner_class_name)
    to_return += "\n"
    for i in range(tests.size()):
        to_return += str('  ', tests[i].to_s())
    return to_return
