/*  EQEMu:  Everquest Server Emulator
Copyright (C) 2001-2002  EQEMu Development Team (http://eqemu.org)

  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; version 2 of the License.
  
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY except by those people which sell it, which
	are required to give you total support for your newly bought product;
	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, write to the Free Software
	  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
#include "../common/debug.h"
#include "groups.h"
#include "entity.h"
#include "PlayerCorpse.h"
#include "object.h"
#include "doors.h"
#include "client.h"
#include "NpcAI.h"
#include "../common/packet_functions.h"
#include "../common/packet_dump.h"
#include "worldserver.h"
extern EntityList entity_list;
extern WorldServer worldserver;

#ifdef GUILDWARS
#include "StringIDs.h"
#include "GuildWars.h"
extern GuildLocationList location_list;
extern GuildWars guildwars;
#include "../common/guilds.h"
extern GuildRanks_Struct guilds[512];
#endif

//
// Xorlac: This will need proper synchronization to make it work correctly.
//         Also, should investigate client ack for packet to ensure proper synch.
//

Group::Group(Mob* leader)
{
	memset(members,0,sizeof(Mob*) * MAX_GROUP_MEMBERS);
	members[0] = leader;
    leader->CastToClient()->isgrouped = true;
	SetLeader(leader);
	int i;
	for(i=0;i<MAX_GROUP_MEMBERS;i++)
		memset(membername[i],0,64);
	strcpy(membername[0],leader->GetName());
	strcpy(leader->CastToClient()->GetPP().groupMembers[0],leader->GetName());
}
Group::Group(SendGroup_Struct* sgs)
{
	memset(members,0,sizeof(Mob*) * MAX_GROUP_MEMBERS);
	int i;
	for(i=0;i<MAX_GROUP_MEMBERS;i++)
		memset(membername[i],0,64);
	for(i=1;i<sgs->grouptotal;i++){	
		strcpy(membername[i],sgs->members[i]);
	}
	strcpy(membername[0],sgs->leader);
}
bool Group::AddMember(Mob* newmember)
{
	int i=0;
    for (i = 0; i < MAX_GROUP_MEMBERS; i++) {
        if (members[i] == NULL) {
            members[i] = newmember;
            break;
        }
    }
	if ((i == MAX_GROUP_MEMBERS) || (!newmember->IsClient()))
		return false;
	strcpy(membername[i],newmember->GetName());
	int x=1;
	APPLAYER* outapp = new APPLAYER(OP_GroupUpdate,sizeof(GroupJoin_Struct));
	GroupJoin_Struct* gj = (GroupJoin_Struct*) outapp->pBuffer;	
	strcpy(gj->membername, newmember->GetName());
	gj->action = 0;      
	for (i = 0;i < MAX_GROUP_MEMBERS; i++) {
		if (members[i] != NULL && members[i] != newmember) {
			strcpy(gj->yourname,members[i]->GetName());		
			members[i]->CastToClient()->QueuePacket(outapp);
			strcpy(members[i]->CastToClient()->GetPP().groupMembers[this->GroupCount()-1],newmember->GetName());
			if(IsLeader(members[i])){
				strcpy(newmember->CastToClient()->GetPP().groupMembers[0],members[i]->GetName());
			}
			else{
				strcpy(newmember->CastToClient()->GetPP().groupMembers[x],members[i]->GetName());
				x++;
			}
		}
	}
	strcpy(newmember->CastToClient()->GetPP().groupMembers[x],newmember->GetName());
	newmember->CastToClient()->Save();
    newmember->isgrouped = true;	
    safe_delete(outapp);
	return true;
}
void Group::SendUpdate(int32 type,Mob* member){
	if(!member->IsClient())
		return;
	APPLAYER* outapp = new APPLAYER(OP_GroupUpdate,sizeof(GroupUpdate_Struct));
	GroupUpdate_Struct* gu = (GroupUpdate_Struct*)outapp->pBuffer;	
	gu->action=type;
	strcpy(gu->yourname,member->GetName());
	int x=0;
	int i=0;
	for (i = 0;i < MAX_GROUP_MEMBERS; i++) {
		if (members[i] != NULL && members[i] != member) {
			if(IsLeader(members[i])){
				strcpy(gu->leadersname,members[i]->GetName());
				strcpy(gu->membername[x],members[i]->GetName());
				x++;
			}
			else{
				strcpy(gu->membername[x],members[i]->GetName());
				x++;
			}
		}
	}
	member->CastToClient()->QueuePacket(outapp);
	safe_delete(outapp);
}
//
// Xorlac: Does this consider side effects of being the last member to disband in a group, leadership changes, etc?
//
void Group::SendWorldGroup(int32 zone_id,Mob* zoningmember){
	ServerPacket* pack = new ServerPacket(ServerOP_SendGroup,sizeof(SendGroup_Struct));
	SendGroup_Struct* sgs=(SendGroup_Struct*)pack->pBuffer;
	sgs->grouptotal=GroupCount();
	sgs->zoneid=zone_id;
	strcpy(sgs->thismember,zoningmember->GetName());
	strcpy(sgs->leader,GetLeaderName());
	int i=0;
	for (i = 0; i < MAX_GROUP_MEMBERS; i++)
    {
        if (strlen(membername[i])>1)
        {
			strcpy(sgs->members[i],membername[i]);
        }
    }
    pack->Deflate();
	bool ret = worldserver.SendPacket(pack);
	safe_delete(pack);
}
void Group::SendHPPackets(Mob* member){
	if(!member)
		return;
	APPLAYER* hpapp = new APPLAYER(OP_HPUpdate, sizeof(SpawnHPUpdate_Struct));
	int i=0;
	for (i = 0; i < MAX_GROUP_MEMBERS; i++)
    {
		if(strlen(membername[i])>1){
			Client* gmember=entity_list.GetClientByName(membername[i]);
			if (gmember)
			{
				gmember->CreateHPPacket(hpapp,false); 
				member->CastToClient()->QueuePacket(hpapp);
			}
		}
    }
	safe_delete(hpapp);	
}
bool Group::UpdatePlayer(Mob* update){
	int i=0;
	for (i = 0; i < MAX_GROUP_MEMBERS; i++)
    {
        if (!strcmp(membername[i],update->GetName()))
        {
            members[i] = update;
			members[i]->isgrouped=true;
            return true;
        }
    }
	return false;
}
void Group::Remove(Mob* removemob){
	int i;

	if (removemob == NULL)
    {
		return;
    }

    for (i = 0; i < MAX_GROUP_MEMBERS; i++)
    {
        if (members[i] == removemob)
        {
            members[i] = NULL;
            break;
        }
    }
}
bool Group::DelMember(Mob* oldmember,bool ignoresender){
	int i;

	if (oldmember == NULL)
    {
		return false;
    }

    for (i = 0; i < MAX_GROUP_MEMBERS; i++)
    {
        if (members[i] == oldmember)
        {
            members[i] = NULL;
            break;
        }
    }
    memset(membername[i],0,64);
	APPLAYER* outapp = new APPLAYER(OP_GroupUpdate,sizeof(GroupJoin_Struct));

	GroupJoin_Struct* gu = (GroupJoin_Struct*) outapp->pBuffer;
	gu->action = 6;
	strcpy(gu->membername, oldmember->GetName());

	for (i = 0; i < MAX_GROUP_MEMBERS; i++)
    {
        if (members[i] == NULL) {
                //if (DEBUG>=5) LogFile->write(EQEMuLog::Debug, "Group::DelMember() null member at slot %i", i);
                continue;
        }
        if (members[i] != oldmember && members[i]->IsClient()) {
			strcpy(gu->yourname, members[i]->GetName());
            members[i]->CastToClient()->QueuePacket(outapp);
		}
		#ifdef IPC
		if(members[i] == oldmember && members[i]->IsNPC() && members[i]->CastToNPC()->IsGrouped() && members[i]->CastToNPC()->IsInteractive()) {
		    members[i]->CastToNPC()->TakenAction(23,0);
        }
        #endif	
     }

	if (!ignoresender && oldmember->IsClient()) {
		strcpy(gu->yourname,oldmember->GetName());
		strcpy(gu->membername,oldmember->GetName());
		gu->action = 6;

	    oldmember->CastToClient()->QueuePacket(outapp);
    }

	oldmember->isgrouped = false;
	disbandcheck = true;

    safe_delete(outapp);
	return true;	
}

void Group::TeleportGroup(Mob* sender, int32 zoneID, float x, float y, float z)
{
    for (int i = 0; i < MAX_GROUP_MEMBERS; i++)
    {
    #ifdef IPC
		if (members[i] != NULL && (members[i]->IsClient() || (members[i]->IsNPC() && members[i]->CastToNPC()->IsInteractive())) && members[i] != sender) {
    #else
        if (members[i] != NULL && members[i]->IsClient() && members[i] != sender) {
    #endif
        members[i]->CastToClient()->MovePC(zoneID, x, y, z);
		}
	}	
}
void Group::DisbandGroup(){
	APPLAYER* outapp = new APPLAYER(OP_GroupUpdate,sizeof(GroupUpdate_Struct));

	GroupUpdate_Struct* gu = (GroupUpdate_Struct*) outapp->pBuffer;
	gu->action = 6;
	
    for (int i = 0; i < MAX_GROUP_MEMBERS; i++)
    {
        if (members[i] == NULL)
                continue;
		if (members[i]->IsClient()) {
			strcpy(gu->yourname, members[i]->GetName());
            members[i]->CastToClient()->QueuePacket(outapp);
		}

		   

        members[i]->isgrouped = false;
	}	

	entity_list.RemoveGroup(this->GetID());

    safe_delete(outapp);
}
void Group::CastGroupSpell(Mob* caster, uint16 spellid)
{
	if(!caster)
		return;

	castspell = true;

    for (int z=0; z < MAX_GROUP_MEMBERS; z++)
    {
		if (members[z] != NULL && members[z] != caster)
        {
	        members[z]->SpellOnTarget(spellid,members[z]);
		}
	}	
	caster->SpellOnTarget(spellid,caster);
	castspell = false;
	disbandcheck = true;
}

void Group::SplitExp(uint32 exp, int16 otherlevel)
{
	int i;
	uint32 groupexp = exp;
	int8 membercount = 0;
	int8 maxlevel = 1;
	for (i = 0; i < MAX_GROUP_MEMBERS; i++)
	{
		if (members[i] != NULL)
        {
			if(members[i]->GetLevel() > maxlevel)
				maxlevel = members[i]->GetLevel();
			//groupexp += exp/10;
			groupexp += (uint32)(exp * zone->GetGroupEXPBonus());

			membercount++;
		}
	}

	if (membercount == 0)
		return;

	for (i = 0; i < MAX_GROUP_MEMBERS; i++)
	{
		if (members[i] != NULL && members[i]->IsClient())
    		{
            //
	    // add exp + exp cap
            //
			sint16 diff = members[i]->GetLevel() - maxlevel;
            if (diff >= -8) /*Instead of person who killed the mob, the person who has the highest level in the group*/{
			    members[i]->CastToClient()->AddEXP(((members[i]->GetLevel()+3) * (members[i]->GetLevel()+3) * 75*3.5f < groupexp/membercount ) ? (int32)(members[i]->GetLevel() * members[i]->GetLevel() * 75*3.5f):(int32)(groupexp/membercount) );
		    }
		}
	}	
}

bool Group::Process()
{
if(disbandcheck && !GroupCount())
	return false;
else if(disbandcheck && GroupCount())
	disbandcheck = false;
return true;
}

bool Group::IsGroupMember(Mob* client)
{
	for (int i = 0; i < MAX_GROUP_MEMBERS; i++)
    {
		if (members[i] == client)
        {
			return true;
        }
	}

	return false;
}

int8 Group::GroupCount()
{
int count = 0;
	for (int i = 0; i < MAX_GROUP_MEMBERS; i++)
    {
		if (strlen(membername[i])>0)
        {
			count++;
        }
	}

	return count;
}

int32 Group::GetHighestLevel()
{
int32 level = 0;
	for (int i = 0; i < MAX_GROUP_MEMBERS; i++)
    {
		if (members[i])
        {
			if(members[i]->GetLevel() > level)
				level = members[i]->GetLevel();
        }
	}
	return level;
}

void Group::GroupMessage(Mob* sender,const char* message)
{
	for (int i = 0; i < MAX_GROUP_MEMBERS; i++) {
		if(!members[i]){
			if(strlen(membername[i])>1){
				worldserver.SendChannelMessage(sender->CastToClient(), membername[i], 2, 0, 0, message);
			}
			continue;
		}

		if (members[i]->IsClient() && members[i]->CastToClient()->GetFilter(FILTER_GROUP)!=0)
			members[i]->CastToClient()->ChannelMessageSend(sender->GetName(),members[i]->GetName(),2,0,message);
		#ifdef IPC
		if (members[i]->CastToNPC()->IsInteractive() && members[i] != sender)
			members[i]->CastToNPC()->InteractiveChat(2,1,message,(sender->GetTarget() != NULL) ? sender->GetTarget()->GetName():sender->GetName(),sender);
	      	//InteractiveChat(int8 chan_num, int8 language, const char * message, const char* targetname,Mob* sender);
  	    #endif
	}	
}

int32 Group::GetTotalGroupDamage(Mob* other) {
    int32 total = 0;
	
	for (int i = 0; i < MAX_GROUP_MEMBERS; i++) {
		if(!members[i])
			continue;
		if (other->CheckAggro(members[i]))
			total += other->GetHateAmount(members[i],true);
	}
	return total;
}

#ifdef GUILDWARS
void Group::CauseEXPLoss() {
for(int i=0;i<MAX_GROUP_MEMBERS;i++)
{
if(!members[i])
continue;
else if(members[i]->IsClient())
members[i]->CastToClient()->SetEXP((int32)(members[i]->CastToClient()->GetEXP() - members[i]->GetLevel()*((float)members[i]->GetLevel()/18)*1000 > 0)? (int32)(members[i]->CastToClient()->GetEXP() - members[i]->GetLevel()*((float)members[i]->GetLevel()/18)*1000) : 1,members[i]->CastToClient()->GetAAXP());
}
}

void Group::CauseFactionLoss(Mob* killed) {
	int32 dbid = 0;
	if(killed->IsClient())
		dbid = killed->CastToClient()->GuildDBID();
	else
		dbid = killed->CastToNPC()->GetGuildID();
for(int i=0;i<MAX_GROUP_MEMBERS;i++)
{
if(!members[i])
continue;
else if(members[i]->IsClient())
{
				sint32 sub = guildwars.CalculateFactionDecrease(killed,members[i]);
				sint32 add = guildwars.CalculateFactionIncrease(killed,members[i]);
				database.KillFactionModify(members[i]->CastToClient()->CastToClient(),members[i]->CastToClient()->CastToClient()->GuildDBID(),dbid,sub,add);
				database.ModifyGuildFaction(members[i]->CastToClient()->CastToClient()->GuildDBID(),dbid,sub);
				members[i]->CastToClient()->Message_StringID(0,FACTION_WORSE,guilds[database.GetGuildEQID(dbid)].name);
}
}
}

void Group::GivePoints(sint32 points) {
for(int i=0;i<MAX_GROUP_MEMBERS;i++)
{
if(!members[i])
continue;
else if(members[i]->IsClient())
{
				members[i]->CastToClient()->UpdateLDoNPoints(points,0);
				
				char* pts = 0;
				sprintf(pts,"%i",points);
				members[i]->CastToClient()->Message_StringID(0,ADVENTURE_COMPLETE,pts);
}
}
}
#endif
