import configparser
import json
from abc import ABC
from urllib.parse import quote_plus


class BaseConfig(ABC):

    def __init__(self, config_file='dicebox.config'):
        ###############################################################################
        # Create config objects.
        ###############################################################################
        local_config = configparser.ConfigParser()
        local_config.read(config_file)

        ###############################################################################
        # Data Set Options
        ###############################################################################

        # Load user defined config
        self.DATASET = local_config.get('DATASET', 'name')
        self.DICEBOX_COMPLIANT_DATASET = local_config.getboolean('DATASET', 'dicebox_compliant')
        self.NB_CLASSES = local_config.getint('DATASET', 'categories')
        self.IMAGE_WIDTH = local_config.getint('DATASET', 'image_width')
        self.IMAGE_HEIGHT = local_config.getint('DATASET', 'image_height')
        self.DATA_BASE_DIRECTORY = local_config.get('DIRECTORY', 'dataset_base_directory')

        # Build Calculated Configs
        self.NETWORK_NAME = "%s_%ix%i" % (self.DATASET, self.IMAGE_WIDTH, self.IMAGE_HEIGHT)
        self.INPUT_SHAPE = (self.IMAGE_WIDTH, self.IMAGE_HEIGHT, 3)  # 3 indicates the number of channels (RGB) so 3.
        self.DATA_DIRECTORY = "%s/%s/data/" % (self.DATA_BASE_DIRECTORY, self.NETWORK_NAME)

        ###############################################################################
        # Neural Network Taxonomy Options
        ###############################################################################
        self.MIN_NEURONS = local_config.getint('TAXONOMY', 'min_neurons')
        self.MAX_NEURONS = local_config.getint('TAXONOMY', 'max_neurons')
        self.MIN_LAYERS = local_config.getint('TAXONOMY', 'min_layers')
        self.MAX_LAYERS = local_config.getint('TAXONOMY', 'max_layers')
        self.LAYER_TYPES = local_config.get('TAXONOMY', 'layer_types')
        self.ACTIVATION = local_config.get('TAXONOMY', 'activation')
        self.OPTIMIZER = local_config.get('TAXONOMY', 'optimizer')

        self.TAXONOMY = {
            'min_neurons': self.MIN_NEURONS,
            'max_neurons': self.MAX_NEURONS,
            'min_layers': self.MIN_LAYERS,
            'max_layers': self.MAX_LAYERS,
            'layer_types': json.loads(self.LAYER_TYPES),
            'activation': json.loads(self.ACTIVATION),
            'optimizer': json.loads(self.OPTIMIZER)
        }

        ###############################################################################
        # Evolution Options
        ###############################################################################
        self.EPOCHS = local_config.getint('EVOLUTION', 'epochs')

        # Number of times to evole the population.
        self.GENERATIONS = local_config.getint('EVOLUTION', 'generations')

        # Number of networks in each generation.
        self.POPULATION = local_config.getint('EVOLUTION', 'population')
        self.NOISE = local_config.getfloat('GLOBAL', 'noise')

        ###############################################################################
        # Training Options / Settings for the 1920x1080 dataset
        ###############################################################################
        self.BATCH_SIZE = local_config.getint('TRAINING', 'batch_size')
        self.TRAIN_BATCH_SIZE = local_config.getint('TRAINING', 'train_batch_size')
        self.TEST_BATCH_SIZE = local_config.getint('TRAINING', 'test_batch_size')
        self.LOAD_BEST_WEIGHTS_ON_START = local_config.getboolean('TRAINING', 'load_best_weights_on_start')

        ###############################################################################
        # Direcrtory Options
        ###############################################################################
        self.LOGS_DIR = local_config.get('DIRECTORY', 'logs_dir')
        self.WEIGHTS_DIR = local_config.get('DIRECTORY', 'weights_dir')
        self.TMP_DIR = local_config.get('DIRECTORY', 'tmp_dir')
        self.POPULATION_DIR = local_config.get('DIRECTORY', 'population_dir')

        ###############################################################################
        # Server Options
        ###############################################################################
        self.API_ACCESS_KEY = local_config.get('SERVER', 'api_access_key')
        self.API_VERSION = local_config.get('SERVER', 'api_version')
        self.LISTENING_HOST = local_config.get('SERVER', 'listening_host')
        self.FLASK_DEBUG = local_config.getboolean('SERVER', 'flask_debug')
        self.MODEL_WEIGHTS_FILENAME = local_config.get('SERVER', 'model_weights_filename')

        ###############################################################################
        # Sensory Service Options
        ###############################################################################
        self.SENSORY_SERVER = local_config.get('SENSORY_SERVICE', 'sensory_server')
        self.SENSORY_PORT = local_config.getint('SENSORY_SERVICE', 'sensory_port')
        self.SENSORY_URI = local_config.get('SENSORY_SERVICE', 'sensory_uri')

        self.SENSORY_SERVICE_RABBITMQ_EXCHANGE = local_config.get('SENSORY_SERVICE', 'rabbitmq_exchange')
        self.SENSORY_SERVICE_RABBITMQ_BATCH_REQUEST_ROUTING_KEY = local_config.get(
            'SENSORY_SERVICE', 'rabbitmq_batch_request_routing_key')
        self.SENSORY_SERVICE_RABBITMQ_BATCH_REQUEST_TASK_QUEUE = local_config.get(
            'SENSORY_SERVICE', 'rabbitmq_batch_request_task_queue')

        self.SENSORY_SERVICE_RABBITMQ_URI = local_config.get('SENSORY_SERVICE', 'rabbitmq_uri')
        self.SENSORY_SERVICE_RABBITMQ_USERNAME = local_config.get('SENSORY_SERVICE', 'rabbitmq_username')
        self.SENSORY_SERVICE_RABBITMQ_PASSWORD = local_config.get('SENSORY_SERVICE', 'rabbitmq_password')
        self.SENSORY_SERVICE_RABBITMQ_SERVER = local_config.get('SENSORY_SERVICE', 'rabbitmq_server')
        self.SENSORY_SERVICE_RABBITMQ_PORT = local_config.getint('SENSORY_SERVICE', 'rabbitmq_port')
        self.SENSORY_SERVICE_RABBITMQ_VHOST = quote_plus(
            local_config.get('SENSORY_SERVICE', 'rabbitmq_vhost'))

        self.SENSORY_SERVICE_RABBITMQ_URL = "%s%s:%s@%s:%s/%s" % (
            self.SENSORY_SERVICE_RABBITMQ_URI,
            self.SENSORY_SERVICE_RABBITMQ_USERNAME,
            self.SENSORY_SERVICE_RABBITMQ_PASSWORD,
            self.SENSORY_SERVICE_RABBITMQ_SERVER,
            self.SENSORY_SERVICE_RABBITMQ_PORT,
            self.SENSORY_SERVICE_RABBITMQ_VHOST
        )
        self.SENSORY_SERVICE_SHARD_SIZE = local_config.getint('SENSORY_SERVICE', 'shard_size')

        ###############################################################################
        # Training Service Options
        ###############################################################################
        self.TRAINING_SERVICE_RABBITMQ_EXCHANGE = local_config.get(
            'TRAINING_SERVICE', 'rabbitmq_exchange')
        self.TRAINING_SERVICE_RABBITMQ_TRAINING_REQUEST_ROUTING_KEY = local_config.get(
            'TRAINING_SERVICE', 'rabbitmq_batch_request_routing_key')
        self.TRAINING_SERVICE_RABBITMQ_TRAIN_REQUEST_TASK_QUEUE = local_config.get(
            'TRAINING_SERVICE', 'rabbitmq_train_request_task_queue')
        self.TRAINING_SERVICE_RABBITMQ_RABBITMQ_URI = local_config.get('TRAINING_SERVICE', 'rabbitmq_uri')
        self.TRAINING_SERVICE_RABBITMQ_USERNAME = local_config.get('TRAINING_SERVICE', 'rabbitmq_username')
        self.TRAINING_SERVICE_RABBITMQ_PASSWORD = local_config.get('TRAINING_SERVICE', 'rabbitmq_password')
        self.TRAINING_SERVICE_RABBITMQ_SERVER = local_config.get('TRAINING_SERVICE', 'rabbitmq_server')
        self.TRAINING_SERVICE_RABBITMQ_PORT = local_config.getint('TRAINING_SERVICE', 'rabbitmq_port')
        self.TRAINING_SERVICE_RABBITMQ_VHOST = quote_plus(local_config.get('TRAINING_SERVICE', 'rabbitmq_vhost'))
        self.TRAINING_SERVICE_RABBITMQ_URL = "%s%s:%s@%s:%s/%s" % (
            self.TRAINING_SERVICE_RABBITMQ_RABBITMQ_URI,
            self.TRAINING_SERVICE_RABBITMQ_USERNAME,
            self.TRAINING_SERVICE_RABBITMQ_PASSWORD,
            self.TRAINING_SERVICE_RABBITMQ_SERVER,
            self.TRAINING_SERVICE_RABBITMQ_PORT,
            self.TRAINING_SERVICE_RABBITMQ_VHOST
        )

        ###############################################################################
        # Training Processor Options
        ###############################################################################
        self.TRAINING_PROCESSOR_SERVICE_RABBITMQ_EXCHANGE = local_config.get(
            'TRAINING_PROCESSOR_SERVICE', 'rabbitmq_exchange')
        self.TRAINING_PROCESSOR_SERVICE_RABBITMQ_TRAINING_REQUEST_ROUTING_KEY = local_config.get(
            'TRAINING_PROCESSOR_SERVICE', 'rabbitmq_batch_request_routing_key')
        self.TRAINING_PROCESSOR_SERVICE_RABBITMQ_TRAIN_REQUEST_TASK_QUEUE = local_config.get(
            'TRAINING_PROCESSOR_SERVICE', 'rabbitmq_train_request_task_queue')
        self.TRAINING_PROCESSOR_SERVICE_RABBITMQ_URI = local_config.get(
            'TRAINING_PROCESSOR_SERVICE', 'rabbitmq_uri')
        self.TRAINING_PROCESSOR_SERVICE_RABBITMQ_USERNAME = local_config.get(
            'TRAINING_PROCESSOR_SERVICE', 'rabbitmq_username')
        self.TRAINING_PROCESSOR_SERVICE_RABBITMQ_PASSWORD = local_config.get(
            'TRAINING_PROCESSOR_SERVICE', 'rabbitmq_password')
        self.TRAINING_PROCESSOR_SERVICE_RABBITMQ_SERVER = local_config.get(
            'TRAINING_PROCESSOR_SERVICE', 'rabbitmq_server')
        self.TRAINING_PROCESSOR_SERVICE_RABBITMQ_PORT = local_config.getint(
            'TRAINING_PROCESSOR_SERVICE', 'rabbitmq_port')
        self.TRAINING_PROCESSOR_SERVICE_RABBITMQ_VHOST = quote_plus(
            local_config.get('TRAINING_PROCESSOR_SERVICE', 'rabbitmq_vhost'))
        self.TRAINING_PROCESSOR_SERVICE_RABBITMQ_URL = "%s%s:%s@%s:%s/%s" % (
            self.TRAINING_PROCESSOR_SERVICE_RABBITMQ_URI,
            self.TRAINING_PROCESSOR_SERVICE_RABBITMQ_USERNAME,
            self.TRAINING_PROCESSOR_SERVICE_RABBITMQ_PASSWORD,
            self.TRAINING_PROCESSOR_SERVICE_RABBITMQ_SERVER,
            self.TRAINING_PROCESSOR_SERVICE_RABBITMQ_PORT,
            self.TRAINING_PROCESSOR_SERVICE_RABBITMQ_VHOST
        )

        ###############################################################################
        # Client Options
        ###############################################################################
        self.CLASSIFICATION_SERVER = local_config.get('CLIENT', 'classification_server')
        self.CLASSIFICATION_SERVER_PORT = local_config.getint('CLIENT', 'classification_port')
        self.CLASSIFICATION_SERVER_URI = local_config.get('CLIENT', 'classification_uri')
