/*************************************************************************
Crytek Source File.
Copyright (C), Crytek Studios, 2001-2006.
-------------------------------------------------------------------------
$Id$
$DateTime$

-------------------------------------------------------------------------
History:
- 23:3:2006   13:05 : Created by Márcio Martins

*************************************************************************/
#include "StdAfx.h"
#include "Melee.h"
#include "Game.h"
#include "Item.h"
#include "Weapon.h"
#include "GameRules.h"
#include <IEntitySystem.h>

#include "IRenderer.h"
#include "IRenderAuxGeom.h"	


//std::vector<Vec3> g_points;


//------------------------------------------------------------------------
CMelee::CMelee()
{
}

//------------------------------------------------------------------------
CMelee::~CMelee()
{
}

void CMelee::Init(IWeapon *pWeapon, const struct IItemParamsNode *params)
{
	m_pWeapon = static_cast<CWeapon *>(pWeapon);

	if (params)
		ResetParams(params);

	m_attacking = false;
	m_delayTimer=0.0f;
	m_durationTimer=0.0f;
}

//------------------------------------------------------------------------
void CMelee::Update(float frameTime, uint frameId)
{
	FUNCTION_PROFILER( GetISystem(), PROFILE_GAME );

	if (m_attacking)
	{
		if (m_delayTimer>0.0f)
		{
			m_delayTimer-=frameTime;
			if (m_delayTimer<=0.0f)
			{
				m_delayTimer=0.0f;
				m_last_pos.zero();
			}
		}
		else if (m_durationTimer>0.0f)
		{
			m_durationTimer-=frameTime;
			if (m_durationTimer<=0.0f)
				m_durationTimer=0.0f;

			Vec3 localPos=m_pWeapon->GetSlotHelperPos(CItem::eIGS_FirstPerson, m_meleeparams.helper.c_str(), false);
			
			if (m_last_pos.len2()>0.0f)
			{
				Vec3 origin=m_pWeapon->GetEntity()->GetWorldTM().TransformPoint(m_last_pos);
				Vec3 dir=(m_pWeapon->GetEntity()->GetWorldTM().TransformPoint(localPos)-origin).normalized()*0.25f;
				Vec3 offset(0,0,0);

				if (m_meleeparams.offset!=0.0f)
				{
					CActor *pActor = m_pWeapon->GetOwnerActor();
					IMovementController * pMC = pActor ? pActor->GetMovementController() : 0;
					if (pMC)
					{
						SMovementState info;
						pMC->GetMovementState(info);

						offset = info.aimDirection*m_meleeparams.offset;
					}
				}

				IPhysicalEntity *pIgnore = m_pWeapon->GetOwner() ? m_pWeapon->GetOwner()->GetPhysics():0;

				bool valid=false;
				ray_hit hit;
				if (GetISystem()->GetIPhysicalWorld()->RayWorldIntersection(origin, dir, ent_all,
					rwi_stop_at_pierceable|rwi_ignore_back_faces,&hit, 1, pIgnore) && hit.n.Dot(dir)<0.0f)
					valid=true;

				//GetISystem()->GetIRenderer()->GetIRenderAuxGeom()->DrawSphere(origin+dir, 0.15f, ColorB(0, 255, 0, 255));

				if (GetISystem()->GetIPhysicalWorld()->RayWorldIntersection(origin+offset*0.5, dir, ent_all,
					rwi_stop_at_pierceable|rwi_ignore_back_faces,&hit, 1, pIgnore) && hit.n.Dot(dir)<0.0f)
					valid=true;

				//GetISystem()->GetIRenderer()->GetIRenderAuxGeom()->DrawSphere(origin+(offset*0.5)+dir, 0.15f, ColorB(0, 168, 0, 255));

				if (!valid && GetISystem()->GetIPhysicalWorld()->RayWorldIntersection(origin+offset, dir, ent_all,
					rwi_stop_at_pierceable|rwi_ignore_back_faces,&hit, 1, pIgnore) && hit.n.Dot(dir)<0.0f)
					valid=true;

				//GetISystem()->GetIRenderer()->GetIRenderAuxGeom()->DrawSphere(origin+offset+dir, 0.15f, ColorB(0, 64, 0, 255));

				if (valid)
				{
					dir.normalize();
					Hit(hit, dir);
					m_durationTimer=0.0f; // only one hit per attack
				}
			}

			m_last_pos=localPos;
		}
	}

	/*if (!g_points.empty())
	{
		for(std::vector<Vec3>::iterator it=g_points.begin();it!=g_points.end();++it)
			GetISystem()->GetIRenderer()->GetIRenderAuxGeom()->DrawSphere(*it, 0.35f, ColorB(255, 128, 0, 255));
	}
	/*/
}

//------------------------------------------------------------------------
void CMelee::Destroy()
{
}

//------------------------------------------------------------------------
void CMelee::ResetParams(const struct IItemParamsNode *params)
{
	const IItemParamsNode *melee = params?params->GetChild("melee"):0;
	const IItemParamsNode *actions = params?params->GetChild("actions"):0;

	m_meleeparams.Reset(melee);
	m_meleeactions.Reset(actions);
}

//------------------------------------------------------------------------
void CMelee::PatchParams(const struct IItemParamsNode *patch)
{
	const IItemParamsNode *melee = patch->GetChild("melee");
	const IItemParamsNode *actions = patch->GetChild("actions");

	m_meleeparams.Reset(melee, false);
	m_meleeactions.Reset(actions, false);
}

//------------------------------------------------------------------------
void CMelee::Activate(bool activate)
{
	m_attacking = false;
	m_delayTimer=0.0f;
	m_durationTimer=0.0f;
}

//------------------------------------------------------------------------
bool CMelee::CanFire(bool considerAmmo) const
{
	return !m_attacking;
}

//------------------------------------------------------------------------
void CMelee::StartFire(EntityId shooterId)
{
	if (!CanFire())
		return;

	m_attacking = true;
	m_pWeapon->ExitZoom();
	m_pWeapon->PlayAction(m_meleeactions.attack.c_str());
	m_pWeapon->SetBusy(true);
	
	struct StopAttackingAction : public ISchedulerAction
	{
		CMelee *_this;
		StopAttackingAction(CMelee *melee): _this(melee) {};
		void execute(CItem *pItem)
		{
			_this->m_attacking = false;

			_this->m_delayTimer = 0.0f;
			_this->m_durationTimer = 0.0f;
			pItem->SetBusy(false);
		}
	};

	m_pWeapon->GetScheduler()->TimerAction(m_pWeapon->GetCurrentAnimationTime(CItem::eIGS_FirstPerson), new StopAttackingAction(this), true);

	m_delayTimer = m_meleeparams.delay;
	m_durationTimer = m_meleeparams.duration;
}

//------------------------------------------------------------------------
void CMelee::StopFire(EntityId shooterId)
{
}

//------------------------------------------------------------------------
void CMelee::NetShoot(const Vec3 &hit)
{
}

//------------------------------------------------------------------------
const char *CMelee::GetType() const
{
	return "melee";
}

//------------------------------------------------------------------------
const char *CMelee::GetAmmoType() const
{
	return "";
}

//------------------------------------------------------------------------
int CMelee::GetDamage() const
{
	return m_meleeparams.damage;
}

//------------------------------------------------------------------------
void CMelee::Hit(ray_hit &hit, const Vec3 &dir)
{
	float strength = m_pWeapon->GetOwnerActor()?m_pWeapon->GetOwnerActor()->GetActorStrength():1.0f;

	IEntity *pTarget = GetISystem()->GetIEntitySystem()->GetEntityFromPhysics(hit.pCollider);

	CGameRules *pGameRules = g_pGame->GetGameRules();

	CGameRules::HitInfo info(m_pWeapon->GetOwnerId(), pTarget?pTarget->GetId():0, m_pWeapon->GetEntityId(),
		m_meleeparams.damage*strength, 0.0f, pGameRules->GetHitMaterialIdFromSurfaceId(hit.surface_idx), hit.partid,
		pGameRules->GetHitTypeId("melee"), hit.pt, dir, hit.n);

	if (hit.pCollider && m_meleeparams.impulse>0.001f)
	{
		pe_action_impulse ai;
		ai.partid = hit.partid;
		ai.ipart = hit.ipart;
		ai.point = hit.pt;
		ai.iApplyTime = 0;
		ai.impulse = dir*(m_meleeparams.impulse*strength);
		hit.pCollider->Action(&ai);
	}

//	g_points.push_back(hit.pt);

	pGameRules->ClientHit(info);
}
