#Base Model Class

from inc_noesis import *

def registerNoesisTypes():
	handle = noesis.register("Enchanted Arms MDL", ".mdl")
	noesis.setHandlerTypeCheck(handle, earmsCheckType)
	noesis.setHandlerLoadModel(handle, earmsLoadModel)
	#noesis.logPopup()

	handle = noesis.register("Enchanted Arms TBND", ".tbnd")
	noesis.setHandlerTypeCheck(handle, tbndCheckType)
	noesis.setHandlerLoadRGBA(handle, tbndLoadRGBA)

	return 1


def tbndCheckType(data):
	td = NoeBitStream(data)
	Magic = td.readUInt()
	if Magic != 0x33444E42:
		return 0
	return 1

def earmsCheckType(data):
	bs = NoeBitStream(data)
	Magic = bs.readUInt()
	if Magic != 0x344C444D:
		return 0
	return 1

def tbndLoadRGBA(data, texList):
	td = NoeBitStream(data, NOE_BIGENDIAN)
	td.seek(0x10, NOESEEK_ABS)
	texCount = td.readUInt(); td.seek(0x20, NOESEEK_ABS)
	texOff = []
	for i in range(0, texCount):
		texOff.append(td.read(">" + 4 * "i"))
	for i in range(0, texCount):
		td.seek(texOff[i][2], NOESEEK_ABS)
		tpfinfo = td.read(">" + 6 * "i")
		tpfinfo2 = td.read(">" + 4 * "h")
		td.seek(0x10, NOESEEK_REL)
		texName = td.readBytes(0x50).decode("ASCII").rstrip("\0")
		texData = td.readBytes(tpfinfo[5])
		texFmt = noesis.NOESISTEX_DXT1
		if tpfinfo2[0] == 0:
			texFmt = noesis.NOESISTEX_DXT1
		elif tpfinfo2[0] == 1:
			texFmt = noesis.NOESISTEX_DXT1
		elif tpfinfo2[0] == 768:
			texFmt = noesis.NOESISTEX_DXT3
		if tpfinfo2[0] == 1280:
			texFmt = noesis.NOESISTEX_DXT5
		texList.append(NoeTexture(texName, tpfinfo2[2], tpfinfo2[3], texData, texFmt))
	return 1

class EnchArms(object): 
      
	def __init__(self, data):     
		self.inFile   = NoeBitStream(data, NOE_BIGENDIAN)
		rapi.rpgSetOption(noesis.RPGOPT_BIGENDIAN, 1)
		rapi.rpgSetOption(noesis.RPGOPT_TRIWINDBACKWARD, 1)
		rapi.setPreviewOption("setAngOfs", "0 90 90")
		rapi.setPreviewOption("setAnimSpeed", "2.0")
		#all morphs that we provide are relative to the base positions,
		#so we just leave these options on.
		#(otherwise, they affect things only at CommitMorphFrame time,
		#and can be toggled on and off as needed)
		rapi.rpgSetOption(noesis.RPGOPT_MORPH_RELATIVEPOSITIONS, 1)
		rapi.rpgSetOption(noesis.RPGOPT_MORPH_RELATIVENORMALS, 1)
		self.texList  = []
		self.matList  = [] 
		self.boneList = []
		self.boneMap  = []
		self.meshOffsets = []

	def alignPosition(self, bs, alignment):
		alignment = alignment - 1
		bs.seek(((bs.tell() + alignment) & ~alignment), NOESEEK_ABS)

	def loadHeader(self, bs):
		magic = bs.readBytes(4).decode("ASCII").rstrip("\0")
		mdlVersion,  mdlSubVersion = bs.read(">" + 2 * "H")
		self.dataOffset, self.dataSize, self.count1, self.materialCount, \
		self.boneCount, self.meshCount, self.meshCount2 \
		= bs.read(">" + 7 * "i")
		self.bbMin = NoeVec3( (bs.readFloat(), bs.readFloat(), bs.readFloat()) )
		self.bbMax = NoeVec3( (bs.readFloat(), bs.readFloat(), bs.readFloat()) )
		self.totalVertCount, self.totalFaceCount = bs.read(">" + 2 * "i")
		bs.seek(0x80, NOESEEK_ABS)
		#print("Data Offset:", self.dataOffset, " Data Size:", self.dataSize, " Count 1:", self.count1, \
		#" Material Count:", self.materialCount, " Bone Count:", self.boneCount, " Mesh Count:", \
		#self.meshCount, " Mesh Count 2:", self.meshCount2)
		#print("Bounding Box Min:", self.bbMin, " Bounding Box Max:", self.bbMax)
		#print("Vertex Count:", self.totalVertCount, " Face Count:", self.totalFaceCount)

	def loadSection1(self, bs):
		#print("Section1 Start:", bs.tell())
		bs.seek(0x30 * self.count1, NOESEEK_REL)


	def loadMaterials(self, bs):
		#print("Material Info Start:", bs.tell())
		for i in range(0, self.materialCount):
			matStart = bs.tell()
			try:
				materialName = bs.readBytes(0x1F).decode("ASCII").rstrip("\0")
			except:
				print("WARNING: Non-ASCII string data:")
				materialName = "material_" + str(i)
			#materialName = bs.readBytes(0x1F).decode("ASCII").rstrip("\0")
			shaderName = bs.readBytes(0x1F).decode("ASCII").rstrip("\0")
			material = NoeMaterial(materialName, "")
			modelID = bs.readUByte()
			shaderCount = bs.readUByte()
			for a in range(0, shaderCount):
				valueType = bs.readUByte()
				valueName = bs.readBytes(0x1F).decode("ASCII").rstrip("\0")
				if valueName in testMaterialLoaderDict:
					testMaterialLoaderDict[valueName](material, bs)
				else:
					print(bs.tell())
					print("New Material Found:",valueName)
					bs.seek(0x20, NOESEEK_REL)
			self.matList.append(material)
			bs.seek(0x840 + matStart, NOESEEK_ABS)

	def loadBones(self, bs):
		#print("Bone Info Start:", bs.tell())
		for i in range(0, self.boneCount):
			try:
				boneName = bs.readBytes(0x20).decode("ASCII").rstrip("\0")
			except:
				print("WARNING: Non-ASCII string data:")
				boneName = "bone_" + str(i)
			pos = NoeVec3.fromBytes(bs.readBytes(12), NOE_BIGENDIAN)
			boneMtx = NoeAngles.fromBytes(bs.readBytes(12), NOE_BIGENDIAN).toDegrees().toMat43_XYZ()
			boneMtx[3] = pos
			bs.seek(0x24, NOESEEK_REL)
			bInfo = bs.read(">" + 4 * "h")
			bs.seek(0x2C, NOESEEK_REL)
			newBone = NoeBone(i, boneName, boneMtx, None, bInfo[0])
			self.boneList.append(newBone)
		self.boneList = rapi.multiplyBones(self.boneList)


	def loadModelInfo(self, bs):
		#print("Model Info Start:", bs.tell())
		for i in range(0, self.meshCount):
			morphInfo = []
			bonePallet = []
			#print(bs.tell())
			flags = bs.read(4 * "b")
			faceCount, unk = bs.read(">" + 2 * "h")
			for a in range(0, 28):
				boneID = bs.readShort()
				if boneID != -1:
					bonePallet.append(boneID)
			faceSize, faceStart, vertSize, vertStart = bs.read(">" + 4 * "i")
			if flags[0] == 2:
				for a in range(0, 16):
					morphSize, morphOffset = bs.read(">" + 2 * "i")
					if morphSize != 0:
						morphInfo.append([morphSize, morphOffset])
			self.meshOffsets.append([faceSize, faceStart, vertSize, vertStart, flags[0], flags[1], faceCount, morphInfo])
			self.boneMap.append(bonePallet)
			#print(flags)
			#print((self.dataOffset + faceStart), (self.dataOffset + vertStart), vertSize)
			#print(faceSize, faceStart, vertSize, vertStart)
			


	def loadMesh(self, bs):
		bs.seek(self.dataOffset, NOESEEK_ABS)
		#print("Mesh Start:", bs.tell())
		for i in range(0, self.meshCount):
			#print(self.meshOffsets[i][4])
			bs.seek(self.dataOffset + self.meshOffsets[i][1], NOESEEK_ABS)
			faceBuff = bs.readBytes(self.meshOffsets[i][0])
			bs.seek(self.dataOffset + self.meshOffsets[i][3], NOESEEK_ABS)
			vertBuff = bs.readBytes(self.meshOffsets[i][2])
			rapi.rpgSetBoneMap(self.boneMap[i])
			#rapi.rpgSetName(str(i))
			rapi.rpgSetMaterial(self.matList[self.meshOffsets[i][5]].name)
			if self.meshOffsets[i][4] == 0:
				rapi.rpgSetPosScaleBias(None, None)
				rapi.rpgBindPositionBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 0x40, 0)
				rapi.rpgBindColorBufferOfs(vertBuff, noesis.RPGEODATA_UBYTE, 0x40, 24, 4)
				rapi.rpgBindUV1BufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 0x40, 28)
				rapi.rpgBindUV2BufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 0x40, 44)
				#rapi.rpgBindBoneIndexBufferOfs(vertBuff, noesis.RPGEODATA_UBYTE, 0x40, 20, 4)
				#rapi.rpgBindBoneWeightBufferOfs(vertBuff, noesis.RPGEODATA_UBYTE, 0x40, 60, 4)
				rapi.rpgCommitTriangles(faceBuff, noesis.RPGEODATA_SHORT, self.meshOffsets[i][6], noesis.RPGEO_TRIANGLE_STRIP, 1)
			elif self.meshOffsets[i][4] == 1:
				rapi.rpgSetPosScaleBias(None, None)
				rapi.rpgBindPositionBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 0x54, 0)
				#rapi.rpgBindColorBufferOfs(vertBuff, noesis.RPGEODATA_UBYTE, 0x54, 24, 4)
				rapi.rpgBindUV1BufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 0x54, 28)
				rapi.rpgBindUV2BufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 0x54, 44)
				rapi.rpgBindBoneIndexBufferOfs(vertBuff, noesis.RPGEODATA_SHORT, 0x54, 60, 4)
				rapi.rpgBindBoneWeightBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 0x54, 68, 4)
				rapi.rpgCommitTriangles(faceBuff, noesis.RPGEODATA_SHORT, self.meshOffsets[i][6], noesis.RPGEO_TRIANGLE_STRIP, 1)
			elif self.meshOffsets[i][4] == 2:
				rapi.rpgSetPosScaleBias((328.0,328.0,328.0),(0,0,0))
				#print(self.meshOffsets[i][7][0])
				bs.seek(self.dataOffset + self.meshOffsets[i][7][0][1], NOESEEK_ABS)
				posBuff = bs.readBytes(self.meshOffsets[i][7][0][0])
				rapi.rpgBindPositionBufferOfs(posBuff, noesis.RPGEODATA_SHORT, 16, 0)
				#rapi.rpgBindColorBufferOfs(vertBuff, noesis.RPGEODATA_UBYTE, 0x3C, 0, 4)
				rapi.rpgBindUV1BufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 0x3C, 4)
				rapi.rpgBindUV2BufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 0x3C, 20)
				rapi.rpgBindBoneIndexBufferOfs(vertBuff, noesis.RPGEODATA_SHORT, 0x3C, 36, 4)
				rapi.rpgBindBoneWeightBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 0x3C, 44, 4)
				for a in range(1, len(self.meshOffsets[i][7])):
					bs.seek(self.dataOffset + self.meshOffsets[i][7][a][1], NOESEEK_ABS)
					morphBuff = bs.readBytes(self.meshOffsets[i][7][a][0])
					rapi.rpgFeedMorphTargetPositionsOfs(morphBuff, noesis.RPGEODATA_SHORT, 16, 0)
					rapi.rpgCommitMorphFrame(self.meshOffsets[i][7][a][0] // 16)
				rapi.rpgCommitMorphFrameSet()
				rapi.rpgCommitTriangles(faceBuff, noesis.RPGEODATA_SHORT, self.meshOffsets[i][6], noesis.RPGEO_TRIANGLE_STRIP, 1)

			rapi.rpgClearBufferBinds()
	def load_tex(self): 
		folderName = rapi.getDirForFilePath(rapi.getInputName())
		baseName = rapi.getExtensionlessName(rapi.getLocalFileName(rapi.getInputName()))
		texFile = folderName + baseName + ".tbnd"
		if (rapi.checkFileExists(texFile)):
			#print(texFile)
			texData = rapi.loadIntoByteArray(texFile)
			tbndLoadRGBA(texData, self.texList)


	def parse_file(self): 
		bs = self.inFile
		self.loadHeader(bs)
		self.load_tex()
		self.loadSection1(bs)
		self.loadMaterials(bs)
		self.loadBones(bs)
		self.loadModelInfo(bs)
		self.loadMesh(bs)
		return 1


#Noesis blends:
#0 - "None"
#1 - "GL_ZERO"
#2 - "GL_ONE"
#3 - "GL_SRC_COLOR"
#4 - "GL_ONE_MINUS_SRC_COLOR"
#5 - "GL_SRC_ALPHA"
#6 - "GL_ONE_MINUS_SRC_ALPHA"
#7 - "GL_DST_ALPHA"
#8 - "GL_ONE_MINUS_DST_ALPHA"
#9 - "GL_DST_COLOR"
#10 - "GL_ONE_MINUS_DST_COLOR"
#11 - "GL_SRC_ALPHA_SATURATE"

class Material:
	def _init_(self):
		texColorExpr = "abs(mtl_diffuse_a, 0, 12)"
		self.setExpr_texidx(texColorExpr)
		pass
		#self.setBlendMode("None", "None")

	def load_g_MatAlphaBlend(self, bs):
		AlphaBlend = bs.readUInt()
		bs.seek(0x1C, NOESEEK_REL)

	def load_g_MatAlphaRef(self, bs):
		AlphaRef = bs.readUInt()
		self.setDefaultBlend(0)
		#if AlphaRef:
		#	self.setBlendMode("GL_ONE_MINUS_SRC_COLOR", "None")
		bs.seek(0x1C, NOESEEK_REL)

	def load_g_MatSelfIllumeCol(self, bs):
		#self.setAmbientColor(NoeVec4.fromBytes(bs.readBytes(16), NOE_BIGENDIAN))
		bs.seek(0x20, NOESEEK_REL)

	def load_g_MatSelfIllumeCoef(self, bs):
		bs.seek(0x20, NOESEEK_REL)

	def load_g_MatDiffCol(self, bs):
		#self.setDiffuseColor(NoeVec4.fromBytes(bs.readBytes(16), NOE_BIGENDIAN))
		bs.seek(0x20, NOESEEK_REL)

	def load_g_MatDiffCoef(self, bs):
		bs.seek(0x20, NOESEEK_REL)

	def load_g_MatDiffRough(self, bs):
		bs.seek(0x20, NOESEEK_REL)

	def load_g_MatSpecCol(self, bs):
		#self.setSpecularColor(NoeVec4.fromBytes(bs.readBytes(16), NOE_BIGENDIAN))
		bs.seek(0x20, NOESEEK_REL)

	def load_g_MatSpecCoef(self, bs):
		bs.seek(0x20, NOESEEK_REL)

	def load_g_MatSpecPower(self, bs):
		bs.seek(0x20, NOESEEK_REL)

	def load_g_MatEnvmapFresnelScale(self, bs):
		bs.seek(0x20, NOESEEK_REL)

	def load_g_MatEnvmapFresnelBias(self, bs):
		bs.seek(0x20, NOESEEK_REL)

	def load_g_DiffuseTexture(self, bs):
		self.setTexture(bs.readBytes(0x20).decode("ASCII").rstrip("\0"))


	def load_g_DiffuseTexture2(self, bs):
		bs.seek(0x20, NOESEEK_REL)
		

	def load_g_AlphamapTexture(self, bs):
		bs.seek(0x20, NOESEEK_REL)
		#self.setOpacityTexture(bs.readBytes(0x20).decode("ASCII").rstrip("\0"))

	def load_g_LightmapTexture(self, bs):
		bs.seek(0x20, NOESEEK_REL)
		#self.setOpacityTexture(bs.readBytes(0x20).decode("ASCII").rstrip("\0"))

	def load_g_CubeEnvmapTexture(self, bs):
		bs.seek(0x20, NOESEEK_REL)

	def load_g_EnvmapMaskTexture(self, bs):
		bs.seek(0x20, NOESEEK_REL)
		#self.setEnvTexture(bs.readBytes(0x20).decode("ASCII").rstrip("\0"))

	def load_g_BumpmapTexture(self, bs):
		#bs.seek(0x20, NOESEEK_REL)
		self.setBumpTexture(bs.readBytes(0x20).decode("ASCII").rstrip("\0"))

	def load_g_MatParallaxOffset(self, bs):
		bs.seek(0x20, NOESEEK_REL)

	def load_g_EnchantMapTexture(self, bs):
		bs.seek(0x20, NOESEEK_REL)
		#self.setBumpTexture(bs.readBytes(0x20).decode("ASCII").rstrip("\0"))

	def load_g_MatEnchantMapParamID(self, bs):
		MatEnchantMapParamID = bs.readUInt()
		bs.seek(0x1C, NOESEEK_REL)

	def load_g_MatDiffLerpExp(self, bs):
		bs.seek(0x20, NOESEEK_REL)

testmaterialTypeDict = {

}



testMaterialLoaderDict = {
	"g_MatAlphaBlend"		: Material.load_g_MatAlphaBlend,
	"g_MatAlphaRef"			: Material.load_g_MatAlphaRef,
	"g_MatSelfIllumeCol"		: Material.load_g_MatSelfIllumeCol,
	"g_MatSelfIllumeCoef"		: Material.load_g_MatSelfIllumeCoef,
	"g_MatDiffCol"			: Material.load_g_MatDiffCol,
	"g_MatDiffCoef"			: Material.load_g_MatDiffCoef,
	"g_MatDiffRough"		: Material.load_g_MatDiffRough,
	"g_MatSpecCol"			: Material.load_g_MatSpecCol,
	"g_MatSpecCoef"			: Material.load_g_MatSpecCoef,
	"g_MatSpecPower"		: Material.load_g_MatSpecPower,
	"g_MatEnvmapFresnelScale"	: Material.load_g_MatEnvmapFresnelScale,
	"g_MatEnvmapFresnelBias"	: Material.load_g_MatEnvmapFresnelBias,
	"g_DiffuseTexture"		: Material.load_g_DiffuseTexture,
	"g_DiffuseTexture2"		: Material.load_g_DiffuseTexture2,
	"g_AlphamapTexture"		: Material.load_g_AlphamapTexture,
	"g_LightmapTexture"		: Material.load_g_LightmapTexture,
	"g_CubeEnvmapTexture"		: Material.load_g_CubeEnvmapTexture,
	"g_EnvmapMaskTexture"		: Material.load_g_EnvmapMaskTexture,
	"g_BumpmapTexture"		: Material.load_g_BumpmapTexture,
	"g_MatParallaxOffset"		: Material.load_g_MatParallaxOffset,
	"g_EnchantMapTexture"		: Material.load_g_EnchantMapTexture,
	"g_MatEnchantMapParamID"	: Material.load_g_MatEnchantMapParamID,
	"g_MatDiffLerpExp"		: Material.load_g_MatDiffLerpExp

}



def earmsLoadModel(data, mdlList):
	ctx = rapi.rpgCreateContext()
	parser = EnchArms(data)
	parser.parse_file()
	#rapi.rpgSmoothNormals()
	#rapi.rpgSmoothTangents()
	#rapi.rpgUnifyBinormals(1)
	try:
		mdl = rapi.rpgConstructModel()
	except:
		mdl = NoeModel()
	mdl.setModelMaterials(NoeModelMaterials(parser.texList, parser.matList))
	mdlList.append(mdl); mdl.setBones(parser.boneList)	
	return 1