import sims4.log
logger = sims4.log.Logger('GSI')
GSI_BASE_VERSION = 1
GSI_ARCHIVE_UID_FIX = 2
LATEST_GSI_SERVER_VERSION = GSI_ARCHIVE_UID_FIX
CLIENT_GSI_BASE_VERSION = 1
CLIENT_GSI_ARCHIVE_UID_FIX = 2

class GsiSchemaVisualizers:
    GRID = 'GridTable'
    BAR = 'BarChart'
    LINE = 'LineGraph'


class GsiFieldVisualizers:
    STRING = 'string'
    FLOAT = 'float'
    INT = 'int'
    TIME = 'time'


class GsiSchema:

    class GsiSchemaCheat:

        def __init__(self, cheat_string, label=None, dbl_click=None, in_menu=False, refresh_view=True):
            output = {'url': cheat_string}
            if label:
                output['label'] = label
            if dbl_click:
                output['dblClick'] = dbl_click
            output['refresh_view'] = refresh_view
            output['inMenu'] = in_menu
            self.output = output
            self.inputs = None

        def __enter__(self):
            return self

        def __exit__(self, exc_type, exc_value, traceback):
            pass

        def add_static_param(self, value):
            self.output['url'] += ' {}'.format(value)

        def add_token_param(self, token_name, dynamic_token_fn=None):
            self.output['url'] += ' {{{0}}}'.format(token_name)
            if dynamic_token_fn is not None:
                self.output[token_name] = []
                if 'dynamic_token' in self.output:
                    logger.error('The GSI currently does not support multiple dynamic tokens for the same cheat because of the menu.')
                self.output['dynamic_token'] = token_name
                self.output[token_name] = dynamic_token_fn

        def add_input_param(self, label, default=None):
            if self.inputs is None:
                self.inputs = []
                self.output['inputs'] = self.inputs
            token_name = 'inputVal{}'.format(len(self.inputs) + 1)
            self.output['url'] += ' {{{0}}}'.format(token_name)
            input_schema = {'name': token_name, 'label': label}
            if default:
                input_schema['default'] = default
            self.inputs.append(input_schema)

    def __init__(self, label=None, sim_specific=False, auto_refresh=True, exclude_from_dump=False):
        self.fields = []
        output = {'definition': self.fields}
        if label:
            output['label'] = label
        self._sim_specific = sim_specific
        if sim_specific is True:
            output['sim_specific'] = sim_specific
        if exclude_from_dump:
            output['exclude_from_dump'] = True
        output['autoRefresh'] = auto_refresh
        self.output = output
        self.cheats = None
        self.view_cheats = None
        self.associations = None

    def copy(self, new_label):
        new_schema = GsiSchema(sim_specific=self._sim_specific)
        new_schema.output = dict(self.output)
        new_schema.output['label'] = new_label
        if self.cheats is not None:
            new_schema.cheats = list(self.cheats)
            new_schema.output['cheats'] = new_schema.cheats
        if self.view_cheats is not None:
            new_schema.view_cheats = list()
            new_schema.output['viewCheats'] = new_schema.view_cheats
        if self.associations is not None:
            new_schema.associations = list(self.associations)
            new_schema.output['associations'] = new_schema.associations
        return new_schema

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        pass

    def add_filter(self, filter_name):
        if 'filters' not in self.output:
            self.output['filters'] = []
        self.output['filters'].append(filter_name)

    def add_field(self, name, label=None, visualizer=GsiFieldVisualizers.STRING, unique_field=False, hidden=False, **kwargs):
        if name == 'id' and unique_field == False:
            logger.error('Keyword (id) is not a valid archive field identifier. This will cause overwriting issues if you have more than one entry with the same id. Schema: {}, Field: {}', self.output['label'], label, owner='rmccord')
        field = {'name': name}
        if unique_field:
            if 'unique_field' not in self.output:
                self.output['unique_field'] = name
            else:
                logger.error('You cannot have more then one field marked as unique in a GSI Schema.')
        if label:
            field['label'] = label
        if visualizer:
            if visualizer == GsiFieldVisualizers.TIME:
                visualizer = GsiFieldVisualizers.INT
                field['is_time'] = True
            field['type'] = visualizer
        if hidden is True:
            field['hidden'] = hidden
        field.update(kwargs)
        self.fields.append(field)

    @property
    def is_sim_specific(self):
        return self._sim_specific

    @property
    def is_graph_schema(self):
        return False

    def add_cheat(self, cheat_string, label=None, dbl_click=None, refresh_view=True):
        new_cheat = self.GsiSchemaCheat(cheat_string, label=label, dbl_click=dbl_click, refresh_view=refresh_view)
        if self.cheats is None:
            self.cheats = []
            self.output['cheats'] = self.cheats
        self.cheats.append(new_cheat.output)
        return new_cheat

    def add_view_cheat(self, cheat_string, label=None, dbl_click=None, in_menu=False, refresh_view=True):
        new_cheat = self.GsiSchemaCheat(cheat_string, label=label, dbl_click=dbl_click, in_menu=in_menu, refresh_view=refresh_view)
        if self.view_cheats is None:
            self.view_cheats = []
            self.output['viewCheats'] = self.view_cheats
        self.view_cheats.append(new_cheat.output)
        return new_cheat

    def add_has_many(self, name, schema_class, *args, **kwargs):
        new_schema = schema_class(*args, **kwargs)
        if self.associations is None:
            self.associations = []
            self.output['associations'] = self.associations
        self.output['associations'].append({'name': name, 'type': 'hasMany', 'schema': new_schema.output})
        return new_schema

    def add_has_one(self, name, schema_class, *args, **kwargs):
        new_schema = schema_class(*args, **kwargs)
        if self.associations is None:
            self.associations = []
            self.output['associations'] = self.associations
        self.output['associations'].append({'name': name, 'type': 'hasOne', 'schema': new_schema.output})
        return new_schema


class GsiGridSchema(GsiSchema):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.output['visualizer'] = GsiSchemaVisualizers.GRID

    def add_field(self, *args, width=None, **kwargs):
        if width:
            kwargs['width'] = width
        super().add_field(*args, **kwargs)


class GsiGraphSchema(GsiSchema):

    class Axis:
        X = 'xField'
        Y = 'yField'

    class AxisType:
        Invalid = 'Invalid'
        Numeric = 'Numeric'
        Category = 'Category'

    def __init__(self, *args, x_min=None, x_max=None, y_min=None, y_max=None, x_axis_label='', y_axis_label='', x_axis_type=AxisType.Category, y_axis_type=AxisType.Numeric, has_legend=True, **kwargs):
        super().__init__(*args, **kwargs)
        if x_min is not None:
            self.output['xMin'] = x_min
        if x_max is not None:
            self.output['xMax'] = x_max
        if y_min is not None:
            self.output['yMin'] = y_min
        if y_max is not None:
            self.output['yMax'] = y_max
        self.output['hasLegend'] = has_legend
        self.output['xAxisLabel'] = x_axis_label
        self.output['yAxisLabel'] = y_axis_label
        self.output['xAxisType'] = x_axis_type
        self.output['yAxisType'] = y_axis_type

    def add_field(self, *args, axis=None, is_percent=None, **kwargs):
        if axis:
            kwargs['axis'] = axis
        if is_percent:
            kwargs['is_percent'] = is_percent
        super().add_field(*args, **kwargs)

    @property
    def is_graph_schema(self):
        return True


class GsiBarChartSchema(GsiGraphSchema):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.output['visualizer'] = GsiSchemaVisualizers.BAR


class GsiLineGraphSchema(GsiGraphSchema):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.output['visualizer'] = GsiSchemaVisualizers.LINE
        self.output['graphType'] = 'line'


class GsiScatterGraphSchema(GsiGraphSchema):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.output['visualizer'] = GsiSchemaVisualizers.LINE
        self.output['graphType'] = 'scatter'


class GSIGlobalCheatSchema(GsiSchema):

    class GsiSchemaCheat(GsiSchema.GsiSchemaCheat):

        def add_token_param(self, token_name):
            raise RuntimeError('Cannot add a token param to a global GSI Cheat')

        def add_input_param(self, label, default=None):
            raise RuntimeError('Cannot add an input param to a global GSI Cheat')

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.output['is_global_cheat'] = True

    def add_field(self, name, label=None, visualizer=GsiFieldVisualizers.STRING, hidden=False, **kwargs):
        raise RuntimeError('Cannot call add_field on a global GSI Cheat')

    def add_view_cheat(self, cheat_string, label=None):
        raise RuntimeError('Cannot call add_view_cheat on a global GSI Cheat')

    def add_has_many(self, name, schema_class, *args, **kwargs):
        raise RuntimeError('Cannot call add_has_many on a global GSI Cheat')

    def add_has_one(self, name, schema_class, *args, **kwargs):
        raise RuntimeError('Cannot call add_has_one on a global GSI Cheat')

