class NeuralEditNeuralNet(PickleToXML):
    __pickle_to_xml__ = ['NetPath', 'Elements']
    # The idea with pickling this is to save a REFERENCE to the Net file
    # and pickle the Net in its own file - the editor handles 
    # saving and restoring the net as well as re-establishing the self.Net property
    # 
    
    def __init__(self):
        '''
        A container for NeuralEditElement representing the GUI components of a neural net
        '''
        self.Net = NeuralNet()
        self.NetPath = None  # set during pickle op
        self.Elements = []   # elements are UI representation of individual neurons
        self.LookupTable = {}

    def to_json(self):
        ''' convert UI Net to json repr
        { 'nodes': { 'node_x' : {'x': float, 'y': float }, ... }, 'edges': [[node_source, node_target], ... ] }
        '''
        json = {'nodes': {}, 'edges': [], 'outputs': [], 'inputs': []}
        for e in self.Elements:
            json['nodes'][e.Name] = {'x': e.Position[0], 'y': e.Position[1]}

            # add edges, outputs, inputs
            neuron = self.lookup_neuron(e)
            if len(neuron.Outgoing) == 0:
                if len(neuron.Incoming) != 0:
                    json['outputs'].append(e.Name)
            else:
                if len(neuron.Incoming) == 0:
                    json['inputs'].append(e.Name)

                # record edges on the outgoing side
                for link in neuron.Outgoing:
                    target = self.lookup_element(link.Target)
                    json['edges'].append([e.Name, target.Name])

            # add outputs

            # add inputs
        return json

    def element_from_point(self, point):
        for e in reversed(self.Elements):  # reverse order of hit test from painting
            if e.hit_test(point):
                return e
        return None

    def rebuild_lookup_table(self):
        self.LookupTable = {}
        for e in self.Elements:
            self.LookupTable[e.Name] = (self.Net.lookup_neuron_by_name(e.Name), e)

    def lookup_neuron(self, element):
        return None if element is None else self.LookupTable[element.Name][0]

    def lookup_element(self, neuron):
        return None if neuron is None else self.LookupTable[neuron.Name][1]

    def add_named_element(self, position, name):
        neuron = self.Net.add_neuron()
        neuron.Name = name
        element = NeuralEditElement(name, position)
        self.Elements.append(element)
        self.LookupTable[name] = (neuron, element)
        return element

    def add_element(self, position, neuron_type, path):
        if path is None:
            neuron = self.Net.add_neuron()
        else:
            neuron = self.Net.add_subnet(neuron_type, path)
            
        element = NeuralEditElement(neuron.Name, position)
        self.Elements.append(element)
        self.LookupTable[neuron.Name] = (neuron, element)
        return element

    def add_link(self, start_element, end_element):
        return self.Net.add_link(self.lookup_neuron(start_element), self.lookup_neuron(end_element))

    def remove_element(self, element):
        (neuron, element) = self.LookupTable[element.Name]
        self.Elements.remove(element)
        self.Net.remove_neuron(neuron)
        self.LookupTable.pop(element.Name)        

    def rename_element(self, element, name):
        (neuron, element) = self.LookupTable[element.Name]
        self.LookupTable.pop(element.Name)
        neuron.Name = ""  # temp to let get_unique_name reuse our name
        name = self.Net.get_unique_name(name)
        neuron.Name = name
        element.Name = name
        self.LookupTable[neuron.Name] = (neuron, element)
        return name  # in case the selected name is different from what was passed in