# file: spec.py
# Copyright (C) 2005,2006,2007,2008 Evil Mr Henry, Phil Bordelon, Brian Reid,
#                        and FunnyMan3595
# This file is part of Endgame: Singularity.

# Endgame: Singularity is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

# Endgame: Singularity is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with Endgame: Singularity; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

# This file contains class to handle basic functionality of Spec's

import inspect


UNSET = object()


class InvalidDataEntryError(RuntimeError):
    pass


def promote_to_list(value):
    if not isinstance(value, list):
        return [value]
    return value


def validate_must_be_list(value):
    if type(value) != list:  # pragma: no cover
        raise TypeError(
            "Must be list, got type %s (value: %s)" % (type(value), repr(value))
        )


class SpecDataField(object):
    def __init__(
        self,
        field_name,
        data_field_name=None,
        mandatory=None,
        converter=None,
        validator=None,
        default_value=UNSET,
    ):
        self.field_name = field_name
        self.data_field_name = (
            data_field_name if data_field_name is not None else field_name
        )
        self.mandatory = mandatory
        self.default_value = default_value
        if self.mandatory is None:
            self.mandatory = True if default_value is UNSET else False
        self.converter = converter
        self.validator = validator
        assert (
            self.mandatory or default_value is not UNSET
        ), "%s (field: %s) must either be mandatory or have a " "default_value" % (
            self.__class__.__name__,
            field_name,
        )
        assert (
            not converter or not validator
        ), "%s (field: %s) at can most have one of converter or validator" % (
            self.__class__.__name__,
            field_name,
        )

    def parse_data_field(self, raw_data_set, data_reference):
        try:
            value = raw_data_set[self.data_field_name]
        except KeyError:
            if self.mandatory:  # pragma: no cover
                raise InvalidDataEntryError(
                    "Missing mandatory data field %s (field name: %s) for %s"
                    % (self.data_field_name, self.field_name, data_reference)
                )
            if callable(self.default_value):
                return self.default_value()
            return self.default_value

        if self.validator:
            self.validator(value)
        if self.converter:
            value = self.converter(value)
        return value


def spec_field_effect(mandatory=True):
    return SpecDataField(
        "effect_data",
        data_field_name="effect",
        mandatory=mandatory,
        validator=list,
        default_value=list,
    )


class GenericSpec(object):
    spec_data_fields = None

    def __init__(self, id):
        self.id = id
        assert self.__class__.spec_data_fields is not None, (
            "Class %s is missing spec_data_field" % self.__class__.__name__
        )

    @classmethod
    def create_from_data_file(cls, data_id, spec_data):
        named_fields = {
            f.field_name: f.parse_data_field(spec_data, data_id)
            for f in cls.spec_data_fields
        }
        named_fields["id"] = data_id
        # Use reflection to call the constructor with the arguments
        # properly aligned
        try:
            getfullargspec = inspect.getfullargspec
        except AttributeError:
            getfullargspec = inspect.getargspec
        arg_desc = getfullargspec(cls.__init__)
        args = [named_fields[name] for name in arg_desc.args[1:]]
        return cls(*args)
