#by Durik256
from inc_noesis import *

def registerNoesisTypes():
    handle = noesis.register("Football Superstars", ".cxmodel;.pcccomponent;.pccprototype")
    noesis.setHandlerTypeCheck(handle, CheckType)
    noesis.setHandlerLoadModel(handle, LoadModel)    
    return 1
    
def CheckType(data):
    if data[:9] != b'_GUID_ID_':
        return 0
    return 1
    
def LoadModel(data, mdlList):
    global bs, bones, materials
    bs = NoeBitStream(data)
    ctx = rapi.rpgCreateContext()
    
    #fin all .cxmodel (so as not to parse unnecessary parts of the file...)
    #cxmodels_offsets = [(i-13) for i in findall(b'\xC5\x08\x32\x54', data)]
    cxmodels_offsets = [i for i in findall(b'_GUID_ID_', data)]
    
    bones, materials = [], []
    for ofs in cxmodels_offsets:
        bs.seek(ofs)
        parseCXMODEL()
    
    try:
        mdl = rapi.rpgConstructModel()
    except:
        mdl = NoeModel()
    mdl.setBones(bones)
    mdlList.append(mdl)
    mdl.setModelMaterials(NoeModelMaterials([], materials))
    rapi.setPreviewOption("setAngOfs", "0 90 0")
    return 1
    
def parseCXMODEL():
    bs.seek(9,1)#_GUID_ID_  36478
    bs.seek(bs.readUInt(),1)#ID 16 bytes

    root = readLabel(bs)
    print('root:', root)
    if root != 'Model':
        return 0
    
    global numNode, _exec
    numNode, _exec = bs.readUInt(), ''
    unk0 = bs.readUInt()
    
    for x in range(numNode):
        readNode(bs, x)

    #print(_exec)
    globals_dict = {'readLabel': readLabel, 'bs': bs, 'NoeVec3':NoeVec3}
    exec(_exec, globals_dict)
    #print(globals_dict)
    _mdl = globals_dict['Model']()
    print(bs.tell())
    
    #_mdl = Model()
    CreateMesh(_mdl)   

def findall(p, s):
    i = s.find(p)
    while i != -1:
        yield i
        i = s.find(p, i+1)   

def readLabel(bs):
    lenght = bs.readUInt()
    if lenght:
        lenght += 1
    return noeStrFromBytes(bs.read(lenght))

def readNode(bs, curIndex):
    global numNode, _exec
    #print(curIndex,'Node>>>>>>')
    label = readLabel(bs)
    #print('label:',label)
    _exec += 'class %s:\n'%label#print('class %s:\n'%label)
    _exec += '    def __init__(self):\n'#print('    def __init__(self):\n')
    childCount = bs.readUInt()
    #print('    childCount:', childCount)
    
    for x in range(childCount):
        readChild(bs)
    if curIndex != numNode - 1:
        array = bs.readUInt()
        #print('    array:', array)
    end = bs.readUByte()
    #print('    end:', end)
    _exec += '\n'#print('\n')

def readChild(bs):
    global _exec
    label = readLabel(bs)
    #print('    ChildNode>>>>>>')
    
    #print('        label:', label)
    types = {0:'None', 2:'bs.readUInt()', 3:'bs.readFloat()', 4:'NoeVec3([bs.readFloat(),bs.readFloat(),0])', 5:'NoeVec3.fromBytes(bs.read(12))', 7:'readLabel(bs)',8:'"cxmodel"'}
    type = bs.readUInt()
    #print('        type:', type)
    _value = ''
    if type == 9:
        struct_label = readLabel(bs)
        _value = struct_label + '()'
        #print('            struct_label:', struct_label)
    else:
        _value = types[type]
    
    array = bs.readUInt()
    if array:
        _exec += '        self.num_%s = bs.readUInt()\n'%label#print('        self.num_%s = bs.readUInt()\n'%label)
        _exec += '        self.%s = []\n'%label#print('        self.%s = []\n'%label)
        _exec += '        for x in range(self.num_%s):\n'%label#print('        for x in range(num_%s):\n'%label)
        _exec += '            self.%s.append(%s)\n'%(label, _value)#print('            %s.append(%s)\n'%(label, _value))
    
    else:
        _exec += '        self.%s = %s\n'%(label, _value)#print('        self.%s = %s\n'%(label, _value))
    
    #print('        array:', array)
    end = bs.readUByte()
    #print('        end:', end)
    if end == 0:
        unk = bs.readUInt()
        #print('        unk:', unk)
        if unk == 1:
            unk2 = bs.readUInt()
            #print('        unk2:', unk2)

def CreateMesh(_mdl):
    print('num_materials:',_mdl.num_materials)
    global materials
    for x in _mdl.materials:
        materials.append(NoeMaterial(x, x))
        print('    material:', x)
    
    print('num_bones:',_mdl.num_bones)
    global bones
    if _mdl.num_bones and not bones:
        for x in _mdl.bones:
            name = x.name 
            parent = x.parent
            translation = x.restingTransform.translation
            v = x.restingTransform.orientation.v
            s = x.restingTransform.orientation.s
            
            mat = NoeQuat([v[0], v[1], v[2], s]).toMat43().inverse()
            mat[3] = translation
            
            bones.append(NoeBone(len(bones),name,mat,parent))
        bones = rapi.multiplyBones(bones)
    
    print('>>>patchMeshes:')
    print('corners_num_positions:',_mdl.patchMeshes.corners.num_positions)
    print('corners_num_boneInfluences:', _mdl.patchMeshes.corners.num_boneInfluences)
    print('controls_num_positions:',_mdl.patchMeshes.controls.num_positions)
    print('patchMeshes_num_meshes:',_mdl.patchMeshes.num_meshes)
    
    if _mdl.patchMeshes.corners.num_positions:
        vbuf = b''
        for x in _mdl.patchMeshes.corners.positions:
            vbuf += x.toBytes()
            
        rapi.rpgSetName(_mdl.header.sourceFile)
        rapi.rpgBindPositionBuffer(vbuf, noesis.RPGEODATA_FLOAT, 12)
        
        if _mdl.patchMeshes.corners.num_uvs == _mdl.patchMeshes.corners.num_positions:
            uvbuf = b''
            for x in _mdl.patchMeshes.corners.uvs:
                uvbuf += x.toBytes()
            rapi.rpgBindUV1Buffer(uvbuf, noesis.RPGEODATA_FLOAT, 12)
            
        if _mdl.patchMeshes.corners.num_boneInfluences == _mdl.patchMeshes.corners.num_positions:
            wbuf = b''
            for x in _mdl.patchMeshes.corners.boneInfluences:
                wbuf += noePack('I', x.packedIndices)
                wbuf += noePack('f', x.w0)
                wbuf += noePack('f', x.w1)
                wbuf += noePack('f', x.w2)
            rapi.rpgBindBoneIndexBuffer(wbuf, noesis.RPGEODATA_UBYTE, 16, 4)
            rapi.rpgBindBoneWeightBufferOfs(wbuf, noesis.RPGEODATA_FLOAT, 16, 4, 3)

        
        for i in range(_mdl.patchMeshes.num_meshes):
            print('materialIndex:', _mdl.patchMeshes.meshes[i].materialIndex)
            try:
                mat_name = materials[_mdl.patchMeshes.meshes[i].materialIndex - _mdl.num_materials].name
            except:
                mat_name = 'mat_' + str(_mdl.patchMeshes.meshes[i].materialIndex)
            rapi.rpgSetMaterial(mat_name)
            
            print('    num_tris:', _mdl.patchMeshes.meshes[i].num_tris)
            if _mdl.patchMeshes.meshes[i].num_tris:
                ibuf = b''
                for x in _mdl.patchMeshes.meshes[i].tris:
                    ibuf += noePack('I', x.corner2)
                    ibuf += noePack('I', x.corner1)
                    ibuf += noePack('I', x.corner0)
                
                rapi.rpgCommitTriangles(ibuf, noesis.RPGEODATA_UINT, len(ibuf)//4, noesis.RPGEO_TRIANGLE)

            _mdl.patchMeshes.meshes[i].num_quads
            print('    num_quads:', _mdl.patchMeshes.meshes[i].num_quads)
            if _mdl.patchMeshes.meshes[i].num_quads:
                ibuf = b''
                for x in _mdl.patchMeshes.meshes[i].quads:
                    ibuf += noePack('I', x.corner3)
                    ibuf += noePack('I', x.corner2)
                    ibuf += noePack('I', x.corner1)
                    ibuf += noePack('I', x.corner0)

                rapi.rpgCommitTriangles(ibuf, noesis.RPGEODATA_UINT, len(ibuf)//4, noesis.RPGEO_QUAD_ABC_ACD)
    
    print('>>>polyMeshes:')
    print('num_meshes:', _mdl.polyMeshes.num_meshes)
    print('num_positions:', _mdl.polyMeshes.vertices.num_positions)
    print('num_normals:', _mdl.polyMeshes.num_normals)
    print('num_uvs:', _mdl.polyMeshes.vertices.num_uvs)
    print('num_boneInfluences:', _mdl.polyMeshes.vertices.num_boneInfluences)
    
    
    if _mdl.polyMeshes.vertices.positions:
        vbuf = b''
        for x in _mdl.polyMeshes.vertices.positions:
            vbuf += x.toBytes()
        
        rapi.rpgSetName(_mdl.header.sourceFile)
        rapi.rpgBindPositionBuffer(vbuf, noesis.RPGEODATA_FLOAT, 12)
        
        #if _mdl.polyMeshes.num_normals == _mdl.polyMeshes.vertices.num_positions:
        #    nbuf = b''
        #    for x in _mdl.polyMeshes.normals:
        #        nbuf += x.toBytes()
        #    rapi.rpgBindNormalBuffer(nbuf, noesis.RPGEODATA_FLOAT, 12)
            
        if _mdl.polyMeshes.vertices.num_uvs == _mdl.polyMeshes.vertices.num_positions:
            uvbuf = b''
            for x in _mdl.polyMeshes.vertices.uvs:
                uvbuf += x.toBytes()
            rapi.rpgBindUV1Buffer(uvbuf, noesis.RPGEODATA_FLOAT, 12)
        
        if _mdl.polyMeshes.vertices.num_boneInfluences == _mdl.polyMeshes.vertices.num_positions:
            wbuf = b''
            for x in _mdl.polyMeshes.vertices.boneInfluences:
                wbuf += noePack('I', x.packedIndices)
                wbuf += noePack('f', x.w0)
                wbuf += noePack('f', x.w1)
                wbuf += noePack('f', x.w2)
            rapi.rpgBindBoneIndexBuffer(wbuf, noesis.RPGEODATA_UBYTE, 16, 4)
            rapi.rpgBindBoneWeightBufferOfs(wbuf, noesis.RPGEODATA_FLOAT, 16, 4, 3)
        
        for i in range(_mdl.polyMeshes.num_meshes):
            print('materialIndex:', _mdl.polyMeshes.meshes[i].materialIndex)
            print('num_faces:', _mdl.polyMeshes.meshes[i].num_faces)
        
            ibuf = b''
            for x in _mdl.polyMeshes.meshes[i].faces:
                ibuf += noePack('I', x.vertexIndex2)
                ibuf += noePack('I', x.vertexIndex1)
                ibuf += noePack('I', x.vertexIndex0)
            
            try:
                mat_name = materials[_mdl.polyMeshes.meshes[i].materialIndex - _mdl.num_materials].name
            except:
                mat_name = 'mat_' + str(_mdl.polyMeshes.meshes[i].materialIndex)
            rapi.rpgSetMaterial(mat_name)
            #rapi.rpgSetMaterial('mat_%i'%_mdl.polyMeshes.meshes[i].materialIndex)
            rapi.rpgCommitTriangles(ibuf, noesis.RPGEODATA_UINT, len(ibuf)//4, noesis.RPGEO_TRIANGLE)