/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

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, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; 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, see <http://www.gnu.org/licenses/>.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
#include "pyVNodeMgr.h"
#include "../plVault/plVaultCallback.h"
#include "../plVault/plVaultInitTasks.h"
#include "../pfPython/pyVaultNode.h"
#include "../pfPython/pyVaultFolderNode.h"
#include "../pyNetClientComm/pyNetClientComm.h"
#include "../plNetMessage/plNetMessage.h"
#include "../plStatusLog/plStatusLog.h"

////////////////////////////////////////////////////////////////////

class pyVaultOperationCallback : public plVaultOperationCallback
{
public:
    PyObject * fPyObject;
    pyVaultOperationCallback( PyObject * pyObject )
        : fPyObject( pyObject )
    {
        Py_XINCREF( fPyObject );
    }
    ~pyVaultOperationCallback()
    {
        Py_XDECREF( fPyObject );
    }
    void VaultOperationStarted( UInt32 context )
    {
        if ( fPyObject )
        {
            // Do callback
            PyObject* func = PyObject_GetAttrString( fPyObject, "operationStarted" );
            if ( func )
            {
                if ( PyCallable_Check(func)>0 )
                {
                    PyObject* retVal = PyObject_CallMethod(fPyObject, "operationStarted", "l", context);
                    Py_XDECREF(retVal);
                }
            }
        }
    }
    void VaultOperationComplete( UInt32 context, int resultCode )
    {
        if ( fPyObject )
        {
            // Pass args.
//          PyObject* pyArgs = PyObject_GetAttrString( fPyObject, "fCbArgs" );
//          if ( pyArgs )
//          {
//              dict pyDict = dict();
//              std::map<UInt16,plCreatable*>   args;
//              fCbArgs.GetItems( args );
//              for ( std::map<UInt16,plCreatable*>::iterator ii=args.begin(); ii!=args.end(); ++ii )
//              {
//                  UInt16 key = ii->first;
//                  plCreatable* arg = ii->second;
//                  plCreatableGenericValue * genValue = plCreatableGenericValue::ConvertNoRef( arg );
//                  if ( genValue )
//                  {
//                      plGenericType & value = genValue->Value();
//                      switch ( value.GetType() )
//                      {
//                      case plGenericType::kInt:
//                          pyDict[key] = (int)value;
//                          break;
//                      case plGenericType::kUInt:
//                          pyDict[key] = (unsigned int)value;
//                          break;
//                      case plGenericType::kFloat:
//                          pyDict[key] = (float)value;
//                          break;
//                      case plGenericType::kDouble:
//                          pyDict[key] = (double)value;
//                          break;
//                      case plGenericType::kBool:
//                          pyDict[key] = (bool)value;
//                          break;
//                      case plGenericType::kChar:
//                          pyDict[key] = (char)value;
//                          break;
//                      case plGenericType::kString:
//                          pyDict[key] = (const char *)value;
//                          break;
//                      case plGenericType::kAny:
//                          break;
//                      case plGenericType::kNone:
//                          break;
//                      }
//                  }
//              }
//              PyObject_SetAttrString( fPyObject, "fCbArgs", pyDict.ptr() );
//          }
            // Do callback
            PyObject* func = PyObject_GetAttrString( fPyObject, "operationComplete" );
            if ( func )
            {
                if ( PyCallable_Check(func)>0 )
                {
                    PyObject* retVal = PyObject_CallMethod(fPyObject, "operationComplete", "li", context, resultCode);
                    Py_XDECREF(retVal);
                }
            }
        }
        delete this;
    }
};


class pyVaultCallback : public plVaultStubbedCallback
{
public:
    PyObject * fPyObject;
    pyVaultCallback( PyObject * pyObject )
        : fPyObject( pyObject )
    {
        Py_XINCREF( fPyObject );
    }
    ~pyVaultCallback()
    {
        Py_XDECREF( fPyObject );
    }
};


////////////////////////////////////////////////////////////////////

int pyVNodeMgr::VaultMsgHandler::HandleMessage( plNetMessage* msg )
{
    plNetMsgVault * vaultMsg = plNetMsgVault::ConvertNoRef( msg );

    if ( vaultMsg )
    {
        plNetCoreMessage * ncmsg = (plNetCoreMessage*)msg->GetNetCoreMsg();
        msg->PeekBuffer( ncmsg->GetData(), ncmsg->GetLen() );
        fMyVNodeMgr->GetStatusLog()->AddLineF( "\t%s", msg->AsStdString().c_str() );
        plVault::ProcessMsg( vaultMsg );
        return plNetClientComm::kOK_MsgConsumed;
    }

    return hsFail;
}


// pyVNodeMgr ----------------------------------------------
pyVNodeMgr::pyVNodeMgr( PyObject* thaComm )
{
    if (!pyNetClientComm::Check(thaComm))
    {
        fMyCommObj = NULL;
        return; // screwed!
    }

    fMsgHandler.setMgr(this);

    fMyCommObj = thaComm;
    Py_INCREF(fMyCommObj);
    fMyComm = pyNetClientComm::ConvertFrom(fMyCommObj);
    fMyComm->GetNetClientComm()->AddMsgHandlerForType( plNetMsgVault::Index(), &fMsgHandler );
    plVNodeMgr::SetStatusLog( fMyComm->GetNetClientComm()->GetLog(), false );
}

// ~pyVNodeMgr ----------------------------------------------
pyVNodeMgr::~pyVNodeMgr()
{
    fMyComm->GetNetClientComm()->RemoveMsgHandler( &fMsgHandler );
    Py_DECREF(fMyCommObj);
}

void pyVNodeMgr::setMyComm(PyObject* thaComm)
{
    if (fMyComm)
    {
        fMyComm->GetNetClientComm()->RemoveMsgHandler(&fMsgHandler);
        Py_DECREF(fMyCommObj);
        fMyCommObj = NULL;
        fMyComm = NULL;
    }
    if (!pyNetClientComm::Check(thaComm))
        return; // screwed!

    fMyCommObj = thaComm;
    Py_INCREF(fMyCommObj);
    fMyComm = pyNetClientComm::ConvertFrom(fMyCommObj);
    fMyComm->GetNetClientComm()->AddMsgHandlerForType(plNetMsgVault::Index(), &fMsgHandler);
    plVNodeMgr::SetStatusLog(fMyComm->GetNetClientComm()->GetLog(), false);
}

// IAmOnline ----------------------------------------------
bool pyVNodeMgr::IAmOnline() const
{
    return true;
}

// IIsThisMe ----------------------------------------------
bool pyVNodeMgr::IIsThisMe( plVaultPlayerInfoNode* node ) const
{
    return ( fMyComm->GetNetClientComm()->GetPlayerID()==node->GetPlayerID() );
}

// IIsThisMe ----------------------------------------------
bool pyVNodeMgr::IIsThisMe( plVaultPlayerNode * node ) const
{
    return ( fMyComm->GetNetClientComm()->GetPlayerID()==node->GetID() );
}

// ISendNetMsg ----------------------------------------------
int pyVNodeMgr::ISendNetMsg( plNetMsgVault* msg, UInt32 sendFlags/*=0 */)
{
    return fMyComm->GetNetClientComm()->SendMsg( msg, sendFlags );
}

// IGetPlayerID ----------------------------------------------
UInt32 pyVNodeMgr::IGetPlayerID() const
{
    return fMyComm->GetNetClientComm()->GetPlayerID();
}

// Update ----------------------------------------------
int pyVNodeMgr::Update( double secs )
{
    return plVNodeMgr::Update( secs );
}

// Startup ----------------------------------------------
void pyVNodeMgr::Startup()
{
    plVNodeMgr::Startup();
}

// Shutdown ----------------------------------------------
void pyVNodeMgr::Shutdown()
{
    plVNodeMgr::Shutdown();
}


// IsConnected ----------------------------------------------
bool pyVNodeMgr::IsConnected()
{
    return plVNodeMgr::IsConnected();
}

// Disconnect ----------------------------------------------
void pyVNodeMgr::Disconnect( PyObject* cb/*=nil*/, UInt32 cbContext/*=0 */)
{
    // disconnect from allplayers and globalsdl folders
    plVaultNodeRef * out;
    plVaultNode * root = plVNodeMgr::GetRootNode();
    if ( root )
    {
        plVaultFolderNode tmpGlobalSDL;
        tmpGlobalSDL.SetFolderType( plVault::kAllAgeGlobalSDLNodesFolder );
        if ( root->FindNode( &tmpGlobalSDL, out ) )
            root->RemoveNode( out->GetChildID() );
        plVaultFolderNode tmpAllPlayers;
        tmpAllPlayers.SetFolderType( plVault::kAllPlayersFolder );
        if ( root->FindNode( &tmpAllPlayers, out ) )
            root->RemoveNode( out->GetChildID() );
    }
    plVNodeMgr::Disconnect( new pyVaultOperationCallback( cb ), cbContext );
}

// Connect ----------------------------------------------
void pyVNodeMgr::Connect( int childFetchLevel/*=plVault::kFetchAllChildren*/, PyObject* cb/*=nil*/, UInt32 cbContext/*=0 */)
{
    plVNodeMgr::Connect( childFetchLevel, new pyVaultOperationCallback( cb ), cbContext );
}

// FetchNode ----------------------------------------------
bool pyVNodeMgr::FetchNode( UInt32 nodeID,
    int childFetchLevel/*=plVault::kFetchAllChildren*/,
    PyObject* cb/*=nil*/,
    UInt32 cbContext/*=0 */)
{
    return plVNodeMgr::FetchNode( nodeID, childFetchLevel, new pyVaultOperationCallback( cb ), cbContext );
}

// GetRootNode ----------------------------------------------
PyObject* pyVNodeMgr::GetRootNode() const
{
    return pyVaultNode::New( plVNodeMgr::GetRootNode() );
}

// GetClientID ----------------------------------------------
UInt32 pyVNodeMgr::GetClientID() const
{
    return plVNodeMgr::GetClientID();
}

// GetNode ----------------------------------------------
PyObject* pyVNodeMgr::GetNode( UInt32 id ) const
{
    plVaultNode * tmp;
    if ( plVNodeMgr::GetNode( id, tmp ) )
        return pyVaultNode::New( tmp );
    PYTHON_RETURN_NONE;
}

// FindNode ----------------------------------------------
PyObject* pyVNodeMgr::FindNode( pyVaultNode* templateNode ) const
{
    plVaultNode * node;
    if ( plVNodeMgr::FindNode( templateNode->GetNode(), node ) )
        return pyVaultNode::New( node );
    PYTHON_RETURN_NONE;
}

// EnableCallbacks ----------------------------------------------
bool pyVNodeMgr::EnableCallbacks( bool b )
{
    return plVNodeMgr::EnableCallbacks( b );
}

// AddCallback ----------------------------------------------
void pyVNodeMgr::AddCallback( PyObject* cb )
{
    pyVaultCallback * pycb = new pyVaultCallback( cb );
    fPyCallbacks.push_back( pycb );
    plVNodeMgr::AddCallback( pycb );
}

// RemoveCallback ----------------------------------------------
void pyVNodeMgr::RemoveCallback( PyObject* cb )
{
    PyCallbackVec   tmp;
    for ( int i=0; i<fPyCallbacks.size(); i++ )
    {
        if ( fPyCallbacks[i]->fPyObject==cb )
            tmp.push_back( fPyCallbacks[i] );
    }
    for ( int i=0; i<tmp.size(); i++ )
    {
        PyCallbackVec::iterator it = std::find( fPyCallbacks.begin(), fPyCallbacks.end(), tmp[i] );
        if ( it!=fPyCallbacks.end() )
            fPyCallbacks.erase( it );
        plVNodeMgr::RemoveCallback( tmp[i] );
        delete tmp[i];
    }
}

// CreateNode ----------------------------------------------
PyObject* pyVNodeMgr::CreateNode( int nodeType, bool persistent )
{
    plVaultNode * node = plVNodeMgr::CreateNode( nodeType, persistent?kNewPersistentNodeOptions( this ):kNilNodeOptions() );
    if ( node )
        return pyVaultNode::New( node );
    PYTHON_RETURN_NONE;
}

// Dump ----------------------------------------------
void pyVNodeMgr::Dump() const
{
    plVNodeMgr::Dump();
}

////////////////////////////////////////////////////////////////////////

pyPlayerVNodeMgr::pyPlayerVNodeMgr( PyObject* thaComm )
: pyVNodeMgr( thaComm )
{}

// IAmSuperUser ----------------------------------------------
bool pyPlayerVNodeMgr::IAmSuperUser( void ) const
{
    return false;
}

// IFillOutConnectFields ----------------------------------------------
void pyPlayerVNodeMgr::IFillOutConnectFields( plNetMsgVault* msg ) const
{
    msg->GetArgs()->AddInt( plVault::kArg_NodeMgrType, plVault::kNodeType_VNodeMgrPlayer );
    msg->GetArgs()->AddInt( plVault::kArg_NodeMgrID, fMyComm->GetNetClientComm()->GetPlayerID() );
}

// IGetNodeInitializationTask ----------------------------------------------
plVNodeInitTask * pyPlayerVNodeMgr::IGetNodeInitializationTask( plVaultNode * node )
{
    if ( plVaultPlayerNode::ConvertNoRef( node ) )
        return new plVaultPlayerInitializationTask( this, node, true );
    return nil;
}

////////////////////////////////////////////////////////////////////////

pyAgeVNodeMgr::pyAgeVNodeMgr( PyObject* thaComm )
: pyVNodeMgr( thaComm )
{}

// IAmSuperUser ----------------------------------------------
bool pyAgeVNodeMgr::IAmSuperUser( void ) const
{
    return false;
}

// IFillOutConnectFields ----------------------------------------------
void pyAgeVNodeMgr::IFillOutConnectFields( plNetMsgVault* msg ) const
{
    msg->GetArgs()->AddInt( plVault::kArg_NodeMgrType, plVault::kNodeType_VNodeMgrAge );
    msg->GetArgs()->AddString( plVault::kArg_NodeMgrAgeInstanceName, fAgeFilename.c_str() );
    msg->GetArgs()->AddItem( plVault::kArg_NodeMgrAgeGuid, &fAgeInstanceGuid );
}

// IGetNodeInitializationTask ----------------------------------------------
plVNodeInitTask * pyAgeVNodeMgr::IGetNodeInitializationTask( plVaultNode * node )
{
    if ( plVaultAgeNode::ConvertNoRef( node ) )
        return new plVaultAgeInitializationTask( this, node, nil, true );
    if ( plVaultAgeInfoNode::ConvertNoRef( node ) )
        return new plVaultAgeInfoInitializationTask( this, node );
    return nil;
}

// SetAgeInfo ----------------------------------------------
void pyAgeVNodeMgr::SetAgeInfo( const char * ageFilename, const char * ageInstanceGuid )
{
    fAgeFilename = ageFilename;
    fAgeInstanceGuid.FromString( ageInstanceGuid );
}

////////////////////////////////////////////////////////////////////////

pyAdminVNodeMgr::pyAdminVNodeMgr( PyObject* thaComm )
: pyVNodeMgr( thaComm )
, fWantGlobalSDL( true )
, fWantAllPlayers( false )
{}

// IAmSuperUser ----------------------------------------------
bool pyAdminVNodeMgr::IAmSuperUser( void ) const
{
    return true;
}

// IFillOutConnectFields ----------------------------------------------
void pyAdminVNodeMgr::IFillOutConnectFields( plNetMsgVault* msg ) const
{
    msg->GetArgs()->AddInt( plVault::kArg_NodeMgrType, plVault::kNodeType_VNodeMgrAdmin );
    msg->GetArgs()->AddInt( plVault::kArg_NodeMgrID, fMyComm->GetNetClientComm()->GetPlayerID() );
}

// IGetNodeInitializationTask ----------------------------------------------
plVNodeInitTask * pyAdminVNodeMgr::IGetNodeInitializationTask( plVaultNode * node )
{
    if ( plVaultAdminNode::ConvertNoRef( node ) )
        return new plVaultAdminInitializationTask( this, node, fWantGlobalSDL, fWantAllPlayers );
    return nil;
}

// GetGlobalInbox ----------------------------------------------
PyObject * pyAdminVNodeMgr::GetGlobalInbox() const
{
    plVaultFolderNode tmp;
    tmp.SetFolderType( plVault::kGlobalInboxFolder );
    plVaultNode * node;
    if ( plVNodeMgr::FindNode( &tmp, node ) )
    {
        return pyVaultFolderNode::New( plVaultFolderNode::ConvertNoRef( node ) );
    }
    PYTHON_RETURN_NONE;
}

////////////////////////////////////////////////////////////////////
// End.