from inc_noesis import *
import noesis
import rapi

def registerNoesisTypes():
    '''Register the plugin. Just change the Game name and extension.'''
    
    handle = noesis.register("Shaiya Online", ".3DO;.3DC;.SMOD")
    noesis.setHandlerTypeCheck(handle, noepyCheckType)
    noesis.setHandlerLoadModel(handle, noepyLoadModel)
    return 1

def noepyCheckType(data):
    '''Verify that the format is supported by this plugin. Default yes'''
    
    filename = rapi.getLocalFileName(rapi.getInputName())
    if not ( rapi.checkFileExt(filename, '.3DO') or \
       rapi.checkFileExt(filename, '.3DC') or \
       rapi.checkFileExt(filename, '.SMOD')):
        return 0
    return 1

def get_type(data):
    
    filename = rapi.getLocalFileName(rapi.getInputName())
    if rapi.checkFileExt(filename, '.3DO'):
        return Shaiya3DO(data)
    elif rapi.checkFileExt(filename, '.3DC'):
        return Shaiya3DC(data)
    elif rapi.checkFileExt(filename, '.SMOD'):
        return ShaiyaSMOD(data)

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 = get_type(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

def build_mesh(meshList):
    
    for mesh in meshList:
        
        if mesh.vertSize == 32:
            rapi.rpgBindPositionBufferOfs(mesh.vertBuff, noesis.RPGEODATA_FLOAT, 32, 0)
            rapi.rpgBindNormalBufferOfs(mesh.vertBuff, noesis.RPGEODATA_FLOAT, 32, 12)
            rapi.rpgBindUV1BufferOfs(mesh.vertBuff, noesis.RPGEODATA_FLOAT, 32, 24)
        elif mesh.vertSize == 36:
            rapi.rpgBindPositionBufferOfs(mesh.vertBuff, noesis.RPGEODATA_FLOAT, 36, 0)
            rapi.rpgBindNormalBufferOfs(mesh.vertBuff, noesis.RPGEODATA_FLOAT, 36, 12)
            rapi.rpgBindUV1BufferOfs(mesh.vertBuff, noesis.RPGEODATA_FLOAT, 36, 28)              
        
        rapi.rpgSetMaterial(mesh.matName)
        rapi.rpgCommitTriangles(mesh.idxBuff, noesis.RPGEODATA_USHORT, mesh.numIdx, noesis.RPGEO_TRIANGLE, 1)

class Mesh(object):
    
    def __init__(self):
        
        self.numVerts = 0
        self.numIdx = 0
        self.matName = ""
        self.vertBuff = bytes()
        self.idxBuff = bytes()
        self.vertSize = 0

class Shaiya3DO(object):
    
    def __init__(self, data):    
        '''Initialize some data. Refer to Sanae.py to see what is already
        initialized'''
        
        self.inFile = NoeBitStream(data)
        self.animList = []
        self.texList = []
        self.matList = []
        self.boneList = []
        self.meshList = []
        self.basename = rapi.getExtensionlessName(rapi.getInputName())
        self.matName = "material"
            
    def read_name(self):
        
        string = self.inFile.readBytes(self.inFile.readUInt())
        try:
            return noeStrFromBytes(string)
        except:
            return string
        
    def parse_vertices(self, numVerts):
        
        return self.inFile.readBytes(numVerts*32)
        
    def parse_faces(self, numIdx):
        
        return self.inFile.readBytes(2 * numIdx)
    
    def parse_material(self, texName=""):
            
        if not texName:
            texName = self.basename + ".dds"
        material = NoeMaterial(self.matName, texName)
        self.matList.append(material)
        
    def parse_mesh(self, mesh):
        
        mesh.vertSize = 32
        mesh.numVerts = self.inFile.readUInt()
        mesh.vertBuff = self.parse_vertices(mesh.numVerts)
        mesh.numIdx = self.inFile.readUInt() * 3
        mesh.idxBuff = self.parse_faces(mesh.numIdx)
        
        
    def parse_file(self):
        '''Main parser method'''
        
        mesh = Mesh()
        texName = self.read_name()
        self.parse_mesh(mesh)
        self.parse_material()
        mesh.matName = self.matName
        self.meshList.append(mesh)
        
        build_mesh(self.meshList)
        
class Shaiya3DC(object):
    
    def __init__(self, data):    
        '''Initialize some data. Refer to Sanae.py to see what is already
        initialized'''
        
        self.inFile = NoeBitStream(data)
        self.animList = []
        self.texList = []
        self.matList = []
        self.boneList = []
        self.meshList = []
        self.basename = rapi.getExtensionlessName(rapi.getInputName())
        self.matName = "material"
        
    def parse_bones(self, numBones):
        
        for i in range(numBones):
            matrix = self.inFile.read('16f')
    
    def parse_vertices(self, numVerts):

        vertBuff = self.inFile.readBytes(numVerts*40)
        rapi.rpgBindPositionBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 40, 0)
        rapi.rpgBindNormalBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 40, 20)
        rapi.rpgBindUV1BufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 40, 32)
    
    def parse_faces(self, numIdx):
        
        idxBuff = self.inFile.readBytes(numIdx*2)
        return idxBuff
    
    def parse_material(self):
        
        texName = self.basename + ".dds"
        material = NoeMaterial(self.matName, texName)
        self.matList.append(material)
        
    def parse_file(self):
        '''Main parser method'''
        
        self.inFile.readUInt()
        try:
            numBones = self.inFile.readUInt()
            self.parse_bones(numBones)
            numVerts = self.inFile.readUInt()
            self.parse_vertices(numVerts)
            numIdx = self.inFile.readUInt()*3
            idxBuff = self.parse_faces(numIdx)
            self.parse_material()
            rapi.rpgSetMaterial(self.matName)
            rapi.rpgCommitTriangles(idxBuff, noesis.RPGEODATA_USHORT, numIdx, noesis.RPGEO_TRIANGLE, 1)
        except:
            self.inFile.seek(4)
            numVerts = self.inFile.readUInt()
            self.parse_vertices(numVerts)
            numIdx = self.inFile.readUInt()*3
            idxBuff = self.parse_faces(numIdx)
            self.parse_material()
            rapi.rpgSetMaterial(self.matName)
            rapi.rpgCommitTriangles(idxBuff, noesis.RPGEODATA_USHORT, numIdx, noesis.RPGEO_TRIANGLE, 1) 
            
class ShaiyaSMOD(object):
    
    def __init__(self, data):    
        '''Initialize some data. Refer to Sanae.py to see what is already
        initialized'''
        
        self.inFile = NoeBitStream(data)
        self.animList = []
        self.texList = []
        self.matList = []
        self.boneList = []
        self.meshList = []
        self.basename = rapi.getExtensionlessName(rapi.getInputName())
        
    def read_name(self):
        
        string = self.inFile.readBytes(self.inFile.readUInt())
        try:
            return noeStrFromBytes(string)
        except:
            return string
    
    def parse_vertices(self, numVerts):
        
        return self.inFile.readBytes(numVerts*36)
        
    def parse_faces(self, numIdx):
        
        return self.inFile.readBytes(numIdx*2)
        
    def parse_mesh(self, numMesh):
                
        for i in range(numMesh):
            mesh = Mesh()
            mesh.vertSize = 36
            mesh.matName = "material[%d]" %i
            mesh.texName = self.read_name()
            mesh.numVerts = self.inFile.readUInt()
            mesh.vertBuff = self.parse_vertices(mesh.numVerts)
            mesh.numIdx = self.inFile.readUInt() * 3
            mesh.idxBuff = self.parse_faces(mesh.numIdx)
            
            self.meshList.append(mesh)
            material = NoeMaterial(mesh.matName, mesh.texName)
            self.matList.append(material)
        
    def parse_file(self):
        '''Main parser method'''
        
        self.inFile.seek(40, 1)
        numMesh = self.inFile.readUInt()
        self.parse_mesh(numMesh)
        
        bboxMin = self.inFile.read('3f')
        bboxMax = self.inFile.read('3f')
        
        build_mesh(self.meshList)