import OifParseTreeMapping

class InitMethod:
    FORWARD_DECL = 0
    ATTRIBUTE_LIST = 1
    VALUE_LIST = 2
    OBJECT_TAG = 3

class _Empty:
    pass

class OifParser:

    def parse(self, oifString):
        parser = OifParseTreeMapping.OifParseTreeMapping()
        tree = parser.parse(oifString)
        return self.processOifFile(tree[0])

    def processOifFile(self, oifFile):
        return self.processObjectDefList(oifFile[0])

    def processObjectDefList(self, objectDefList):
        objects = [self.processObjectDef(objectDefList[0])]
        if objectDefList[1]:
            # There is another object in the chain
            objects.extend(self.processObjectDefList(objectDefList[1]))
        return objects

    def processObjectDef(self, object_def):
        if object_def[5] or (object_def[2] and not object_def[3]):
            raise NotImplementedError('Physically Near To')
        empty = _Empty()
        empty.object_tag = self.processObjectTag(object_def[0])
        empty.classname = self.processClassName(object_def[1])
        (method, data) = self.processInitialization(object_def[3])
        empty.initialization = data
        empty.method = method
        return empty

    def processObjectTag(self, objectTag):
        return objectTag[0]

    def processClassName(self, className):
        return self.processScopedName(className[0])

    def processScopedName(self, scopedName):
        if not scopedName[1]:
            # Simple Name
            name =  scopedName[0]
        elif not scopedName[2]:
            # Global scoped name
            name =  scopedName[1]
        else:
            # A name in a chain
            first = self.processScopedName(scopedName[0])
            name =  '%s.%s' % (first, scopedName[2])
        return name

    def processInitialization(self, initialization):
        if not initialization:
            return (InitMethod.FORWARD_DECL, None)
        method = getattr(InitMethod, initialization[0]['TYPE'])
        if method == InitMethod.ATTRIBUTE_LIST:
            data = self.processAttributeList(initialization[0])
        elif method == InitMethod.VALUE_LIST:
            data = self.processValueList(initialization[0])
        elif method == InitMethod.OBJECT_TAG:
            data = initialization[0][0]
        else:
            raise RuntimeError('Unknown initialization: %s' %
                               initialization[0]['TYPE'])
        return (method, data)

    def processAttributeList(self, attributeList):
        attributes = [self.processAttribute(attributeList[0])]
        if attributeList[2]:
            # Another attribute in the chain
            attributes.extend(self.processAttributeList(attributeList[2]))
        return attributes

    def processValueList(self, valueList):
        values = [self.processValue(valueList[0])]
        if valueList[2]:
            # Another one in the chain
            values.extend(self.processValueList(valueList[2]))
        return values

    def processAttribute(self, attr):
        # fieldname, value
        return (attr[0][0], self.processValue(attr[1]))

    def processValue(self, value):
        if value[0]['TYPE'] == 'LITERAL':
            return self.processLiteral(value[0])
        elif value[0]['TYPE'] == 'COLLECTION_VALUE':
            return self.processCollectionValue(value[0])
        else:
            raise NotImplementedError(str(value[0]['TYPE']))
                 
    def processLiteral(self, literal):
        if literal[0]['TYPE'] == 'STRING_LITERAL':
            return str(literal[0][0])
        elif literal[0]['TYPE'] == 'FLOAT_LITERAL':
            return float(literal[0][0])
        elif literal[0]['TYPE'] == 'INTEGER_LITERAL':
            # could be decimal, octal or hexadecimal
            try: 
                return eval(literal[0][0]) 
            except OverflowError: 
                return eval(literal[0][0] +'L') 
        elif literal[0]['TYPE'] == 'OBJECT_TAG':
            return literal[0][0]
        else:
            raise NotImplementedError(str(literal[0]['TYPE']))

    def processCollectionValue(self, collectionValue):
        # '{' <value_list> '}'
        return tuple(self.processValueList(collectionValue[1]))
