
#include "WorldPacket.h"
#include "WorldSession.h"
#include "WorldSocket.h"

WorldSocket::WorldSocket(SocketHandler &h, WorldSession *s) : TcpSocket(h)
{
    _session = s;
    _gothdr = false;
    _ok=false;
}

bool WorldSocket::IsOk(void)
{
    return _ok;
}

void WorldSocket::OnConnect()
{
    log("Connected to world server.");
    _ok = true;
}

void WorldSocket::OnConnectFailed()
{
    logerror("Connecting to World Server failed!");
    _ok = false;
}

void WorldSocket::OnDelete()
{
    log("Connection to world server has been closed.");
    _ok = false;
    _session->SetMustDie(); // recreate the session if needed
}

void WorldSocket::OnException()
{
    if(_ok)
    {
        logerror("WorldSocket::OnException()");
        _ok = false;
    }
}

void WorldSocket::OnRead()
{
    TcpSocket::OnRead();
    if(!ibuf.GetLength())
    {
        this->CloseAndDelete();
        return;
    }
    while(ibuf.GetLength() > 0) // when all packets from the current ibuf are transformed into WorldPackets the remaining len will be zero
    {

        if(_gothdr) // already got header, this packet has to be the data part
        {
            ASSERT(_remaining > 0); // case pktsize==0 is handled below
            if(ibuf.GetLength() < _remaining)
            {
                DEBUG(logdebug("Delaying WorldPacket generation, bufsize is %u but should be >= %u",ibuf.GetLength(),_remaining));
                break;
            }
            _gothdr=false;
            WorldPacket *wp = new WorldPacket(_remaining);
            wp->resize(_remaining);
            ibuf.Read((char*)wp->contents(),_remaining);
            wp->SetOpcode(_opcode);
            GetSession()->AddToPktQueue(wp);
        }
        else // no pending header stored, so this packet must be a header
        {
            if(ibuf.GetLength() < sizeof(ServerPktHeader))
            {
                DEBUG(logdebug("Delaying header reading, bufsize is %u but should be >= %u",ibuf.GetLength(),sizeof(ServerPktHeader)));
                break;
            }
            ServerPktHeader hdr;
            ibuf.Read((char*)&hdr,sizeof(ServerPktHeader));
            _crypt.DecryptRecv((uint8*)&hdr,sizeof(ServerPktHeader));
            _remaining = ntohs(hdr.size)-2;
            _opcode = hdr.cmd;
            if(_opcode > 1000) // no opcode has yet a number over 1000
            {
                logcritical("CRYPT ERROR: opcode=%u, remain=%u",_opcode,_remaining); // this should never be the case!
                GetSession()->GetInstance()->SetError(); // no way to recover the crypt, must exit
                // if the crypt gets messy its hardly possible to recover it, especially if we dont know
                // the lentgh of the following data part
                // TODO: invent some way how to recover the crypt (reconnect?)
                return;
            }

            // the header is fine, now check if there are more data
            if(_remaining == 0) // this is a packet with no data (like CMSG_NULL_ACTION)
            {
                WorldPacket *wp = new WorldPacket;
                wp->SetOpcode(_opcode);
                GetSession()->AddToPktQueue(wp);
            }
            else // there is a data part to fetch
            {
                _gothdr=true; // only got the header, next packet will contain the data
            }
        }
    }
}

void WorldSocket::SendWorldPacket(WorldPacket &pkt)
{
    if(!_ok)
        return;
    ClientPktHeader hdr;
    memset(&hdr,0,sizeof(ClientPktHeader));
    hdr.size = ntohs(pkt.size()+4);
    hdr.cmd = pkt.GetOpcode();
    _crypt.EncryptSend((uint8*)&hdr, 6);
    ByteBuffer final(pkt.size()+6);
    final.append((uint8*)&hdr,sizeof(ClientPktHeader));
    if(pkt.size())
        final.append(pkt.contents(),pkt.size());
    SendBuf((char*)final.contents(),final.size());
}

void WorldSocket::InitCrypt(uint8 *key,uint32 len)
{
    _crypt.SetKey(key,len);
    _crypt.Init();
}
