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

  Creation date: 23.04.2007
  Last modification date: 16.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 "CMFile.h"
#include "fileio.h"
#include "File3Dg.h"
#include "Enemy.h"
#include "CytadelaDefinitions.h"
#include <cassert>

bool CMFile::open(const char *filename)
{
	assert(filename != 0);

	close();

	m3DgFilePath = filename;
	std::string cmfile = m3DgFilePath + ".cmf";

	mFilePtr = fopen(cmfile.c_str(), "rb");
	if(mFilePtr == 0)
		return false;

	return true;
}

void CMFile::close()
{
	if(mFilePtr != 0)
		fclose(mFilePtr);
	mFilePtr = 0;
	m3DgFilePath.clear();
}

bool CMFile::load(GameObjectsManager *objects)
{
	assert(objects != 0);
	assert(mFilePtr != 0);

	I3Dg *geometry = (I3Dg *)objects;
	if(not ((File3Dg *)geometry)->read(m3DgFilePath.c_str()))
		return false;

	//read the objects info and create game objects
	if(not readObjectsInfo(objects))
		return false;

	//map data
	uint32_t size;
	if(not readMapDataSize(size))
		return false;
	//alloc memory for the data
	MapDataPtr ptr = objects->allocMapData(size);
	if(ptr == 0)
		return false;
	//read the map data
	if(not readMapData(ptr))
		return false;

	return true;
}

bool CMFile::readMapDataSize(uint32_t &size)
{
	assert(mFilePtr != 0);

	fseek(mFilePtr, 2 * sizeof(Point3Dg), SEEK_SET);
	//read size of the data
	return fioRead(size, mFilePtr);
}

bool CMFile::readMapData(MapDataPtr &data)
{
	assert(mFilePtr != 0);

	fseek(mFilePtr, 2 * sizeof(Point3Dg), SEEK_SET);
	//read size of the data
	uint32_t size;
	if(not fioRead(size, mFilePtr))
		return false;
	//read the data
	for(uint32_t i = 0; i < size/sizeof(MapDataType); i++) {
		MapDataType unit;
		if(not fioRead(unit, mFilePtr))
			return false;
		data[i] = unit;
	}
	return true;
}

bool CMFile::readInitialPosition(Point3Dg &position)
{
	assert(mFilePtr != 0);

	fseek(mFilePtr, 0, SEEK_SET);
	fioRead(position.x, mFilePtr);
	fioRead(position.y, mFilePtr);
	return fioRead(position.z, mFilePtr);
}

bool CMFile::readInitialDirection(Point3Dg &direction)
{
	assert(mFilePtr != 0);

	fseek(mFilePtr, sizeof(Point3Dg), SEEK_SET);
	fioRead(direction.x, mFilePtr);
	fioRead(direction.y, mFilePtr);
	return fioRead(direction.z, mFilePtr);
}

bool CMFile::readObjectsInfo(GameObjectsManager *objects)
{
	assert(mFilePtr != 0);

	//skip non-objects' data
	fseek(mFilePtr, 2 * sizeof(Point3Dg), SEEK_SET);
	//read size of the data
	uint32_t size;
	if(not fioRead(size, mFilePtr))
		return false;
	fseek(mFilePtr, size, SEEK_CUR);

	//read the data
	uint8_t type;
	while(fioRead(type, mFilePtr)) {
		//UID
		Mesh3DgUID uid;
		if(not fioRead(uid, mFilePtr))
			return false;
		//player collision type
		uint8_t playerCollision;
		if(not fioRead(playerCollision, mFilePtr))
			return false;
		//ammo collision type
		uint8_t ammoCollision;
		if(not fioRead(ammoCollision, mFilePtr))
			return false;
		//type specific info
		switch(type) {
			case GOTYPE_WALL: {
				GOWall *wall = (GOWall *)objects->createGameObject(GOTYPE_WALL, objects->meshes[uid]);
				if(wall == 0)
					return false;
				wall->setPlayerCollisionType((PlayerCollisionType)playerCollision);
				wall->setAmmoCollisionType((AmmoCollisionType)ammoCollision);
				//read the rest of the data
				Mesh3DgUID slotMUID;
				uint32_t offset;
				uint32_t wallBlood[3], basicSlotMtl;
				uint8_t flags;
				fioRead(slotMUID, mFilePtr);
				for(int i = 0; i < 3; i++)
					fioRead(wallBlood[i], mFilePtr);
				fioRead(basicSlotMtl, mFilePtr);
				fioRead(offset, mFilePtr);
				if(not fioRead(flags, mFilePtr))
					return false;
				//set the data
				if(slotMUID != 0)
					wall->setSlotMesh(objects->meshes[slotMUID]);
				wall->setBloodMaterialNumbers(wallBlood);
				wall->setBasicSlotMaterial(basicSlotMtl);
				wall->setSlotActionDataOffset(offset);
				wall->setAllFlags(flags);
				break;
			}
			case GOTYPE_SPRITE: {
				GOSprite *sprite = (GOSprite *)objects->createGameObject(GOTYPE_SPRITE, objects->meshes[uid]);
				if(sprite == 0)
					return false;
				int16_t hp;
				if(not fioRead(hp, mFilePtr))
					return false;
				sprite->setHP(hp);
				sprite->setPlayerCollisionType((PlayerCollisionType)playerCollision);
				sprite->setAmmoCollisionType((AmmoCollisionType)ammoCollision);
				break;
			}
			case GOTYPE_DOOR: {
				GODoor *door = (GODoor *)objects->createGameObject(GOTYPE_DOOR, objects->meshes[uid]);
				if(door == 0)
					return false;
				door->setPlayerCollisionType((PlayerCollisionType)playerCollision);
				door->setAmmoCollisionType((AmmoCollisionType)ammoCollision);
				//read the rest of the data
				Mesh3DgUID frameMUID;
				uint8_t doorType;
				fioRead(frameMUID, mFilePtr);
				if(not fioRead(doorType, mFilePtr))
					return false;
				//set the data
				if(frameMUID == 0)
					return false;
				door->setFrameMesh(objects->meshes[frameMUID]);
				door->setDoorType((DoorType)doorType);
				//add the object to the interface
				break;
			}
			case GOTYPE_ENEMY: {
				Enemy *enemy = (Enemy *)objects->createGameObject(GOTYPE_ENEMY, objects->meshes[uid]);
				if(enemy == 0)
					return false;

				int16_t hp;
				fioRead(hp, mFilePtr);
				uint8_t flags, weaponType;
				fioRead(flags, mFilePtr);
				fioRead(weaponType, mFilePtr);
				Point3Dg direction;
				fioRead(direction.x, mFilePtr);
				fioRead(direction.y, mFilePtr);
				fioRead(direction.z, mFilePtr);
				float speed;
				fioRead(speed, mFilePtr);
				uint16_t something, agression;
				fioRead(something, mFilePtr);
				fioRead(agression, mFilePtr);
				float ammoXPosShift, ammoYPosShift;
				fioRead(ammoXPosShift, mFilePtr);
				if(not fioRead(ammoYPosShift, mFilePtr))
					return false;

				enemy->setHP(hp);
				enemy->setFlags(flags);
				enemy->setWeaponType(weaponType);
				enemy->setDirection((Vector3Dg&)direction);
				enemy->setSpeed(speed);
//				enemy->setSomething(something);
				enemy->setAgression(agression);
				enemy->setPlayerCollisionType((PlayerCollisionType)playerCollision);
				enemy->setAmmoCollisionType((AmmoCollisionType)ammoCollision);
				enemy->setAmmoXPosShift(ammoXPosShift);
				enemy->setAmmoYPosShift(ammoYPosShift);
				break;
			}
		}
	}
	return true;
}
