import os



##############################################################################################################
#
# NOTE !!!
#
# THIS FILE MAY BE AUTOGENERATED.
# ONLY MODIFY THE ORIGINAL FILE IN THE /sharedData/ FOLDER!
# OTHER INSTANCES OF THIS FILE ARE COPIES!
#
# NOTE !
#
##############################################################################################################


class Identifier:
    """
    a token by which objects of different types can be compared.
    All objects that are read from the input have an identifier.
    All objects generated during the output have a preliminary identifier, because the output will be interpreted by the Execution Environment.
    (preliminary identifiers can be used like any other).
    An identifier may optionally also have a name.
    Identifiers provided by the server will have this field set if the Identifier refers to an object with a name.
    This fields is optional and not used for identification purposes.
    """
    def __init__(self, identifier, type, preliminary=False, name=None):
        self.id = identifier
        self.type = type
        self.preliminary = preliminary
        self.name = name
    def __str__(self):
        return "%s Identifier %s%s" % (self.type, self.id, " (preliminary)" if self.preliminry else "")
    def __hash__(self):
        """
        override the default hashing, to make equality comparisons possible
        """
        res = hash(self.type)
        res *= 17
        res += hash(self.id)
        res *= 17
        res += hash(self.preliminary)
        return res
    def __eq__(self, other):
        """
        Override the default Equals behavior
        """
        return (self.type == other.type) and (self.id == other.id) and (self.preliminary == other.preliminary)
    def __ne__(self, other):
        """
        Define a non-equality test
        """
        return not self.__eq__(other)
    def to_json(self):
        """
        gives a JSON dictionary representation of this identifier.
        Counterpart to parse_identifier().
        """
        res = { 'type' : self.type, 'id' : self.id }
        if self.preliminary:
            res['preliminary'] = True
        if self.name:
            res['name'] = self.name
        return res


def parse_identifier(dictionary):
        """
        creates an Identifier from a JSON dictionary structure
        Counterpart to Identifier.to_json().
        """
        id = dictionary['id']
        type = dictionary['type']
        preliminary = dictionary.get('preliminary', False)
        name = dictionary.get('name', None)
        return Identifier(id, type, preliminary=preliminary, name=name)


_preliminary_dentifier_counter = 0
def create_preliminary_identifier(type):
    """
    creates a preliminary Identifier, for use by output objects.
    """
    global _preliminary_dentifier_counter
    res = Identifier(_preliminary_dentifier_counter, type, preliminary=True)
    _preliminary_dentifier_counter += 1
    return res


class FileObject:
    """
    represents a file that was given as an input argument, along with information about it.
    """
    def __init__(self, identifier, file_name, creation_step, creation_index, usage_index=None):
        self.identifier = identifier
        self.file_name = file_name
        self.creation_step = creation_step
        self.creation_index = creation_index
        self.usage_index = usage_index # this is None for newly created output files, and an index for input files
    def __str__(self):
        return self.identifier
    def _change_file_name_for_use_as_input(self, index):
        """
        replaces the name of the file with a name that is used for input files.
        This method should be used by the ExecutionEnvironment after copying a file to use it as input for another program execution.
        """
        self.file_name = "in_%d" % index
    def to_json(self):
        """
        gives a JSON dictionary representation of this FileObject that can be parsed as a new FileObject.
        this is a counterpart to parse_file_object().
        """
        res = {}
        res['file'] =  self.file_name
        res['identifier'] = self.identifier.to_json()
        res['creation_step'] = self.creation_step
        res['creation_index'] = self.creation_index
        return res


def parse_file_object(dictionary, usage_index=None):
    """
    creates a FileObject from a JSON dictionary structure
    Counterpart to FileObject.to_json()
    """
    identifier = parse_identifier(dictionary['identifier'])
    file_name = dictionary['file']
    creation_step = dictionary['creation_step']
    creation_index = dictionary['creation_index']
    res = FileObject(identifier, file_name, creation_step, creation_index, usage_index)
    return res


class Tag:
    """
    a Tag consists of a Symbol, defined here as a string, and a number of identifiers.
    It also has an identifier of its own.
    """
    def __init__(self, own_identifier, symbol, *arguments):
        if not isinstance(own_identifier, Identifier):
            raise ValueError("a Tag must get an Identifier as its identifier")
        if not isinstance(symbol, str):
            raise ValueError("the Symbol of a Tag must be given as a string")
        arguments = [self._get_identifier_of_argument(a) for a in arguments]
        self.identifier = own_identifier
        self.symbol = symbol
        self.arguments = arguments
    def __str__(self):
        return "Tag %s: symbol=%s, arguments=(%s)" % (self.identifier, self.symbol, ', '.join(['%s' % a for a in identifiers]))
    def to_json(self):
        """
        gives a JSON dictionary representation of this Tag.
        Counterpart to parse_tag().
        """
        res = {'identifier' : self.identifier.to_json(), 'symbol' : self.symbol, 'args' : [self._get_identifier_of_argument(a).to_json() for a in self.arguments]}
        return res
    def _get_identifier_of_argument(self, arg):
        if isinstance(arg, Identifier):
            return arg
        if hasattr(arg, 'identifier') and isinstance(arg.identifier, Identifier):
            return arg.identifier
        raise ValueError("each argument of a Tag must either be an Identifier or have an identifier field of that type")


def parse_tag(dictionary):
    """
    creates a Tag from a JSON dictionary structure
    Counterpart to Tag.to_json().
    """
    identifier = parse_identifier(dictionary['identifier'])
    symbol = dictionary['symbol']
    args = [parse_identifier(a) for a in dictionary['args']]
    return Tag(identifier, symbol, *args)


class EventRequest:
    """
    represents a request of this program to the Execution Environment.
    """
    def __init__(self, request_type):
        self.request_type = request_type
    def to_json(self):
        """
        gives a JSON dictionary representation of this EventRequest.
        """
        res = {
            'event_type' : self.request_type,
            'args' : self._args_to_json(),
        }
        return res


class ProgramExecutionRequest(EventRequest):
    """
    a request to execute a Program.
    This expects a program as its first argument, and files to use as parameters as the rest.
    """
    def __init__(self):
        super().__init__('execute_program')
        self.program_identifier = None
        self.argument_identifiers = []
    def program(self, program_identifier):
        if not isinstance(program_identifier, str) and not isinstance(program_identifier, int):
            error_message = """The program may be identified as a String displaying the name of the program (in which case the latest version is picked),
                as a String of <name>#<version> (which identifies the version directly),
                or as an integer that is the program's ID (which is unambiguous and includes the version)"""
            raise ValueError(error_message)
        self.program_identifier = program_identifier
        return self
    def arguments(self, argument_identifiers):
        for arg in argument_identifiers:
            if not isinstance(arg, str) and not isinstance(arg, Identifier):
                raise ValueError("each argument of a program execution request must be a string or an Identifier")
        self.argument_identifiers = argument_identifiers
        return self
    def _args_to_json(self):
        res = {
            'program_identifier' : self.program_identifier,
            'argument_identifiers' : [a.to_json() for a in self.argument_identifiers],
        }
        return res


class DisplayMessageRequest(EventRequest):
    """
    a request to display a message to the user in an interactive website.
    The message generated here will be put in a HTML form and displayed.
    """
    def __init__(self):
        super().__init__('display_message')
        self.message_text = None
        self.add_feedback_buttons = False
    def text(self, message_text):
        if not isinstance(message_text, str):
            raise ValueError("The message to display must be a string")
        self.message_text = message_text
        return self
    def feedback_buttons(self, add=True):
        self.add_feedback_buttons = add
        return self
    def _args_to_json(self):
        res = {
            'message_text' : self.message_text,
            'add_feedback_buttons' : self.add_feedback_buttons
        }
        return res


