'''Noesis import plugin. Written by Tsukihime'''

from inc_noesis import *
import noesis
import rapi

_ENDIAN = 0 # 1 for big endian

def registerNoesisTypes():
    '''Register the plugin. Just change the Game name and extension.'''
    
    handle = noesis.register("Kimi ga Yobu", ".lfx")
    noesis.setHandlerTypeCheck(handle, noepyCheckType)
    noesis.setHandlerLoadModel(handle, noepyLoadModel)
    return 1

def noepyCheckType(data):
    '''Verify that the format is supported by this plugin. Default yes'''
    
    if len(data) < 0:
        return 0
    bs = NoeBitStream(data)
    try:
        idstring = noeStrFromBytes(bs.readBytes(3))
        if idstring == "LFX":
            return 1
        return 0
    except:
        return 0

def noepyLoadModel(data, mdlList):
    '''Build the model, set materials, bones, and animations. You do not
    need all of them as long as they are empty lists (they are by default)'''
    
    ctx = rapi.rpgCreateContext()
    parser = SanaeParser(data)
    parser.parse_file()
    mdl = rapi.rpgConstructModel()
    mdl.setModelMaterials(NoeModelMaterials(parser.texList, parser.matList))
    mdl.setBones(parser.boneList)
    mdl.setAnims(parser.animList)
    mdlList.append(mdl)
    return 1

class Mesh(object):
    '''A generic mesh object, for convenience'''
    
    def __init__(self):
        
        self.positions = bytes()
        self.normals = bytes()
        self.uvs = bytes()
        self.numVerts = 0
        self.numIdx = 0
        self.idxBuff = bytes()
        self.matName = ""
        self.meshName = ""

class SanaeParser(object):
    
    def __init__(self, data):    
        '''Initialize some data. Refer to Sanae.py to see what is already
        initialized'''
        
        self.inFile = NoeBitStream(data, _ENDIAN)
        self.animList = []
        self.texList = []
        self.matList = []
        self.boneList = []
        self.meshList = []
        self.filename = rapi.getLocalFileName(rapi.getInputName())
            
    def build_mesh(self, mesh):
        
        for matNum, numFaces, idxBuff in mesh.faceGroups:
            rapi.immBegin(noesis.RPGEO_TRIANGLE)
            #rapi.immBegin(noesis.RPGEO_POINTS)
            for i in range(numFaces):
                numIdx = idxBuff[i*13]
                
                for j in range(numIdx):
                    idx = (13 * i) + (j * 4)
                    normIdx = idxBuff[idx + 3]
                    vertIdx = idxBuff[idx + 1]
                    rapi.immNormal3f(mesh.normBuff, normIdx*12)
                    rapi.immVertex3f(mesh.vertBuff, vertIdx*12)
            rapi.immEnd()
            
    def read_name(self, n):
        
        string = self.inFile.readBytes(n)
        return noeStrFromBytes(string)

    def read_tex_name(self):
        
        data = self.inFile.readBytes(28)
        return noeStrFromBytes(data.split(b"\x00")[0])
    
    def parse_materials(self, numMat):
        
        for i in range(numMat):
            self.inFile.read('19f')
            numTex = self.inFile.readByte()
            matName = self.read_name(40)
            for i in range(numTex):
                texName = self.read_tex_name()
            self.inFile.seek(48, 1)
            
            #print(matName, texName)
        
    def parse_vertices(self, numVerts):
        
        return self.inFile.readBytes(numVerts * 12)
    
    def parse_faces(self, numFaces):
        
        #return self.inFile.readBytes(numFaces * 26)
        return self.inFile.read("%dH" %(numFaces*13))
    
    def parse_face_groups(self, numGroups):

        faceGroups = []
        print(numGroups)
        for i in range(numGroups):
            matNum, unk = self.inFile.read('2H')
            numFaces = self.inFile.readUShort()
            idxBuff = self.parse_faces(numFaces)        
            
            faceGroups.append([matNum, numFaces, idxBuff])
        return faceGroups
    
    def parse_mesh(self):
        '''Can be a mesh or a bone'''

        mesh = Mesh()
        self.inFile.readShort()
        mesh.numVerts = self.inFile.readUShort()
        mesh.vertBuff = self.parse_vertices(mesh.numVerts)

        mesh.numNorms = self.inFile.readUShort()
        mesh.normBuff = self.inFile.readBytes(mesh.numNorms * 12)
        
        mesh.numUV = self.inFile.readUShort()
        mesh.uvBuff = self.inFile.readBytes(mesh.numUV * 8)
    
        unk = self.inFile.readUShort()
        self.inFile.readBytes(unk * 4)
        
        self.inFile.readByte()
        unk2 = self.inFile.readShort()
        self.inFile.readBytes(unk2 * 24)
        
        self.inFile.readShort()
        self.inFile.read('16f')
        self.inFile.seek(58, 1)
        
        print(self.inFile.tell())
        numGroups = self.inFile.readUShort()
        size = self.inFile.readUInt()
        mesh.faceGroups = self.parse_face_groups(numGroups)
        self.build_mesh(mesh)
        self.inFile.read('2H')
            
    def parse_bone(self):
        
        self.inFile.read('5H')
        self.inFile.readByte()
        self.inFile.readShort()
        self.inFile.read('16f')
        self.inFile.read('10f')
        
        count = self.inFile.readUInt()
        self.parse_unk(count)
        
        count2 = self.inFile.readUInt()
        self.parse_unk2(count2)
        
        count3 = self.inFile.readUInt()
        self.parse_unk3(count3)
        
        self.inFile.readUInt()
        unk1 = self.inFile.readShort()
        if unk1:        
            self.inFile.read('16f')
            self.inFile.read('2H')
            count4 = self.inFile.readUShort()
            self.parse_unk4(count4)
        self.inFile.read('4H')
        
    def parse_unk(self, count):
        
        return self.inFile.readBytes(count * 16)
    
    def parse_unk2(self, count):
        
        return self.inFile.readBytes(count * 16)
    
    def parse_unk3(self, count):
        
        return self.inFile.readBytes(count * 20)
    
    def parse_unk4(self, count):
        
        #self.inFile.readUInt()
        #self.inFile.readFloat()
        return self.inFile.readBytes(count * 8)
    
    def parse_object(self):
        '''Determine whether it's a mesh, bone, or something else'''
        
        while 1:
            self.inFile.readShort()
            if self.inFile.tell() == self.inFile.dataSize:
                break
            
            objName = self.read_name(40)
            self.inFile.readShort()
            check = self.inFile.readShort()
            if check:
                print("%s mesh" %objName)
                self.inFile.seek(-4, 1)
                self.parse_mesh()
            else:
                print("%s bone" %objName)
                self.inFile.seek(-2, 1)
                self.parse_bone()
        
    def parse_file(self):
        '''Main parser method'''
        
        idstring = self.inFile.readBytes(3)
        modelType, unk, unk, numMat, unk = self.inFile.readBytes(5)
        
        size = self.inFile.readShort()
        #self.parse_materials(numMat)
        #self.inFile.seek(73, 1)
        self.inFile.seek(size)
        
        if modelType == 0x01:
            self.inFile.seek(51, 1)
            self.parse_mesh()
        elif modelType == 0x81:
            self.inFile.seek(9, 1)
            self.parse_object()
            
        
        
               