"""Settings module.

"""


from itertools import starmap
from dataclasses import dataclass
from configparser import ConfigParser

from cml.shared.parameter import *


class MetaSettings(type):
    INPUT_FILE: str = InputFile()
    LEARN_DIR: str = LearnDir()
    MAX_LEARN_DIR: int = MaxLearnDir()
    USE_EXISTING_MODELS: bool = UseExistingModels()
    KNOWLEDGE_DIR: str = KnowledgeDir()

    SET_TARGETS: str = SetTargets()
    SET_FEATURES: str = SetFeatures()
    CUT_TIME_STAMP:  bool = CutTimeStamp()
    BLOCK_SIZE: int = BlockSize()
    SORT_TIME_STAMP: bool = SortTimeStamp()

    MAX_BLOCKS: int = MaxBlocks()
    STACK_ITERATIONS: int = StackIterations()
    LEARN_BLOCK_MINIMUM: int = LearnblockMinimum()
    SIGMA_ZETA_CUTOFF: float = SigmaZetaCutoff()

    MAX_CATEGORIES: int = MaxCategories()
    MIN_CATEGORY_SIZE: int = MinCategorySize()
    MAX_MODEL_TARGETS: int = MaxModelTargets()
    MAX_TARGET_ERROR: float = MaxTargetError()

    MAX_FEATURES: int = MaxFeatures()
    MAX_FILTER_X: int = MaxFilterX()
    MAX_FILTER_Y: int = MaxFilterY()
    MAX_MODELS_REDUCTION: bool = MaxModelsReduction()
    MIN_TEST_ACCURACY: float = MinTestAccuracy()
    MAX_TEST_ERROR_AVG: float = MaxTestErrorAvg()
    MAX_TEST_ERROR_MAX: float = MaxTestErrorMax()
    RELIABILITY_SAMPLE: float = ReliabilitySample()
    MIN_RELIABILITY: float = MinReliability()
    REDUCE_MODEL_REDUNDANCY: bool = ReduceModelRedundancy()

    DECONST_STRATEGY: str = DeconstStrategy()
    DECONST_MODE: str = DeconstMode()
    DECONST_MAX_DISTANCE_T: int = DeconstMaxDistanceT()
    FORCE_TIME_EXPANSION: bool = ForceTimeExpansion()


class Settings(metaclass=MetaSettings):
    pass


@dataclass
class GeneralSettings:
    input_file: str = InputFile()
    learn_dir: str = LearnDir()
    max_learn_dir: int = MaxLearnDir()
    use_existing_models: bool = UseExistingModels()


@dataclass
class PreprocessingSettings:
    set_features: str = SetFeatures()
    set_targets: str = SetTargets()
    sort_time_stamp: bool = SortTimeStamp()
    cut_time_stamp: bool = CutTimeStamp()


@dataclass
class BlockProcessingSettings:
    block_size: int = BlockSize()
    max_blocks: int = MaxBlocks()
    stack_iterations: int = StackIterations()
    learn_block_minimum: int = LearnblockMinimum()
    sigma_zeta_cutoff: float = SigmaZetaCutoff()


@dataclass
class ConstructionSettings:
    max_categories: int = MaxCategories()
    min_category_size: int = MinCategorySize()
    max_model_targets: int = MaxModelTargets()
    max_target_error: float = MaxTargetError()
    construct_type: str = ""


@dataclass
class FeatureSelectionSettings:
    max_features: int = MaxFeatures()
    max_filter_x: int = MaxFilterX()
    max_filter_y: int = MaxFilterY()
    max_model_reduction: bool = MaxModelsReduction()


@dataclass
class ReconstructionSettings:
    min_test_accuracy: float = MinTestAccuracy()
    max_test_error_avg: float = MaxTestErrorAvg()
    max_test_error_max: float = MaxTestErrorMax()
    reliability_samle: float = ReliabilitySample()
    min_reliability: float = MinReliability()
    reduce_model_redundancy: bool = ReduceModelRedundancy()


@dataclass
class DeconstructionSettings:
    pass


def specific_settings_factory(settings_type: str):
    factory = {
        "general": starmap(
            GeneralSettings, [(Settings.INPUT_FILE,
                               Settings.LEARN_DIR,
                               Settings.MAX_LEARN_DIR,
                               Settings.USE_EXISTING_MODELS)]),
        "preprocessing": starmap(
            PreprocessingSettings, [(Settings.SET_FEATURES,
                                     Settings.SET_TARGETS,
                                     Settings.SORT_TIME_STAMP,
                                     Settings.CUT_TIME_STAMP)]),
        "block_processing": starmap(
            BlockProcessingSettings, [(Settings.BLOCK_SIZE,
                                       Settings.MAX_BLOCKS,
                                       Settings.STACK_ITERATIONS,
                                       Settings.LEARN_BLOCK_MINIMUM,
                                       Settings.SIGMA_ZETA_CUTOFF)]),
        "construction": starmap(
            ConstructionSettings, [(Settings.MAX_CATEGORIES,
                                    Settings.MIN_CATEGORY_SIZE,
                                    Settings.MAX_MODEL_TARGETS,
                                    Settings.MAX_TARGET_ERROR)]),
        "feature_selection": starmap(
            FeatureSelectionSettings, [(Settings.MAX_FEATURES,
                                        Settings.MAX_FILTER_X,
                                        Settings.MAX_FILTER_Y,
                                        Settings.MAX_MODELS_REDUCTION)]),
        "reconstruction": starmap(
            ReconstructionSettings, [(Settings.MIN_TEST_ACCURACY,
                                      Settings.MAX_TEST_ERROR_AVG,
                                      Settings.MAX_TEST_ERROR_MAX,
                                      Settings.RELIABILITY_SAMPLE,
                                      Settings.MIN_RELIABILITY,
                                      Settings.REDUCE_MODEL_REDUNDANCY)]),
        "deconstruction": starmap(
            DeconstructionSettings, [()]
        )
    }

    return next(factory[settings_type])


def read_settings(path: str):
    try:
        config = ConfigParser()
        config.read(path)
        configure_main_settings_class(config)
    except AttributeError as e:
        # TODO (dmt): Implement proper error handling.
        raise Exception("Fehler")


def configure_main_settings_class(config):

    default = config["GENERAL"]
    Settings.INPUT_FILE = default["input_file"]
    Settings.LEARN_DIR = default["learn_dir"]
    Settings.MAX_LEARN_DIR = default["max_learn_dir"]
    Settings.USE_EXISTING_MODELS = default["use_existing_models"]
    Settings.KNOWLEDGE_DIR = default["knowledge_dir"]

    preprocessing = config["PREPROCESSING"]
    Settings.SET_FEATURES = preprocessing["set_features"]
    Settings.SET_TARGETS = preprocessing["set_targets"]
    Settings.SORT_TIME_STAMP = preprocessing["sort_time_stamp"]
    Settings.CUT_TIME_STAMP = preprocessing["cut_time_stamp"]

    block_processing = config["BLOCK_PROCESSING"]
    Settings.BLOCK_SIZE = block_processing["block_size"]
    Settings.MAX_BLOCKS = block_processing["max_blocks"]
    Settings.STACK_ITERATIONS = block_processing["stack_iterations"]
    Settings.LEARN_BLOCK_MINIMUM = block_processing["learn_block_minimum"]
    Settings.SIGMA_ZETA_CUTOFF = block_processing["sigma_zeta_cutoff"]

    construction = config["CONSTRUCTION"]
    Settings.MAX_TARGET_ERROR = construction["max_target_error"]
    Settings.MAX_MODEL_TARGETS = construction["max_model_targets"]
    Settings.MAX_CATEGORIES = construction["max_categories"]
    Settings.MIN_CATEGORY_SIZE = construction["min_category_size"]

    feature_selection = config["FEATURE_SELECTION"]
    Settings.MAX_FEATURES = feature_selection["max_features"]
    Settings.MAX_FILTER_X = feature_selection["max_filter_x"]
    Settings.MAX_FILTER_Y = feature_selection["max_filter_y"]
    Settings.MAX_MODELS_REDUCTION = feature_selection["max_model_reduction"]

    reconstruction = config["RECONSTRUCTION"]
    Settings.MIN_TEST_ACCURACY = reconstruction["min_test_accuracy"]
    Settings.MAX_TEST_ERROR_AVG = reconstruction["max_test_error_avg"]
    Settings.MAX_TEST_ERROR_MAX = reconstruction["max_test_error_max"]
    Settings.RELIABILITY_SAMPLE = reconstruction["reliability_sample"]
    Settings.MIN_RELIABILITY = reconstruction["min_reliability"]
    Settings.REDUCE_MODEL_REDUNDANCY = reconstruction["reduce_model_redundancy"]