/*
  File name: File3Dg.cpp
  Copyright: (C) 2007 - 2009 Tomasz Kazmierczak

  Creation date: 20.04.2007
  Last modification date: 13.06.2009

  This file is part of Cytadela

 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program. If not, see <http://www.gnu.org/licenses/>.  *

*/

#include <memory.h>
#include "File3Dg.h"
#include "fileio.h"
#include "CytadelaDefinitions.h"

#define FILEID_3DG 0x44334C4D

//object types stored in file
enum {
	OBJECT3DG_LIGHT,
	OBJECT3DG_TEXTURE,
	OBJECT3DG_MATERIAL,
	OBJECT3DG_MESHBASE,
	OBJECT3DG_MESH
};

bool File3Dg::read(const char *fileName)
{
	FILE *file = fopen(fileName, "rb");
	if(file == 0) {
		fprintf(stderr, "ERROR: File3Dg::read() - could not open \'%s\' for reading", fileName);
		return false;
	}
	if(not read(file)) {
		fprintf(stderr, "\twhile reading \'%s\'", fileName);
		fclose(file);
		return false;
	}
	fclose(file);
	return true;
}


bool File3Dg::readColor(ColorRGBA3Dg &color, FILE* file)
{
	fioRead(color.r, file);
	fioRead(color.g, file);
	fioRead(color.b, file);
	return fioRead(color.a, file);
}

bool File3Dg::readPoint(Point3Dg &point, FILE *file)
{
	fioRead(point.x, file);
	fioRead(point.y, file);
	return fioRead(point.z, file);
}

bool File3Dg::readVertex(Vertex3Dg &vertex, FILE *file)
{
	readPoint(vertex.coords, file);
	readPoint(vertex.normal, file);
	fioRead(vertex.tex[0], file);
	return fioRead(vertex.tex[1], file);
}

bool File3Dg::readTriangle(Triangle3Dg &triangle, FILE *file)
{
	readVertex(triangle.vertexA, file);
	readVertex(triangle.vertexB, file);
	return readVertex(triangle.vertexC, file);
}

bool File3Dg::readMatrix(Matrix3Dg &matrix, FILE *file)
{
	for(int i = 0; i < 4; i++) {
		for(int j = 0; j < 4; j++) {
			if(not fioRead(matrix.m[i][j], file))
				return false;
		}
	}
	return true;
}

bool File3Dg::readAnimation(MeshAnim3Dg &animation, FILE *file)
{
	readPoint(animation.translation, file);
	readPoint(animation.rotation, file);
	readPoint(animation.scaling, file);
	fioRead(animation.elapsedTime, file);
	fioRead(animation.animStatus, file);
	fioRead(animation.geomAnimTime, file);
	fioRead(animation.texAnimTime, file);
	fioRead(animation.beginMtl, file);
	return fioRead(animation.numMtls, file);
}

bool File3Dg::readLight(FILE *file)
{
	Light3Dg *light = createLight();
	if(light == 0)
		return false;

	uint32_t lightType;
	fioRead(lightType, file);
	light->lightType = LightType3Dg(lightType);
	readColor(light->diffuse, file);
	readColor(light->specular, file);
	readColor(light->ambient, file);
	readPoint(light->position, file);
	readPoint(light->direction, file);
	fioRead(light->range, file);
	fioRead(light->falloff, file);
	fioRead(light->attenuation0, file);
	fioRead(light->attenuation1, file);
	fioRead(light->attenuation2, file);
	fioRead(light->theta, file);
	if(not fioRead(light->phi, file)) {
		fprintf(stderr, "ERROR: File3Dg::readLight() - could not read the file");
		return false;
	}
	return true;
}

bool File3Dg::readTexture(FILE *file)
{
	//texture info
	uint32_t texType;
	uint32_t wrapS, wrapT;
	uint16_t pathBufferSize;
	fioRead(texType, file);
	fioRead(wrapS, file);
	fioRead(wrapT, file);
	if(not fioRead(pathBufferSize, file)) {
		fprintf(stderr, "ERROR: File3Dg::readTexture() - could not read the file");
		return false;
	}

	char *path = new char[pathBufferSize];
	if(path == 0) {
		fprintf(stderr, "ERROR: File3Dg::readTexture() - out of memory");
		return false;
	}
	//read the texture name
	if(fread(path, 1, pathBufferSize * sizeof(char), file) < pathBufferSize * sizeof(char)) {
		delete[] path;
		fprintf(stderr, "ERROR: File3Dg::readTexture() - could not read the file");
		return false;
	}

	Texture3Dg *texture = createTexture(path, pathBufferSize);
	if(texture == 0) {
		delete[] path;
		fprintf(stderr, "ERROR: File3Dg::readTexture() - createTexture() returned \'%s\'", getLastErrorDesc());
		return false;
	}
	delete[] path;

	texture->type = TextureType3Dg(texType);
	texture->wrapModeS = TextureWrapMode3Dg(wrapS);
	texture->wrapModeT = TextureWrapMode3Dg(wrapT);
	return true;
}

bool File3Dg::readMaterial(FILE *file)
{
	Material3Dg *mtl = createMaterial();
	if(mtl == 0) {
		fprintf(stderr, "ERROR: File3Dg::readMaterial() - createMaterial() returned \'%s\'", getLastErrorDesc());
		return false;
	}

	readColor(mtl->diffuse, file);
	readColor(mtl->ambient, file);
	readColor(mtl->specular, file);
	readColor(mtl->emissive, file);
	fioRead(mtl->shininess, file);
	uint32_t texturesQuantity;
	if(not fioRead(texturesQuantity, file)) {
		fprintf(stderr, "ERROR: File3Dg::readMaterial() - could not read the file");
		return false;
	}

	//read texture numbers
	for(uint32_t j = 0; j < texturesQuantity; j++) {
		uint32_t texNum;
		if(not fioRead(texNum, file)) {
			fprintf(stderr, "ERROR: File3Dg::readMaterial() - could not read the file");
			return false;
		}
		if(not mtl->addTexture(texNum)) {
			fprintf(stderr, "ERROR: File3Dg::readMaterial() - could not read the file");
			return false;
		}
	}
	return true;
}

bool File3Dg::readMeshBase(FILE *file)
{
	//read the unique identifier
	MeshBase3DgUID mbuid;
	if(not fioRead(mbuid, file)) {
		fprintf(stderr, "ERROR: File3Dg::readMeshBase() - could not read the file");
		return false;
	}

	//create a new mesh base
	MeshBase3Dg *base = createMeshBase(mbuid);
	if(base == 0) {
		fprintf(stderr, "ERROR: File3Dg::readMeshBase() - createMeshBase() returned \'%s\'", getLastErrorDesc());
		return false;
	}

	uint32_t triansQuantity;
	if(not fioRead(triansQuantity, file)) {
		fprintf(stderr, "ERROR: File3Dg::readMeshBase() - could not read the file");
		return false;
	}

	//read the trians
	Triangle3Dg triangle;
	for(uint32_t t = 0; t < triansQuantity; t++) {
		if(not readTriangle(triangle, file)) {
			fprintf(stderr, "ERROR: File3Dg::readMeshBase() - could not read the file");
			return false;
		}
		if(not base->addTrian(triangle)) {
			fprintf(stderr, "ERROR: File3Dg::readMeshBase() - out of memory");
			return false;
		}
	}
	return true;
}

bool File3Dg::readMesh(FILE *file)
{
	//read the unique identifier
	Mesh3DgUID muid;
	if(not fioRead(muid, file)) {
		fprintf(stderr, "ERROR: File3Dg::readMesh() - could not read the file");
		return false;
	}

	//create a new mesh
	Mesh3Dg *mesh = createMesh(muid);
	if(mesh == 0) {
		fprintf(stderr, "ERROR: File3Dg::readMesh() - createMesh() returned \'%s\'", getLastErrorDesc());
		return false;
	}

	//read the data
	MeshBase3DgUID meshBaseUID;
	uint32_t props;
	fioRead(meshBaseUID, file);
	fioRead(props, file);
	readMatrix(mesh->rotation, file);
	readMatrix(mesh->translation, file);
	readMatrix(mesh->scaling, file);
	fioRead(mesh->materialNumber, file);
	if(not readAnimation(mesh->animation, file)) {
		fprintf(stderr, "ERROR: File3Dg::readMesh() - could not read the file");
		return false;
	}

	//info about mesh
	if(props & MRP_HAS_BASE)
		mesh->setBase(mMeshBases[meshBaseUID]);
	mesh->setProps(props);
	return true;
}

bool File3Dg::read(FILE *file)
{
	//read the header
	FileHeader3Dg header;
	fioRead(header.id, file);
	fioRead(header.version, file);
	fioRead(header.reserved1, file);
	if(not fioRead(header.reserved2, file)) {
		fprintf(stderr, "ERROR: File3Dg::read() - could not read the file");
		return false;
	}

	//check for identifier
	if(header.id != FILEID_3DG) {
		fprintf(stderr, "ERROR: File3Dg::read() - not a 3Dg file");
		return false;
	}
	//check the version
	if(header.version != 43) {
		fprintf(stderr, "ERROR: File3Dg::read() - bad file version");
		return false;
	}

	//read all objects
	uint32_t objectType;
	while(fioRead(objectType, file)) {
		switch(objectType) {
			case OBJECT3DG_LIGHT: {
				if(not readLight(file))
					return false;
				break;
			}
			case OBJECT3DG_TEXTURE: {
				if(not readTexture(file))
					return false;
				break;
			}
			case OBJECT3DG_MATERIAL: {
				if(not readMaterial(file))
					return false;
				break;
			}
			case OBJECT3DG_MESH: {
				if(not readMesh(file))
					return false;
				break;
			}
			case OBJECT3DG_MESHBASE: {
				if(not readMeshBase(file))
					return false;
				break;
			}
			default:
				fprintf(stderr, "ERROR: File3Dg::read() - unknown object type");
				return false;
		}
	}
	return true;
}
