/*!
 * @file Creature.cpp
 * @brief ゲーム中のクリーチャーを定義する
 * @date 2013/12/10
 * @author Deskull
 * 2013 Sikabane Works.
 */

#include "stdafx.h"
#include "Creature.h"

#include "GameSurface.h"
#include "GameLogger.h"
#include "GameWorld.h"

#include "Field.h"
#include "Floor.h"
#include "Item.h"
#include "Square.h"

namespace Deeangband
{

	SOUL Creature::soulLevel[CREATURE_MAX_LEVEL] =
	{
		// 0    // ロスト域
		50,	 // Lv1デフォルト基準ソウル
		100, // Lv2到達ソウル
		130, // :
		180,
		260,
		360,
		480,
		630,
		880,
		1280, // Lv10到達ソウル
		1700,
		2200,
		2800,
		3600,
		4600,
		6100,
		8800,
		10600,
		12600,
		15000, // Lv20到達ソウル
		18000,
		24000,
		35000,
		50000,
		75000,
		100000,
		150000,
		200000,
		275000,
		350000, // Lv30到達ソウル
		450000,
		550000,
		700000,
		1000000,
		1500000,
		2100000,
		2700000,
		3600000,
		4700000,
		6000000, // Lv40到達ソウル
		7500000,
		9000000,
		11500000,
		14500000,
		19000000,
		26000000,
		33000000,
		45000000,
		60000000,
		90000000, // Lv50到達ソウル
		150000000,
		230000000,
		400000000,
		620000000,
		880000000,
		1150000000,
		1500000000,
		3200000000,
		6400000000,
		11100000000, // Lv60到達ソウル
	};

	Creature::Creature(std::map<TAG, std::shared_ptr<Species>>::iterator speciesIt)
		: GameInstance(), HaveSymbol(), HaveHp(), HaveGameTime(), HavePosition(), HaveInventory(), HaveSize()
	{
		WipeData();
		SetSpeciesData(&(*speciesIt->second));
	}

	Creature::Creature(std::map<TAG, std::shared_ptr<Species>>::iterator& speciesIt, std::map<ID, std::shared_ptr<Field>>::iterator& fieldIt, MAP_LENGTH x, MAP_LENGTH y)
		: GameInstance(), HaveSymbol(), HaveHp(), HaveGameTime(), HavePosition(fieldIt, x, y), HaveInventory(), HaveSize()
	{
		WipeData();
		sightList.resize((this->sightRange * 2 + 1)*(this->sightRange * 2 + 1));
		fieldIt->second->GetSight(sightList, this->sightRange, x, y);
		SetSpeciesData(speciesIt->second.get());
	}

	Creature::Creature(void)
	{
		WipeData();
	}

	Creature::~Creature(void)
	{
	}

	void Creature::SetSpeciesData(Species *speciesPtr)
	{
		std::normal_distribution<> distHeight(speciesPtr->GetHeight(), GameConstants::HeightStandardDeviation);
		std::normal_distribution<> distWeight(speciesPtr->GetWeight(), GameConstants::WeightStandardDeviation);
		this->name = speciesPtr->GetName();
		this->symbol = speciesPtr->GetSymbol();
		this->symbolColor = speciesPtr->GetSymbolColor();
		this->currentSoul = this->maxSoul = speciesPtr->GetBaseSoul();
		this->currentFeed = speciesPtr->GetBaseFeed() * 3 / 4;
		this->maxFeed = speciesPtr->GetBaseFeed();
		this->weight = distWeight(Dice::mt);
		this->height = distHeight(Dice::mt);
		this->campTag = TAG_VARIABLE;
		this->firstRace = speciesPtr->GetFirstRaceTag();
		this->secondRace = speciesPtr->GetSecondtRaceTag();
	}

	void Creature::WipeData(void)
	{
		GameInstance::WipeData();
		HaveSymbol::WipeData();
		HaveSize::WipeData();
		this->currentMp = this->maxMp = this->maxMaxMp = 10;
		this->currentSoul = this->maxSoul = Creature::soulLevel[0];
		this->currentDiscipilne.SetPoint(0, 0, 0, 0, 0);
		this->firstRace = "";
		this->secondRace = "";
		this->height = 160.0f;
		this->weight = 50.0f;
		this->divineLevel = -1;
		savings.Set(10, 10, 10);
		this->currentFeed = this->maxFeed = 20000;

		currentStatus.Set(8, 8, 8, 8, 8, 8);
		maxStatus.Set(8, 8, 8, 8, 8, 8);
		maxMaxStatus.Set(20, 20, 20, 20, 20, 20);

		sightRange = BASE_SIGHT_RANGE;

		this->calcHPTable();
		this->currentHp = this->maxHp = this->GetNorMaxHP();

		this->skillExpList.clear();

		this->turn = 0;
		this->campTag = TAG_VARIABLE;

		//this->fieldID = -1;
		//this->position.Set(0, 0);

		this->skillExpList.clear();

		this->symbol = '@';
		this->symbolColor.SetColor(255, 255, 255, 255);
		this->fieldID = 0;
	}

	bool Creature::IsAlive(void)
	{
		return this->currentHp > 0 && this->currentSoul > 0;
	}

	bool Creature::TakeEffect(Effect *effectPtr, POWER amount)
	{
		if(!effectPtr) this->CalcHP(-amount);
		return false;
	}

	void Creature::die(void)
	{
	}

	void Creature::calcHPTable(void)
	{
		int lev;
		HP hitDice = this->GetSize();
		HP minLine = hitDice * (CREATURE_MAX_LEVEL + 4) * 70 / 100;
		HP maxLine = hitDice * (CREATURE_MAX_LEVEL + 4) * 130 / 100;

		if(hitDice <= 0) hitDice = 1;

		do
		{
			this->hpTable[0] = hitDice + Dice::Cast(3, hitDice);
			for(lev = 0; lev < CREATURE_MAX_LEVEL - 1; lev++)
			{
				this->hpTable[lev + 1] = this->hpTable[lev] + Dice::Rand1(hitDice);
			}
		}
		while(this->hpTable[CREATURE_MAX_LEVEL - 1] < minLine && this->hpTable[CREATURE_MAX_LEVEL - 1] > maxLine);
	}

	void Creature::calcMP(MP amount)
	{
		this->currentMp += amount;
		if(this->currentMp > this->maxMp) this->currentMp = this->maxMp;
		if(this->currentMp < 0) this->currentMp = 0;
	}

	void Creature::setMP(MP amount)
	{
		this->currentMp += amount;
		if(this->currentMp > this->maxMp) this->currentMp = this->maxMp;
		if(this->currentMp < 0) this->currentMp = 0;
	}

	LEVEL Creature::GetLevel(void)
	{
		LEVEL level;
		if(this->currentSoul <= 0) return 0;
		if(this->currentSoul < Creature::soulLevel[1]) return 1;
		for(level = 2; level < CREATURE_MAX_LEVEL; level++)
		{
			if(Creature::soulLevel[level - 1] > this->currentSoul) return level;
		}
		return level;
	}

	HP Creature::GetNorMaxHP(void)
	{
		return this->hpTable[this->GetLevel()];
	}

	MP Creature::GetCurMP(void)
	{
		return this->currentMp;
	}

	MP Creature::GetMaxMP(void)
	{
		return this->maxMp;
	}

	MP Creature::GetNorMaxMP(void)
	{
		return this->maxMaxMp;
	}

	AC Creature::GetArmorSaving(void)
	{
		return this->savings.GetArmor();
	}

	EV Creature::GetEvasionSaving(void)
	{
		return this->savings.GetEvasion();
	}

	VO Creature::GetVolitionSaving(void)
	{
		return this->savings.GetVolition();
	}

	SOUL Creature::GetCurrentSoul(void)
	{
		return this->currentSoul;
	}

	SOUL Creature::GetMaxSoul(void)
	{
		return this->maxSoul;
	}

	BASE_STATUS Creature::GetCurrentStatus(CREATURE_STATUS stat)
	{
		return currentStatus.Get(stat);
	}

	BASE_STATUS Creature::GetMaxStatus(CREATURE_STATUS stat)
	{
		return maxStatus.Get(stat);
	}

	BASE_STATUS Creature::GetMaxMAXStatus(CREATURE_STATUS stat)
	{
		return maxMaxStatus.Get(stat);
	}

	DISCIPLINE_POINT Creature::GetDiscipilnePoint(DISCIPLINE_TYPE typ)
	{
		return this->currentDiscipilne.GetPoint(typ);
	}

	DISCIPLINE_POINT Creature::GetDiscipilneRank(DISCIPLINE_TYPE typ)
	{
		return this->currentDiscipilne.GetRank(typ);
	}

	void Creature::GainSoul(SOUL amount)
	{
		SOUL diff;
		if(this->currentSoul < this->maxSoul)
		{
			diff = this->maxSoul - this->currentSoul;
			if(diff < amount)
			{
				this->maxSoul += diff / 20;
				this->currentSoul = this->maxSoul;
				amount -= diff;
			}
			else
			{
				this->maxSoul += amount / 20;
				this->currentSoul += amount;
				amount = 0;
			}
		}
		this->currentSoul += amount;
		this->maxSoul += amount;
	}

	void Creature::LostSoul(SOUL amount)
	{
		this->currentSoul -= amount;
		this->maxSoul -= amount / 20;
		if(this->currentSoul < 1) this->die();
	}

	LEVEL Creature::GetDivineLevel(void)
	{
		return this->divineLevel;
	}

	FEED Creature::GetCurrentFeedPoint(void)
	{
		return this->currentFeed;
	}

	FEED Creature::GetMaxFeedPoint(void)
	{
		return this->maxFeed;
	}

	PLAY_EXIT_CODE Creature::TurnProcess(void)
	{
		Deeangband::GAME_COMMAND commandID;
		commandID = GameElement::GameSurfacePtr->GetCommand(GameElement::GameWorldPtr->GetPlayerCreature());
		return DoGameCommand(commandID);
	}

	PLAY_EXIT_CODE Creature::TimeProcess(void)
	{
		this->currentFeed -= 10;
		return PLAY_EXIT_NONE;
	}

	TURN Creature::GetTurn(void)
	{
		return this->turn;
	}

	SKILL_EXP Creature::GetSkillExp(TAG tag)
	{
		if(this->skillExpList.count(tag)) return this->skillExpList[tag];
		else return 0;
	}

	TAG Creature::GetCampTag(void)
	{
		return this->campTag;
	}

	bool Creature::Melee(Creature *targetPtr)
	{
		this->TakeEffect(NULL, Dice::Cast(1, 6));
		targetPtr->TakeEffect(NULL, Dice::Cast(1, 6));
		return true;
	}

	bool Creature::PickUpItem(std::map<ID, std::shared_ptr<Item>>::iterator itemIt)
	{
		return true;
	}

	bool Creature::DropItem(std::map<ID, std::shared_ptr<Item>>::iterator itemIt)
	{
		return true;
	}

	bool Creature::EatItem(std::map<ID, std::shared_ptr<Item>>::iterator itemIt)
	{
		return true;
	}

	bool Creature::QuaffItem(std::map<ID, std::shared_ptr<Item>>::iterator itemIt)
	{
		return true;
	}

	bool Creature::ActivateItem(std::map<ID, std::shared_ptr<Item>>::iterator itemIt)
	{
		return true;
	}

	bool Creature::ThrowItem(std::map<ID, std::shared_ptr<Item>>::iterator itemIt)
	{
		return true;
	}

	bool Creature::ReadItem(std::map<ID, std::shared_ptr<Item>>::iterator itemIt)
	{
		return true;
	}

	bool Creature::CanEnterPosition(Floor *floorPtr)
	{
		//! @note 移動先の情報が無効(NULL)ならば FALSE		
		if(!floorPtr) return false;
		//! @note 移動先が壁地形ならば FALSE
		if(floorPtr->IsWall()) return false;
		return true;
	}

	void Creature::UpdateFieldLore(void)
	{
		MAP_LENGTH x, y, cx, cy;
		std::vector<Coordinates> rangeVec;
		std::vector<Coordinates>::iterator rangeIt;
		if(fieldPtr == NULL) return;

		cx = this->position.GetX();
		cy = this->position.GetY();

		fieldPtr->GetSight(this->sightList, this->sightRange, cx, cy);
		for(y = -this->sightRange; y < this->sightRange; y++)
		{
			for(x = -this->sightRange; x < this->sightRange; x++)
			{
				if(this->sightList[(y + this->sightRange) * (this->sightRange * 2 + 1) + x + this->sightRange]) this->GetLore()->PutFieldLore(0, cx + x, cy + y); 
			}
		}
	}

	void Creature::XMLSave(std::string filename)
	{
		std::ofstream ofs(filename);
		assert(ofs);
		//boost::archive::xml_oarchive oa(ofs);
		//oa << boost::serialization::make_nvp("Creature", *this);
	}

	bool Creature::InSight(MAP_LENGTH x, MAP_LENGTH y)
	{
		MAP_LENGTH rx = x - this->position.GetX();
		MAP_LENGTH ry = y - this->position.GetY();
		if(rx > this->sightRange || ry > this->sightRange || rx < -this->sightRange || ry < -this->sightRange)
		{
			return false;
		}

		if(x < 0 || y < 0 || x >= fieldPtr->GetWidth() || y >= fieldPtr->GetHeight())
		{
			return false;
		}

		return this->sightList[(ry + this->sightRange) * (this->sightRange * 2 + 1) + rx + this->sightRange];
	}

	ACTION_RESULT Creature::Walk(DIRECTION direction)
	{
		Coordinates nextPoint = this->position + Direction::DirectionVector[direction];
		Square *nextSquarePtr = fieldPtr->GetSquare(nextPoint);
		Floor *floorPtr;
		Creature *targetPtr;

		if(!nextSquarePtr)
		{
			GameElement::GameSurfacePtr->GameMessage("時空の壁に行く手を阻まれている。");
			return AR_FAILURE;
		}

		//! @note 移動先に別のクリーチャーが存在するならば白兵処理に移行して FALSE
		targetPtr = GameElement::GameWorldPtr->GetCreatureFromPosition(this->GetFieldID(), &nextPoint);
		if(targetPtr)
		{
			this->Melee(targetPtr);
			GameElement::GameLoggerPtr->CreaturesCombated(this->GetName(), targetPtr->GetName());
			return AR_FAILURE;
		}

		floorPtr = nextSquarePtr->GetFloorPtr();
		if(!this->CanEnterPosition(floorPtr))
		{
			GameElement::GameSurfacePtr->GameMessage("行く手を阻まれている。");
			return AR_FAILURE;
		}

		this->position = nextPoint;
		this->UpdateFieldLore();
		GameElement::GameSurfacePtr->GameMessage("");
		return AR_SUCCESS;
	}

	ACTION_RESULT Creature::Open(DIRECTION direction)
	{
		// TODO
		return AR_SUCCESS;
	}

	ACTION_RESULT Creature::Close(DIRECTION direction)
	{
		// TODO
		return AR_SUCCESS;
	}

	ACTION_RESULT Creature::Disarm(DIRECTION direction)
	{
		// TODO
		return AR_SUCCESS;
	}

	PLAY_EXIT_CODE Creature::DoGameCommand(GAME_COMMAND command)
	{
		CREATURE_IT playerIt = GameElement::GameWorldPtr->GetPlayerCreature();

		switch(command)
		{
		case GAME_COMMAND_VIEW_PLAYER_STATUS:
			GameElement::GameSurfacePtr->ViewCreatureStatus(GameElement::GameWorldPtr->GetPlayerCreature()->second.get());
			break;
		case GAME_COMMAND_DEBUG_XML_SAVE:
			GameElement::GameWorldPtr->XMLSave();
			GameElement::GameSurfacePtr->SystemMessage("XMLに保存しました。");
			break;
		case GAME_COMMAND_DEBUG_XML_LOAD:
			GameElement::GameSurfacePtr->Draw(playerIt);
			GameElement::GameWorldPtr->XMLLoad();
			GameElement::GameSurfacePtr->SystemMessage("XMLから呼び出しました。");
			break;
		case GAME_COMMAND_EXIT:
			return PLAY_EXIT_QUIT;
			break;
		case GAME_COMMAND_NORTH:
			playerIt->second.get()->Walk(DIRECTION_NORTH);
			break;
		case GAME_COMMAND_NORTH_EAST:
			playerIt->second.get()->Walk(DIRECTION_NORTH_EAST);
			break;
		case GAME_COMMAND_EAST:
			playerIt->second.get()->Walk(DIRECTION_EAST);
			break;
		case GAME_COMMAND_SOUTH_EAST:
			playerIt->second.get()->Walk(DIRECTION_SOUTH_EAST);
			break;
		case GAME_COMMAND_SOUTH:
			playerIt->second.get()->Walk(DIRECTION_SOUTH);
			break;
		case GAME_COMMAND_SOUTH_WEST:
			playerIt->second.get()->Walk(DIRECTION_SOUTH_WEST);
			break;
		case GAME_COMMAND_WEST:
			playerIt->second.get()->Walk(DIRECTION_WEST);
			break;
		case GAME_COMMAND_NORTH_WEST:
			playerIt->second.get()->Walk(DIRECTION_NORTH_WEST);
			break;
		}
		return PLAY_EXIT_NONE;
	}


}