from gk_global import *
from gk_property_descriptor import *
from gk_property_restrictions import *
from gk_property_processors import *


class meta_gds_layer_creator(type):
    # Called when a new object is created
    def __call__(cls, *params, **keyword_params):
        # extract layer number
        if 'number' in keyword_params:
            number = keyword_params['number']
        elif len(params) >= 1:
            number = params[0]
        else:
            number = 0
            #raise AttributeError("wrong parameters passed to gds_layer(). Cannot extract layer number. \n" + str(params))
        
        # check is there is a layerlist passed.
        # otherwise, use current layerlist
        layerlist = get_current_layerlist()
        if 'layerlist' in keyword_params:
            layerlist = keyword_params('layerlist')
        else:
            for p in reversed(params):
                if isinstance(p, gds_layerlist):
                    layerlist = p
                    break

        # extract the name of the new structure based on the arguments of
        # the constructor. For default structures, the name is passed as the first argument
        layer = layerlist.__fast_get_layer__(number)
        if layer == None:
            L = type.__call__(cls, *params, **keyword_params)
            list.append(layerlist, L)
            return L
        else:
            # return the existing object
            return layer

class gds_layer(object):
    __metaclass__ = meta_gds_layer_creator
    number = int_property("__number", required = True)
    name = string_property("__name", required = True)
    def __init__(self, number = 0, name=None, layerlist = None):
        self.number = number
        if name == None:
            self.name = "layer " + str(self.number)
        else:
            self.name = name
    # add visualisation information for later use in SVG output or display

    def __str__(self):
        return str(self.number)
    
    def __repr__(self):
        return "<Layer %d>" %self.number

def layer_property(internal_member_name, **kwargs):
    return restricted_property_descriptor(internal_member_name, restriction = restrict_type(gds_layer), preprocess = processor_typecast(gds_layer))


class gds_layerlist(list):
    def __init__(self, layers = []):
        if isinstance(layers, gds_layer):
            list.__init__(self, [layers])
        elif isinstance(layers, gds_layerlist):
            list.__init__(self, layers)
        elif isinstance(layers, list):
            list.__init__(self, [])
            for s in layers:
                self.add(s) #eliminates layers with duplicate number

    # overload acces routines to get dictionary behaviour but without
    # using the name as primary key
    def __getitem__(self, key):
        if isinstance(key, int):
            for i in self:
                if i.number == key: return i
            raise IndexError("layer " + str(key) + " cannot be found in gds_layerlist.")
        elif isinstance(key, str):
            for i in self:
                if i.name == key: return i
            raise IndexError("layer " + str(key) + " cannot be found in gds_layerlist.")
        else:
            raise TypeError("Index is wrong type " + str(type(key)) + " in gds_layerlist")
    
    def __setitem__(self, key, value):
        if isinstance(key, int):
            for i in range(0, len(self)):
                if self[i].number == key: return list.__setitem__(self, i, value)
            list.append(self, value)
        elif isinstance(key, str):
            for i in range(0, len(self)):
                if self[i].name == key: return list.__setitem__(self, i, value)
            list.append(self, value)
        else:
            raise TypeError("Index is wrong type " + str(type(key)) + " in gds_layerlist")

    def __delitem__(self, key):
        if isinstance(key, int):
            for i in range(0, len(self)):
                if list.__getitem__(self,i).number == key: return list.__delitem__(self,i)
                return
            return list.__delitem__(self,key)
        if isinstance(key, str):
            for i in range(0, len(self)):
                if list.__getitem__(self,i).name == key: return list.__delitem__(self,i)
                return
            return list.__delitem__(self,key)
        else:
            raise TypeError("Index is wrong type " + str(type(key)) + " in gds_layerlist")

    def __contains__(self, item):
        if isinstance(item, gds_layer):
            number = item.number
        elif isinstance(item, int):
            number = item
        elif isinstance(item, str):
            for i in self:
                if i.name == name: return True
            return False

        if isinstance(number, int):
            for i in self:
                if i.number == number: return True
            return False

    def __fast_get_layer__(self, number):
        for L in self:
            if L.number == number: return L
        return None

    def index(self, item):
        if isinstance(item, gds_layer):
            number = item.number
        elif isinstance(item, int):
            number = item

        if isinstance(number, int):
            for i in range(0, len(self)):
                if list.__getitem__(self, i).number == number:
                    return i
            raise ValueError("layer " + number + " is not in gds_layerlist")
        if isinstance(item, str):
            for i in range(0, len(self)):
                if list.__getitem__(self, i).name == item:
                    return i
            raise ValueError("layer " + item + " is not in gds_layerlist")
        else:
            raise ValueError("layer " + item + " is not in gds_layerlist")

    def add(self, item, overwrite = False):
        if isinstance(item, gds_layer):
            if not item in self:
                list.append(self,item)
            elif overwrite:
                self[item.number] = item
                return
        elif isinstance(item, gds_layerlist):
            for s in item:
                self.add(s, overwrite)
        elif isinstance(item, int):
            if overwrite or (not item in self):
                self.add(gds_layer(item), overwrite)
        else:
            print >> stderr, "Wrong type of data to append to gds_layerlist"
            raise TypeError

    def append(self, other, overwrite = False):
        return self.add(other, overwrite)

    def extend(self, other, overwrite = False):
        return self.add(other, overwrite)

    def __add__(self, other):
        L = gds_layerlist(self)
        if isinstance(other, gds_layer) or isinstance(other, int):
            L.add(other)
            return L
        else:
            return super(list,L).__add__(other)

    def __radd__(self, other):
        if isinstance(other, gds_layer) or isinstance(other, list) or isinstance(other, int):
            L = gds_layerlist(other)
            L.add(self)
            return L
        else:
            return list__radd__(self, other)

    def __iadd__(self, other):
        if isinstance(other, gds_layer) or isinstance(other, list) or isinstance(other, int):
            self.add(other)
            return self
        else:
            return list.__iadd__(self, other)

    def clear(self):
        del self[:]
