#include "TitanPeeringNetwork.h"
#include "Titan.h"
#include "registry.h"

    /////////////////////////////////////////////////////////////////////////////////////////////////////
    // TitanPeeringNetwork
    /////////////////////////////////////////////////////////////////////////////////////////////////////

TitanPeeringNetwork::TitanPeeringNetwork(Titan *titan)
{
    m_titan = titan;
    m_api = NULL;
    m_waitingForJoinReply = false;
    m_hackAuthenticated = false;
    m_hackGameListArrived = false;
    m_terminated = false;
    m_objectManager = new PeeringNetwork::ObjectManager(this);
}

TitanPeeringNetwork::~TitanPeeringNetwork()
{
    delete m_api;
    delete m_objectManager;
}

void TitanPeeringNetwork::SetPeeringNetwork(/*gift*/ PeeringNetwork::PeeringServerApi *api)
{
    SyAssert(m_api == NULL);
    m_api = api;
    m_api->SetHandler(this);
}

void TitanPeeringNetwork::GiveTime()
{
    if (m_api != NULL)
    {
        m_api->GiveTime();
        m_objectManager->GiveTime(m_titan->GetTime());
    }
}

void TitanPeeringNetwork::GameCreate(const char *gameName)
{
    SyAssert(m_api != NULL);
    m_api->RequestGameCreate(gameName);
    WaitForJoinReply();
}

void TitanPeeringNetwork::GameJoin(int gameId)
{
    SyAssert(m_api != NULL);
    m_api->RequestGameJoin(gameId);
    WaitForJoinReply();
}

void TitanPeeringNetwork::Flush()
{
  if (m_api == NULL) return;
  m_api->Flush();
}

int  TitanPeeringNetwork::GetPeerID()
{
  if (m_api == NULL) return 0;
  return m_api->GetSelfPeerId();
}

void TitanPeeringNetwork::WaitForJoinReply()
{
        // normally I wouldn't block like this, but for now I am going to hack it in this way.  Eventually we will
        // want to have the OnGameJoined function notify the application that it can now start creating object if it wants
        // By blocking here, we allow the application to simply call GameJoin followed by LoadXml (if they are the game creator)
    m_waitingForJoinReply = true;
    while (m_waitingForJoinReply)
    {
        m_api->GiveTime();
    }
}

void TitanPeeringNetwork::HackStartupNetwork(const char *playerName, const char *gameName, const char *Filename, const char *recordFilename, const char *playbackFilename)
{
    SyStr::Strncpy(m_hackFilename, Filename, sizeof(m_hackFilename));
    SyStr::Strncpy(m_hackGameName, gameName, sizeof(m_hackGameName));

    m_hackAuthenticated = false;
    m_hackGameListArrived = false;
    m_terminated = false;

    m_api = new PeeringNetwork::PeeringServerApi(this, "64.37.154.161", playerName, "none", 0, recordFilename, playbackFilename);

        ///////////////////////////////
        // wait until authenticated
        ///////////////////////////////
    uint64 start = m_titan->GetTime();
    while (!m_hackAuthenticated && (m_titan->GetTime() - start < 10000) && !m_terminated)
    {
        GiveTime();
    }

    if (!m_hackAuthenticated)
    {
            // failure, so kill api and just run solo
        delete m_api;
        m_api = NULL;
        m_titan->LoadLevel(m_hackFilename);
        return;
    }

    m_api->RequestGameList();

        ////////////////////////////////////////////////////////////////////////////////////////////////
        // waiting for game list to arrive (can't do it in the event lest we recursively call GiveTime)
        ////////////////////////////////////////////////////////////////////////////////////////////////
    start = m_titan->GetTime();
    while (!m_hackGameListArrived && (m_titan->GetTime() - start < 10000) && !m_terminated)
    {
        GiveTime();
    }

    if (!m_hackGameListArrived)
    {
            // failure, so kill api and just run solo
        delete m_api;
        m_api = NULL;
        m_titan->LoadLevel(m_hackFilename);
        return;
    }


        // join or create a game as appropriate
    const PeeringNetwork::GameList *gameList = m_api->GetGameList();
    const PeeringNetwork::PacketGameEntry *entry = gameList->First();
    while (entry != NULL)
    {
        if (SyStr::stricmp(entry->m_gameName, m_hackGameName) == 0)
        {
            PeeringNetwork::PeeringServerApi *api = m_api;
            m_api = NULL;
            m_titan->NetworkGameJoin(api, entry->m_gameId);
            return;
        }

        entry = gameList->Next(entry);
    }

        // create a new game
    PeeringNetwork::PeeringServerApi *holdapi = m_api;
    m_api = NULL;
    m_titan->NetworkGameCreate(holdapi, m_hackGameName);
    m_titan->LoadLevel(m_hackFilename);
}

int TitanPeeringNetwork::ObjectReserveId()
{
    return(m_objectManager->ObjectReserveId());
}

void TitanPeeringNetwork::ObjectCreate(int typeId, int objectId, bool ownerLifetime, const char *fullState, int fullStateLen)
{
    m_objectManager->ObjectCreate(typeId, objectId, ownerLifetime, fullState, fullStateLen);
}

void TitanPeeringNetwork::ObjectDestroy(int objectId)
{
    m_objectManager->ObjectDestroy(objectId);
}

void TitanPeeringNetwork::ObjectBroadcast(int objectId, const char *data, int dataLen)
{
    m_objectManager->ObjectBroadcast(objectId, data, dataLen);
}

void TitanPeeringNetwork::ObjectMessage(int objectId, const char *data, int dataLen)
{
    m_objectManager->ObjectMessage(objectId, data, dataLen);
}

void TitanPeeringNetwork::ObjectTransferOwnership(int objectId, int destPeerId)
{
    m_objectManager->ObjectTransferOwnership(objectId, destPeerId);
}

void TitanPeeringNetwork::ObjectRequestOwnership(int objectId)
{
    m_objectManager->ObjectRequestOwnership(objectId);
}

bool TitanPeeringNetwork::ObjectIsOwner(int objectId)
{
   return m_objectManager->ObjectIsOwner(objectId);
}



    //////////////////////////////////
    // api handlers
    //////////////////////////////////

void TitanPeeringNetwork::OnTerminated(PeeringNetwork::PeeringServerApi *api)
{
    m_waitingForJoinReply = false;
    m_terminated = true;
}

void TitanPeeringNetwork::OnAuthenticated(PeeringNetwork::PeeringServerApi *api)
{
    m_hackAuthenticated = true;
}

void TitanPeeringNetwork::OnGameListArrived(PeeringNetwork::PeeringServerApi *api)
{
    // SyAssert(0);  hacked to do the game list stuff here for now
    // we should never get this callback while we are the handler for the api
    m_hackGameListArrived = true;
}

void TitanPeeringNetwork::OnGameJoined(PeeringNetwork::PeeringServerApi *api)
{
    m_waitingForJoinReply = false;
    m_objectManager->SetSelfPeerId(m_api->GetSelfPeerId());
}

void TitanPeeringNetwork::OnPeerAdded(PeeringNetwork::PeeringServerApi *api, int peerId)
{
    m_objectManager->PeerAdded(peerId);
}

void TitanPeeringNetwork::OnPeerRemoved(PeeringNetwork::PeeringServerApi *api, int peerId)
{
    m_objectManager->PeerRemoved(peerId);
}

void TitanPeeringNetwork::OnPeerReady(PeeringNetwork::PeeringServerApi *api, int peerId)
{
}

void TitanPeeringNetwork::OnClusterReady(PeeringNetwork::PeeringServerApi *api)
{
}

void TitanPeeringNetwork::OnRoutePacket(PeeringNetwork::PeeringServerApi *api, int fromPeerId, int applicationType, const char *data, int dataLen)
{
        // application level packet arrived off the cluster
    switch (applicationType)
    {
        case cAppPacketObjectManager:
        {
                // packet is destined for object manager, so forward it for processing
            m_objectManager->ProcessMessage(data, dataLen);
            break;
        }
        default:
        {
            SyAssert(0);    // unknown application-level packet
            break;
        }
    }
}


    ////////////////////////////////////////////////////////////////////////////
    // object manager handlers
    ////////////////////////////////////////////////////////////////////////////

void TitanPeeringNetwork::OnObjectCreate(PeeringNetwork::ObjectManager *manager, int typeId, int objectId, const char *fullState, int fullStateLen)
{
        // create the specified object
  m_titan->GetRegistry()->NetworkCreate((cGameObject::tObjectType)typeId,objectId,fullState,fullStateLen);
}

void TitanPeeringNetwork::OnObjectReset(PeeringNetwork::ObjectManager *manager, int objectId, const char *fullState, int fullStateLen)
{
        // do a complete state-update of specified object (same as the serialized create data)
  m_titan->GetRegistry()->NetworkSetState(objectId,fullState,fullStateLen);
}

void TitanPeeringNetwork::OnObjectDestroy(PeeringNetwork::ObjectManager *manager, int objectId)
{
        // destroy specified object
  m_titan->GetRegistry()->NetworkDelete(objectId);
}

void TitanPeeringNetwork::OnObjectMessage(PeeringNetwork::ObjectManager *manager, int toObjectId, const char *data, int dataLen)
{
        // TODO: send message to specified owned-object for processing
  m_titan->GetRegistry()->NetworkReceiveMessage(toObjectId,data,dataLen);
}

void TitanPeeringNetwork::OnObjectBroadcast(PeeringNetwork::ObjectManager *manager, int fromObjectId, const char *data, int dataLen)
{
        // TODO: send message to specified proxy-object for processing
    m_titan->GetRegistry()->NetworkReceiveBroadcast(fromObjectId,data,dataLen);
}

void TitanPeeringNetwork::OnObjectTakeOwnership(PeeringNetwork::ObjectManager *manager, int objectId)
{
        // TODO: we might do something like create an brain-object and attach it to the object at this point
    m_titan->GetRegistry()->NetworkTakeOwnership(objectId);
}

void TitanPeeringNetwork::OnObjectRequestOwnership(PeeringNetwork::ObjectManager *manager, int objectId, int requestingPeerId)
{
    m_titan->GetRegistry()->NetworkRequestOwnership(objectId, requestingPeerId);
}

int TitanPeeringNetwork::OnObjectGetState(PeeringNetwork::ObjectManager *manager, int objectId, char *stateBuffer, int stateBufferLen)
{
        // serialize the entire state of the object specified
    return m_titan->GetRegistry()->NetworkGetState(objectId, stateBuffer,stateBufferLen);
}

void TitanPeeringNetwork::OnPeerSend(PeeringNetwork::ObjectManager *manager, int peerId, const char *data, int dataLen)
{
    if (m_api != NULL)
    {
        m_api->PeerSend(peerId, cAppPacketObjectManager, data, dataLen);
    }
}

int TitanPeeringNetwork::GetSelfPeerId()
{
  SyAssert(m_api != NULL);

  if (m_api != NULL)
  {
    return m_api->GetSelfPeerId();
  }

  return -1;
}

int TitanPeeringNetwork::GetObjectPeerId(int objectId)
{
  SyAssert(m_objectManager != NULL);

  if (m_objectManager != NULL)
  {
    return m_objectManager->ObjectGetPeerId(objectId);
  }

  return -1;
}
