import json
import jsonschema
from jsonschema import validate
import snakemake.utils
import sys, getopt
import shutil
import filecmp
from pathlib import Path
import os
import random

include: "rules/helper_functions.py"

check_system_requirements()

# This is a workaround. Needed a variable for the configfilename
# and this seemed to be the only way. But there are probably better ways.
args = sys.argv
configfilename = "config/config.json"
i = 0
for arg in args:
    if arg == "--configfile" or arg == "--configfiles":  # This is strange
        configfilename = args[i + 1]
        break
    i += 1


configfile: configfilename

with open("workflow/schemas/config.schema.json") as json_file:
    schema = json.load(json_file)

# Write the definitions
# Generate the evaluation schemas
item = schema["$defs"]["benchmark_setup"]["properties"]["evaluation"]["properties"]
for p in Path("workflow/rules/evaluation/").iterdir():
    # skip .DS_Store
    if p.name == ".DS_Store":
        continue

    module_schema_filename = "workflow/rules/evaluation/{}/schema.json".format(p.name)
    with open(module_schema_filename) as json_file:
        module_schema = json.load(json_file)

    item[p.name] = module_schema


# now the same for the data modules
data_module_schemas = schema["properties"]["resources"]["properties"]["data"]["properties"]
for p in Path("workflow/rules/data/").iterdir():
    if p.name == ".DS_Store" or p.name == "fixed_data":
        continue
    module_schema_filename = "workflow/rules/data/{}/schema.json".format(p.name)
    with open(module_schema_filename) as json_file:
        module_schema = json.load(json_file)
    
    data_module_schemas[p.name] = module_schema

# now for the parameters modules
parameters_module_schemas = schema["properties"]["resources"]["properties"]["parameters"]["properties"]
for p in Path("workflow/rules/parameters/").iterdir():
    if p.name == ".DS_Store" or p.name == "fixed_params":
        continue

    module_schema_filename = "workflow/rules/parameters/{}/schema.json".format(p.name)
    with open(module_schema_filename) as json_file:
        module_schema = json.load(json_file)

    parameters_module_schemas[p.name] = module_schema

# now the same for thr graphs modules
graph_module_schemas = schema["properties"]["resources"]["properties"]["graph"]["properties"]
for p in Path("workflow/rules/graph/").iterdir():
    if p.name == ".DS_Store" or p.name == "fixed_graph":
        continue

    module_schema_filename = "workflow/rules/graph/{}/schema.json".format(p.name)
    with open(module_schema_filename) as json_file:
        module_schema = json.load(json_file)

    graph_module_schemas[p.name] = module_schema

# now the same for the structure_learning_algorithms modules
structure_learning_algorithms_module_schemas = schema["properties"]["resources"]["properties"]["structure_learning_algorithms"]["properties"]
for p in Path("workflow/rules/structure_learning_algorithms/").iterdir():
    if p.name == ".DS_Store":
        continue

    module_schema_filename = "workflow/rules/structure_learning_algorithms/{}/schema.json".format(p.name)
    with open(module_schema_filename) as json_file:
        module_schema = json.load(json_file)

    structure_learning_algorithms_module_schemas[p.name] = module_schema

# Yes this is a hack. But it works probably. Any better ideas?
# Generate random hash for the temporary schema file
tmp_hash = random.getrandbits(128)
tmp_schema = f"schemas/tmp.config.schema_{tmp_hash}.json"
with open("workflow/"+tmp_schema, "w") as outfile:
    outfile.write(json.dumps(schema, indent=4))

snakemake.utils.validate(config, tmp_schema)
# remove the temporary schema file
os.remove("workflow/"+tmp_schema)

include: "rules/docker_images.smk"

container: docker_image("benchmark")

rule all:
    input:
        get_active_rules

if "validate" not in config or config["validate"] is True:
    include: "rules/validate.py"

# The pattern strings are generated from the json config file.
pattern_strings = {}
pattern_strings.update(get_algorithm_patterns(config))
pattern_strings.update(get_graph_patterns(config))
pattern_strings.update(get_parameter_patterns(config))
pattern_strings.update(get_data_patterns(config))
pattern_strings.update(get_mcmc_eval_patterns(config))
pattern_strings["mcmc_est"] = get_mcmc_est_pattern()
include: "rules/module_strings.py"
include: "rules/filename_patterns.py"

# Read which modules are MCMC module (have graphtraj output).
mcmc_modules = get_mcmc_modules()
json_string, json_string_mcmc_noest = gen_json_strings(config, pattern_strings, mcmc_modules)

# Include the graph modules
for p in Path("workflow/rules/graph/").glob("[!.]*"):
    module_name = p.name  # This is used inside the module to get their names
    if (p / "rule.smk").is_file():
        if p.name in pattern_strings or (p.name == "fixed_graph"):

            include: Path("rules/graph/", p.name, "rule.smk")


# Include the parameters modules
for p in Path("workflow/rules/parameters/").glob("[!.]*"):
    module_name = p.name  # This is used inside the module to get their names
    if (p / "rule.smk").is_file():
        if (p.name in pattern_strings) or (p.name == "fixed_params"):

            include: Path("rules/parameters/", p.name, "rule.smk")


# Include the data modules
for p in Path("workflow/rules/data/").glob("[!.]*"):
    module_name = p.name  # This is used inside the module to get their names
    if (p / "rules.smk").is_file():
        if (p.name in pattern_strings) or (p.name == "fixed_data"):
            include: Path("rules/data/", p.name, "rules.smk")
    if (p / "rule.smk").is_file():
        if (p.name in pattern_strings):
            include: Path("rules/data/", p.name, "rule.smk")


# Include the structure_learning_algorithms modules
for p in Path("workflow/rules/structure_learning_algorithms/").glob("[!.]*"):
    module_name = p.name  # This is used inside the module to get their names
    if (p / "rule.smk").is_file():
        if p.name in pattern_strings:
            # set the module name here
            include: Path("rules/structure_learning_algorithms/", p.name, "rule.smk")
            if p.name in mcmc_modules:
                rule:
                    name:
                        p.name + "_estimation"
                    input:
                        traj=alg_output_seqgraph_path_fine_match(p.name),
                    output:
                        adjmat=adjmat_estimate_path_mcmc(p.name)
                    params:
                        graph_type="chordal", # not used
                        estimator="{mcmc_estimator}",
                        threshold="{threshold}",
                        burnin_frac="{burnin_frac}",
                    container:
                        docker_image("pydatascience")
                    script:
                        "scripts/evaluation/graphtraj_est.py"
# Include the evaluation modules

for p in Path("workflow/rules/evaluation/").glob("[!.]*"):
    for bmark_setup in config["benchmark_setup"]: # Maybe includes same module multiple times
        if p.name not in bmark_setup["evaluation"]:
            continue
        if (p / "rules.smk").is_file():
            include: Path("rules/evaluation/", p.name, "rules.smk")
        elif (p / "rule.smk").is_file():
            include: Path("rules/evaluation/", p.name, "rule.smk")

# This one should thake the dataset as well, just to be able to
# ignore constrains including variables not in the dataset.
# Amaaybe inclue alg_input_data() in the path somehow?
rule convert_edge_constraints:
    input:
        edgeConstraints="resources/constraints/{edgeConstraints}",
        data=alg_input_data()
    output:
        #edgeConstraints_in_format="resources/constraints/{edgeConstraints}-{package}"
        edgeConstraints_in_format=temp("resources/constraints/data=/"+alg_input_data()+"/{edgeConstraints}-{package}")
    wildcard_constraints:
        edgeConstraints=r".*\.json"
    params:
        package = "{package}"
    script:
        "scripts/utils/convert_edge_constraints_all.R"
