from inc_noesis import *

def registerNoesisTypes():
	handle = noesis.register("Folklore", ".bi5_")
	noesis.setHandlerTypeCheck(handle, flCheckType)
	noesis.setHandlerLoadModel(handle, flLoadModel)
	#noesis.logPopup()
	return 1


def flCheckType(data):
	bs = NoeBitStream(data)
	return 1


class folkloreFile: 
         
	def __init__(self, bs):
		self.bs = bs
		rapi.rpgSetOption(noesis.RPGOPT_BIGENDIAN, 1)
		self.texList  = []
		self.matList  = [] 
		self.boneList = []
		self.boneMap  = []
		self.offsetList = []
		self.meshOffsets = []

	def loadAll(self, bs):
		fileSize, fileCount, tableOffset, nameOffset = bs.read(">" + 4 * "i")
		bs.seek(tableOffset, NOESEEK_ABS)
		bi5Off = bs.read(">" + (fileCount + 1) * "i")
		bs.seek(nameOffset, NOESEEK_ABS)
		for i in range(0, fileCount):
			fileName = bs.readBytes(0x18).decode("ASCII").rstrip("\0")
			if (bi5Off[i + 1] - bi5Off[i]) != 0:
				self.offsetList.append([fileName, bi5Off[i], (bi5Off[i + 1] - bi5Off[i])])
		for i in range(0, len(self.offsetList)):
			bs.seek(self.offsetList[i][1], NOESEEK_ABS)
			chunkType = bs.readUInt()
			if chunkType in flObjectLoaderDict:
				flObjectLoaderDict[chunkType](self, bs, i)
			else:
				#print("NO :", chunkType)
				self.loadTXT(bs, self.offsetList[i])

	def loadAM2(self, bs, fname):
		am2Header = bs.read(">" + "i2H5i")
		bs.seek(am2Header[7] - 32, NOESEEK_REL)
		boneParenting = []
		for i in range(0, am2Header[1]):
			null, id = bs.read(">" + 2 * "B")
			bs.seek(0x16, NOESEEK_REL)
			boneParenting.append(bs.read(">" + 2 * "h"))
			bs.seek(4, NOESEEK_REL)
			BonePos = NoeVec3.fromBytes(bs.readBytes(12), NOE_BIGENDIAN)
			boneMtx = NoeQuat([0.0, 0.0, 0.0, 1.0]).toMat43()
			boneMtx[3] = BonePos
			bs.seek(0x24, NOESEEK_REL)
			newBone = NoeBone(id, (self.offsetList[fname][0] + "_bone_" + str(id)), boneMtx, None, -1)
			self.boneList.append(newBone)
		for i in range(0, am2Header[1]):
			p = self.boneList[i]
			cidx = boneParenting[i][0]
			while cidx != -1:
				b = self.boneList[cidx]
				b.parentIndex = p.index
				cidx = boneParenting[cidx][1]
		self.boneList = rapi.multiplyBones(self.boneList)

	def loadCMD(self, bs, fname):
		cmdHeader = bs.read(">" + "i2H5i")
		meshInfo = []
		for i in range(0, cmdHeader[1]):
			bs.seek(0x7C, NOESEEK_REL)
			meshInfo.append(bs.read(">" + "7i"))
			bs.seek(0x18, NOESEEK_REL)
		self.loadMaterial(bs, cmdHeader, meshInfo, fname)
		self.loadBonePallet(bs, cmdHeader, meshInfo, fname)
		self.loadMesh(bs, cmdHeader, meshInfo, fname)

	def loadBonePallet(self, bs, cmdHeader, meshInfo, fname):
		for i in range(0, cmdHeader[1]):
			#print(meshInfo[i])
			bs.seek(self.offsetList[fname][1] + meshInfo[i][6], NOESEEK_ABS)
			#print(bs.tell())
			pallet = []
			for a in range(0, 0x100):
				bid = bs.readUByte()
				if bid != 255:
					pallet.append(bid)
			self.boneMap.append(pallet)

	def loadMesh(self, bs, cmdHeader, meshInfo, fname):
		for i in range(0, cmdHeader[1]):
			bs.seek(self.offsetList[fname][1] + meshInfo[i][3], NOESEEK_ABS)
			faceBuff = bs.readBytes(4 * meshInfo[i][1])
			bs.seek(self.offsetList[fname][1] + meshInfo[i][4], NOESEEK_ABS)
			vertBuff = bs.readBytes(0x40 * meshInfo[i][2])
			if meshInfo[i][0] == 0:
				pass
			else:
				rapi.rpgSetBoneMap(self.boneMap[i])
				rapi.rpgSetMaterial(self.matList[i].name)
				rapi.rpgBindPositionBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 0x40, 0)
				rapi.rpgBindNormalBufferOfs(vertBuff, noesis.RPGEODATA_HALFFLOAT, 0x40, 12)
				#rapi.rpgBindColorBufferOfs(vertBuff, noesis.RPGEODATA_UBYTE, 0x40, 24, 4)
				rapi.rpgBindUV1BufferOfs(vertBuff, noesis.RPGEODATA_HALFFLOAT, 0x40, 32)
				rapi.rpgBindUV2BufferOfs(vertBuff, noesis.RPGEODATA_HALFFLOAT, 0x40, 36)
				rapi.rpgBindBoneWeightBufferOfs(vertBuff, noesis.RPGEODATA_HALFFLOAT, 0x40, 48, 4)
				rapi.rpgBindBoneIndexBufferOfs(vertBuff, noesis.RPGEODATA_HALFFLOAT, 0x40, 56, 4)
				rapi.rpgCommitTriangles(faceBuff, noesis.RPGEODATA_INT, meshInfo[i][1], noesis.RPGEO_TRIANGLE_STRIP, 1)
	def loadMaterial(self, bs, cmdHeader, meshInfo, fname):
		texNames = []
		bs.seek(self.offsetList[fname][1] + cmdHeader[5], NOESEEK_ABS)
		for i in range(0, cmdHeader[2]):
			texNames.append(bs.readBytes(0x20).decode("ASCII").rstrip("\0"))
		for i in range(0, cmdHeader[1]):
			bs.seek(self.offsetList[fname][1] + meshInfo[i][5], NOESEEK_ABS)
			matInfo = bs.read(">" + 3 * "h")
			bs.seek(0x1E, NOESEEK_REL)
			matInfo2 = bs.read(">" + 6 * "b")
			bs.seek(6, NOESEEK_REL)
			material = NoeMaterial("material_" + str(fname) + "_" + str(i), "")
			if matInfo2[1] != -1:
				material.setTexture(texNames[matInfo2[1] - 1])
			if matInfo2[2] != -1:
				material.setBumpTexture(texNames[matInfo2[2] - 1])
			material.setFlags(0, 1)
			self.matList.append(material)

	def loadTXT(self, bs, fname):
		pass

	def loadDDS(self, bs, fname):
		ddsHeader = bs.read(">" + "4i")
		bs.seek(0x40, NOESEEK_REL)
		type = bs.readUInt()
		bs.seek(0x28, NOESEEK_REL)
		texData = bs.readBytes(self.offsetList[fname][2] - 0x80)
		texFmt = noesis.NOESISTEX_DXT1
		if type == 0x31545844:
			texFmt = noesis.NOESISTEX_DXT1
		elif type == 0x33545844:
			texFmt = noesis.NOESISTEX_DXT3
		elif type == 0x35545844:
			texFmt = noesis.NOESISTEX_DXT5
		self.texList.append(NoeTexture(self.offsetList[fname][0], ddsHeader[3], ddsHeader[2], texData, texFmt))

flObjectLoaderDict = {
	0x414D3200 : folkloreFile.loadAM2,
	0x434D4400 : folkloreFile.loadCMD,
	0x20534444 : folkloreFile.loadDDS,
}

def flLoadModel(data, mdlList):
	ctx = rapi.rpgCreateContext()
	bi5 = folkloreFile(NoeBitStream(data, NOE_BIGENDIAN))
	bi5.loadAll(bi5.bs)
	rapi.rpgSmoothNormals()
	try:
		mdl = rapi.rpgConstructModel()
	except:
		mdl = NoeModel()
	mdl.setModelMaterials(NoeModelMaterials(bi5.texList, bi5.matList))
	mdlList.append(mdl); mdl.setBones(bi5.boneList)	
	return 1