/*==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/>.

Additional permissions under GNU GPL version 3 section 7

If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.

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 "cyMisc.h"

#include "plgDispatch.h"
#include "hsResMgr.h"
#include "plResMgr/plKeyFinder.h"

#include "pnKeyedObject/plKey.h"
#include "pnKeyedObject/plFixedKey.h"
#include "plMessage/plLinkToAgeMsg.h"
#include "plMessage/plConsoleMsg.h"
#include "plMessage/plAnimCmdMsg.h"
#include "plMessage/plExcludeRegionMsg.h"
#include "plMessage/plInputEventMsg.h"
#include "plMessage/plInputIfaceMgrMsg.h"
#include "pnMessage/plCmdIfaceModMsg.h"
#include "pnMessage/plAttachMsg.h"
#include "plMessage/plTimerCallbackMsg.h"
#include "plMessage/plNetVoiceListMsg.h"
#include "pnMessage/plClientMsg.h"
#include "pnMessage/plCameraMsg.h"
#include "pnTimer/plTimerCallbackManager.h"
#include "plVault/plVault.h"
#include "pnNetCommon/plCreatableUuid.h"
#include "plNetClient/plNetClientMgr.h"
#include "plNetClient/plNetLinkingMgr.h"
#include "plNetTransport/plNetTransport.h"
#include "plNetTransport/plNetTransportMember.h"
#include "plResMgr/plKeyFinder.h"
#include "plAvatar/plAvatarMgr.h"
#include "plAvatar/plMultistageBehMod.h"
#include "plAvatar/plAvBrainCritter.h"
#include "pyCritterBrain.h"
#include "cyPythonInterface.h"
#include "pyKey.h"
#include "pySceneObject.h"
#include "pyPlayer.h"
#include "pyImage.h"
#include "pyDniCoordinates.h"
#include "pyDniInfoSource.h"
#include "pyColor.h"
#include "pyNetLinkingMgr.h"
#include "pyAgeInfoStruct.h"
#include "pyAgeLinkStruct.h"
#include "pyAlarm.h"
#include "pfMessage/pfKIMsg.h"
#include "plNetMessage/plNetMessage.h"
#include "pfCamera/plVirtualCamNeu.h"
#include "plPipeline/plDynamicEnvMap.h"

#include "pfGameGUIMgr/pfGameGUIMgr.h"
#include "pfGameGUIMgr/pfGUIDialogMod.h"
#include "pyGUIDialog.h"
#include "pnSceneObject/plSceneObject.h"
#include "pnSceneObject/plCoordinateInterface.h"

#include "plMessage/plCCRMsg.h"
#include "plAgeLoader/plAgeLoader.h"

#include "plResMgr/plLocalization.h"
#include "plGLight/plLightInfo.h"

#include "plInputCore/plAvatarInputInterface.h"
#include "plInputCore/plInputDevice.h"

#include "plVault/plAgeInfoSource.h"

#include "pfLocalizationMgr/pfLocalizationMgr.h"

//// Static Class Stuff //////////////////////////////////////////////////////
plPipeline* cyMisc::fPipeline = nil;
uint32_t  cyMisc::fUniqueNumber = 0;

#ifdef PLASMA_EXTERNAL_RELEASE
uint32_t  cyMisc::fPythonLoggingLevel = cyMisc::kErrorLevel;
#else
uint32_t  cyMisc::fPythonLoggingLevel = cyMisc::kWarningLevel;
#endif

/////////////////////////////////////////////////////////////////////////////
// static
void cyMisc::Update( double secs )
{
    // only update once per 1/2 sec
    static double lastUpdateTime = 0.0;
    if ( secs-lastUpdateTime>=0.5 )
    {
        lastUpdateTime = secs;
        pyAlarmMgr::GetInstance()->Update( secs );
    }
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : Get/SetDebugPrintLevel
//
//  PURPOSE    : gets and sets the python debug print level
//
uint32_t cyMisc::GetPythonLoggingLevel()
{
    return fPythonLoggingLevel;
}
void cyMisc::SetPythonLoggingLevel(uint32_t new_level)
{
    fPythonLoggingLevel = new_level;
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : Console
//  PARAMETERS : command   - string of console commmand to execute
//
//  PURPOSE    : Execute a console command from a python script
//
void cyMisc::Console(const char* command)
{
    if ( command != nil )
    {
        // create message to send to the console
        plControlEventMsg* pMsg = new plControlEventMsg;
        pMsg->SetBCastFlag(plMessage::kBCastByType);
        pMsg->SetControlCode(B_CONTROL_CONSOLE_COMMAND);
        pMsg->SetControlActivated(true);
        pMsg->SetCmdString(command);
        plgDispatch::MsgSend( pMsg );   // whoosh... off it goes
    }
}

void cyMisc::ConsoleNet(const char* command, hsBool netForce)
{
    if ( command != nil )
    {
        // create message to send to the console
        plControlEventMsg* pMsg = new plControlEventMsg;
        pMsg->SetBCastFlag(plMessage::kBCastByType);
        pMsg->SetBCastFlag(plMessage::kNetPropagate);
        if ( netForce )
            pMsg->SetBCastFlag(plMessage::kNetForce);
        pMsg->SetControlCode(B_CONTROL_CONSOLE_COMMAND);
        pMsg->SetControlActivated(true);
        pMsg->SetCmdString(command);
        plgDispatch::MsgSend( pMsg );   // whoosh... off it goes
    }
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : FindSceneObject
//  PARAMETERS : name   - string of name of the sceneobject
//             : ageName - string of the name of the age to look in
//
//  PURPOSE    : Execute a console command from a python script,
//                  optionally propagate over the net
//
PyObject* cyMisc::FindSceneObject(const plString& name, const char* ageName)
{
    // assume that we won't find the sceneobject (key is equal to nil)
    plKey key=nil;

    if ( !name.IsEmpty() )
    {
        const char* theAge = ageName;
        if ( ageName[0] == 0 )
            theAge = nil;
        key=plKeyFinder::Instance().StupidSearch(theAge,nil,plSceneObject::Index(), name, false);
    }

    if ( key == nil )
    {
        plString errmsg = plString::Format("Sceneobject %s not found",name.c_str());
        PyErr_SetString(PyExc_NameError, errmsg.c_str());
        return nil; // return nil cause we errored
    }
    return pySceneObject::New(key);
}

PyObject* cyMisc::FindActivator(const plString& name)
{
    plKey key = nil;
    if (!name.IsEmpty())
    {
        std::vector<plKey> keylist;
        plKeyFinder::Instance().ReallyStupidActivatorSearch(name, keylist);

        if (keylist.size() == 1)
            key = keylist[0];
    }

    if (key == nil)
    {
        PYTHON_RETURN_NONE;
    }
    else
        return pyKey::New(key);
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : PopUpConsole
//  PARAMETERS : command   - string of console commmand to execute
//
//  PURPOSE    : Execute a console command from a python script
//
void cyMisc::PopUpConsole(const char* command)
{
    if ( command != nil )
    {
        // create message to send to the console
        plControlEventMsg* pMsg1 = new plControlEventMsg;
        pMsg1->SetBCastFlag(plMessage::kBCastByType);
        pMsg1->SetControlCode(B_SET_CONSOLE_MODE);
        pMsg1->SetControlActivated(true);
        plgDispatch::MsgSend( pMsg1 );  // whoosh... off it goes
        // create message to send to the console
        plControlEventMsg* pMsg2 = new plControlEventMsg;
        pMsg2->SetBCastFlag(plMessage::kBCastByType);
        pMsg2->SetControlCode(B_CONTROL_CONSOLE_COMMAND);
        pMsg2->SetControlActivated(true);
        pMsg2->SetCmdString(command);
        plgDispatch::MsgSend( pMsg2 );  // whoosh... off it goes
    }
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : TimerCallback
//  PARAMETERS : command   - string of console commmand to execute
//
//  PURPOSE    : Execute a console command from a python script
//
void cyMisc::TimerCallback(pyKey& selfkey, float time, uint32_t id)
{
    // setup the message to come back to whoever the pyKey is pointing to
    plTimerCallbackMsg* pTimerMsg = new plTimerCallbackMsg(selfkey.getKey(),id);
    plgTimerCallbackMgr::NewTimer( time, pTimerMsg );
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : ClearTimerCallbacks
//  PARAMETERS : key of object to clear callbacks to
//
//  PURPOSE    : clear timer callbacks to a certain key
//
void cyMisc::ClearTimerCallbacks(pyKey& selfkey)
{
    plgTimerCallbackMgr::CancelCallbacksToKey(selfkey.getKey());
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : AttachObject
//  PARAMETERS : child object
//             :   to be attached to parent object
//
//  PURPOSE    : Attach an object to another object, knowing only their pyKeys
//
void cyMisc::AttachObject(pyKey& ckey, pyKey& pkey)
{
    plKey childKey = ckey.getKey();
    plKey parentKey = pkey.getKey();

    // make sure that there was a child ket
    if ( childKey )
    {
        // create the attach message to attach the child
        plAttachMsg* pMsg = new plAttachMsg(parentKey, childKey->GetObjectPtr(), plRefMsg::kOnRequest);
        plgDispatch::MsgSend( pMsg );
    }
}
void cyMisc::AttachObjectSO(pySceneObject& cobj, pySceneObject& pobj)
{
    plKey childKey = cobj.getObjKey();
    plKey parentKey = pobj.getObjKey();

    // make sure that there was a child ket
    if ( childKey )
    {
        // create the attach message to attach the child
        plAttachMsg* pMsg = new plAttachMsg(parentKey, childKey->GetObjectPtr(), plRefMsg::kOnRequest);
        plgDispatch::MsgSend( pMsg );
    }
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : DetachObject
//  PARAMETERS : child object
//             :   to be attached to parent object
//
//  PURPOSE    : Attach an object to another object, knowing only their pyKeys
//
void cyMisc::DetachObject(pyKey& ckey, pyKey& pkey)
{
    plKey childKey = ckey.getKey();
    plKey parentKey = pkey.getKey();

    // make sure that there was a child ket
    if ( childKey )
    {
        // create the attach message to detach the child
        plAttachMsg* pMsg = new plAttachMsg(parentKey, childKey->GetObjectPtr(), plRefMsg::kOnRemove);
        plgDispatch::MsgSend( pMsg );
    }
}
void cyMisc::DetachObjectSO(pySceneObject& cobj, pySceneObject& pobj)
{
    plKey childKey = cobj.getObjKey();
    plKey parentKey = pobj.getObjKey();

    // make sure that there was a child ket
    if ( childKey )
    {
        // create the attach message to detach the child
        plAttachMsg* pMsg = new plAttachMsg(parentKey, childKey->GetObjectPtr(), plRefMsg::kOnRemove);
        plgDispatch::MsgSend( pMsg );
    }
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : LinkToAge
//  PARAMETERS : 
//
//  PURPOSE    : LinkToAge
//
//  STATUS     : Depreciated. Use plNetLinkingMgr or pyNetLinkingMgr instead.
//

//void cyMisc::LinkToAge(pyKey &selfkey, const char *AgeName,const char *SpawnPointName)
//{
//  // find the Modifier that called us
//  hsStatusMessage("PY: LinkToAge\n");
//      // Ask the Modifier if it was Local or Network
//  if (selfkey.WasLocalNotify())
//  {
//      hsStatusMessage("PY:LOCAL NOTIFY\n");
//      plNetLinkingMgr::GetInstance()->LinkToPublicAge( AgeName, SpawnPointName );
//  }
//  else
//  {
//      hsStatusMessage("PY:REMOTE NOTIFY\n");
//  }
//}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : SetDirtySyncStateServer
//  PARAMETERS : 
//
//  PURPOSE    : set the Python modifier to be dirty and asked to be saved out
//
void cyMisc::SetDirtySyncState(pyKey &selfkey, const char* SDLStateName, uint32_t sendFlags)
{
    selfkey.DirtySynchState(SDLStateName, sendFlags);
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : SetDirtySyncStateClients
//  PARAMETERS : 
//
//  PURPOSE    : set the Python modifier to be dirty and asked to be saved out
//
void cyMisc::SetDirtySyncStateWithClients(pyKey &selfkey, const char* SDLStateName, uint32_t sendFlags)
{
    selfkey.DirtySynchState(SDLStateName, sendFlags|plSynchedObject::kBCastToClients);
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : EnableControlKeyEvents & DisableControlKeyEvents
//  PARAMETERS : none
//
//  PURPOSE    : register and unregister for control key events
//
void cyMisc::EnableControlKeyEvents(pyKey &selfkey)
{
    selfkey.EnableControlKeyEvents();
}

void cyMisc::DisableControlKeyEvents(pyKey &selfkey)
{
    selfkey.DisableControlKeyEvents();
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : GetClientName
//  PARAMETERS : avatar key
//
//  PURPOSE    : Return the net client (account) name of the player whose avatar
//              key is provided.
//
hsBool cyMisc::WasLocallyNotified(pyKey &selfkey)
{
    return selfkey.WasLocalNotify();
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : GetClientName
//  PARAMETERS : avatar key
//
//  PURPOSE    : Return the net client (account) name of the player whose avatar
//              key is provided.
//
plString cyMisc::GetClientName(pyKey &avKey)
{
    return plNetClientMgr::GetInstance()->GetPlayerName(avKey.getKey());
}

PyObject* cyMisc::GetAvatarKeyFromClientID(int clientID)
{
    PyObject* keyObj = NULL;

    if (clientID == plNetClientMgr::GetInstance()->GetPlayerID())
    {
        keyObj = pyKey::New(plNetClientMgr::GetInstance()->GetLocalPlayerKey());
    }
    else
    {
        plNetTransportMember **members = nil;
        plNetClientMgr::GetInstance()->TransportMgr().GetMemberListDistSorted( members );

        if( members != nil)
        {
            for(int i = 0; i < plNetClientMgr::GetInstance()->TransportMgr().GetNumMembers(); i++ )
            {
                plNetTransportMember *mbr = members[ i ];
                if( mbr != nil && mbr->GetAvatarKey() != nil && mbr->GetPlayerID() == clientID)
                {   
                    keyObj = pyKey::New(mbr->GetAvatarKey());
                    break;
                }
            }
        }

        delete [] members;
    }

    if (keyObj)
        return keyObj;
    else
        PYTHON_RETURN_NONE;
}


int cyMisc::GetClientIDFromAvatarKey(pyKey& avatar)
{
    int ret = -1;

    if (plNetClientMgr::GetInstance()->GetLocalPlayerKey() == avatar.getKey())
    {
        return (plNetClientMgr::GetInstance()->GetPlayerID());
    }
    plNetTransportMember **members = nil;
    plNetClientMgr::GetInstance()->TransportMgr().GetMemberListDistSorted( members );
    if( members != nil)
    {
        for(int i = 0; i < plNetClientMgr::GetInstance()->TransportMgr().GetNumMembers(); i++ )
        {
            plNetTransportMember *mbr = members[ i ];

            if( mbr != nil && mbr->GetAvatarKey() == avatar.getKey())
            {   
                ret = mbr->GetPlayerID();
                break;
            }
        }
    }

    delete [] members;
    return ret;
}

int cyMisc::GetLocalClientID()
{
    return (plNetClientMgr::GetInstance()->GetPlayerID());
}

hsBool cyMisc::ValidateKey(pyKey& key)
{
    plKey pKey = key.getKey();
    
    if (pKey && pKey->ObjectIsLoaded())
        return true;
        
    return false;
}



/////////////////////////////////////////////////////////////////////////////
//
//  Function   : GetClientName
//  PARAMETERS : 
//
//  PURPOSE    : Return the local net client (account) name
//
plString cyMisc::GetLocalClientName()
{
    return plNetClientMgr::GetInstance()->GetPlayerName();
}


//
// Get Current age information - DEPRECIATED. Use ptDniInfoSource() object instead
//
/////////////////////////////////////////////////////////////////////////////
//
//  Function   : GetAgeName
//  Function   : GetAgeTime
//  Function   : GetAgeGuid
//  PARAMETERS : 
//
//  PURPOSE    : Return the age name of the current age the local player is in
//             : Return the current coordinates of the player within this age
//             : Return the current time with the current age the player is in
//             : Return the current guid of the instance of the age the player is in
//

const char * cyMisc::GetAgeName()
{
    return NetCommGetAge()->ageDatasetName;
}

PyObject* cyMisc::GetAgeInfo()
{
    plNetLinkingMgr* nmgr = plNetLinkingMgr::GetInstance();
    if (nmgr)
    {
        plAgeLinkStruct* als = nmgr->GetAgeLink();
        if (als)
            return pyAgeInfoStruct::New(als->GetAgeInfo());
    }
    PYTHON_RETURN_NONE; // return none, not nil (cause it isn't really an error... or is it?)
}


const char* cyMisc::GetPrevAgeName()
{
    plNetLinkingMgr* nmgr = plNetLinkingMgr::GetInstance();
    if (nmgr)
    {
        plAgeLinkStruct* als = nmgr->GetPrevAgeLink();
        if (als)
            return als->GetAgeInfo()->GetAgeFilename();
    }
    return nil;
}

PyObject* cyMisc::GetPrevAgeInfo()
{
    plNetLinkingMgr* nmgr = plNetLinkingMgr::GetInstance();
    if (nmgr)
    {
        plAgeLinkStruct* als = nmgr->GetPrevAgeLink();
        if (als)
            return pyAgeInfoStruct::New(als->GetAgeInfo());
    }
    PYTHON_RETURN_NONE; // return none, not nil (cause it isn't really an error... or is it?)
}

// current time in current age
uint32_t cyMisc::GetAgeTime( void )
{
    return VaultAgeGetAgeTime();
}



time_t cyMisc::GetDniTime(void)
{
    const plUnifiedTime utime = plNetClientMgr::GetInstance()->GetServerTime();
    if ( utime.GetSecs() != 0)
        return ConvertGMTtoDni(utime.GetSecs());
    else
        return 0;
}

time_t cyMisc::GetServerTime(void)
{
    const plUnifiedTime utime = plNetClientMgr::GetInstance()->GetServerTime();
    return utime.GetSecs();
}

float cyMisc::GetAgeTimeOfDayPercent(void)
{
    return plNetClientMgr::GetInstance()->GetCurrentAgeTimeOfDayPercent();
}

#define kMST (uint32_t)25200
#define kOneHour (uint32_t)3600
#define kOneDay (uint32_t)86400

time_t cyMisc::ConvertGMTtoDni(time_t gtime)
{
    // convert to mountain time
    time_t dtime = gtime - kMST;
    plUnifiedTime utime = plUnifiedTime();
    utime.SetSecs(dtime);
    // check for daylight savings time in New Mexico and adjust
    if ( utime.GetMonth() >= 4 && utime.GetMonth() < 11 )
    {
        plUnifiedTime dstStart = plUnifiedTime();
        dstStart.SetGMTime(utime.GetYear(),4,1,2,0,0);
        // find first Sunday after 4/1 (first sunday of April)
        int days_to_go = 7 - dstStart.GetDayOfWeek();
        if (days_to_go == 7)
            days_to_go = 0;
        time_t dstStartSecs = dstStart.GetSecs() + days_to_go * kOneDay;

        plUnifiedTime dstEnd = plUnifiedTime();
        dstEnd.SetGMTime(utime.GetYear(),10,25,1,0,0);
        // find first sunday after 10/25 (last sunday of Oct.)
        days_to_go = 7 - dstEnd.GetDayOfWeek();
        if (days_to_go == 7)
            days_to_go = 0;
        time_t dstEndSecs = dstEnd.GetSecs() + days_to_go * kOneDay;

        if ( dtime > dstStartSecs && dtime < dstEndSecs )
            // add hour for daylight savings time
            dtime += kOneHour;
    }

    return dtime;
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : ExcludeRegionSet
//  PARAMETERS : key - of the exclude region, ie. where to send the message
//               state  - what state of to set at:
//                        0 = release
//                        1 = clear
//
//  PURPOSE    : Sets the state of an exclude region
//
void cyMisc::ExcludeRegionSet(pyKey& sender, pyKey& exKey, uint16_t state)
{
    plExcludeRegionMsg *msg = new plExcludeRegionMsg;

    switch (state)
    {
        case kExRegClear:
            msg->SetCmd(plExcludeRegionMsg::kClear);
            break;
        case kExRegRelease:
            msg->SetCmd(plExcludeRegionMsg::kRelease);
            break;
    }
    msg->SetSender(sender.getKey());
    msg->AddReceiver(exKey.getKey());
    plgDispatch::MsgSend( msg );    // whoosh... off it goes
}

void cyMisc::ExcludeRegionSetNow(pyKey& sender, pyKey& exKey, uint16_t state)
{
    plExcludeRegionMsg *msg = new plExcludeRegionMsg;

    switch (state)
    {
        case kExRegClear:
            msg->SetCmd(plExcludeRegionMsg::kClear);
            break;
        case kExRegRelease:
            msg->SetCmd(plExcludeRegionMsg::kRelease);
            break;
    }
    msg->SetSender(sender.getKey());
    msg->AddReceiver(exKey.getKey());
    msg->fSynchFlags = plSynchedObject::kSendImmediately;
    plgDispatch::MsgSend( msg );    // whoosh... off it goes
}

#include "hsTimer.h"
/////////////////////////////////////////////////////////////////////////////
//
//  Function   : GetSeconds
//  PARAMETERS : 
//
//  PURPOSE    : Return the nunber of seconds elapsed
//
double cyMisc::GetSeconds()
{
    return hsTimer::GetSeconds();
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : GetSysSeconds
//  PARAMETERS : 
//
//  PURPOSE    : Return the number of system seconds elapsed
//
double cyMisc::GetSysSeconds()
{
    return hsTimer::GetSysSeconds();
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : GetDelSysSeconds
//  PARAMETERS : 
//
//  PURPOSE    : Return the frame delta seconds
//
float cyMisc::GetDelSysSeconds()
{
    return hsTimer::GetDelSysSeconds();
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : LoadDialog
//  PARAMETERS : 
//
//  PURPOSE    : Return the frame delta seconds
//
void cyMisc::LoadDialog(const char* name)
{
    pfGameGUIMgr    *mgr = pfGameGUIMgr::GetInstance();
    if ( mgr )
    {
        if ( !mgr->IsDialogLoaded(name) )
            mgr->LoadDialog( name );
    }
}

// Load dialog and set the GUINotifyMsg receiver key
void cyMisc::LoadDialogK(const char* name, pyKey& rKey)
{
    pfGameGUIMgr    *mgr = pfGameGUIMgr::GetInstance();
    if ( mgr )
    {
        // has the dialog been loaded yet?
        if ( !mgr->IsDialogLoaded(name) )
            // no then load and set handler
            mgr->LoadDialog( name, rKey.getKey() );
        else
            // yes then just set the handler
            mgr->SetDialogToNotify(name,rKey.getKey());
    }
}

// Load dialog and set the GUINotifyMsg receiver key
void cyMisc::LoadDialogKA(const char* name, pyKey& rKey, const char* ageName)
{
    pfGameGUIMgr    *mgr = pfGameGUIMgr::GetInstance();
    if ( mgr )
    {
        // has the dialog been loaded yet?
        if ( !mgr->IsDialogLoaded(name) )
            // no then load and set handler
            mgr->LoadDialog( name, rKey.getKey(), ageName );
        else
            // yes then just set the handler
            mgr->SetDialogToNotify(name,rKey.getKey());
    }
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : UnLoadDialog
//  PARAMETERS : 
//
//  PURPOSE    : UnLoads the dialog by name
//             : optionally sets the receiver key for the GUINotifyMsg
//
void cyMisc::UnloadDialog(const char* name)
{
    pfGameGUIMgr    *mgr = pfGameGUIMgr::GetInstance();
    if ( mgr )
    {
        if ( mgr->IsDialogLoaded(name) )
            mgr->UnloadDialog( name );
    }
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : IsDialogLoaded
//  PARAMETERS : 
//
//  PURPOSE    : Test to see if a dialog is loaded (according to the dialog manager)
//
hsBool cyMisc::IsDialogLoaded(const char* name)
{
    pfGameGUIMgr    *mgr = pfGameGUIMgr::GetInstance();
    if ( mgr )
        return mgr->IsDialogLoaded(name);
    return false;
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : ShowDialog
//  Function   : HideDialog
//  PARAMETERS : 
//
//  PURPOSE    : Show or Hide a dialog by name
//
void cyMisc::ShowDialog(const char* name)
{
    pfGameGUIMgr    *mgr = pfGameGUIMgr::GetInstance();
    if ( mgr )
        mgr->ShowDialog(name);
}
void cyMisc::HideDialog(const char* name)
{
    pfGameGUIMgr    *mgr = pfGameGUIMgr::GetInstance();
    if ( mgr )
        mgr->HideDialog(name);
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : GetDialogFromTagID
//  PARAMETERS : 
//
//  PURPOSE    : Return the frame delta seconds
//
PyObject* cyMisc::GetDialogFromTagID(uint32_t tag)
{
    pfGameGUIMgr    *mgr = pfGameGUIMgr::GetInstance();
    if ( mgr )
    {
        // get the owner dialog modifier pointer
        pfGUIDialogMod* pdialog = mgr->GetDialogFromTag(tag);
        if ( pdialog )
            return pyGUIDialog::New(pdialog->GetKey());
    }

    char errmsg[256];
    sprintf(errmsg,"GUIDialog TagID %d not found",tag);
    PyErr_SetString(PyExc_KeyError, errmsg);
    return nil; // return nil, cause we threw an error
}

PyObject* cyMisc::GetDialogFromString(const char* name)
{
    pfGameGUIMgr    *mgr = pfGameGUIMgr::GetInstance();
    if ( mgr )
    {
        // get the owner dialog modifier pointer
        pfGUIDialogMod* pdialog = mgr->GetDialogFromString(name);
        if ( pdialog )
            return pyGUIDialog::New(pdialog->GetKey());
    }

    char errmsg[256];
    sprintf(errmsg,"GUIDialog %s not found",name);
    PyErr_SetString(PyExc_KeyError, errmsg);
    return nil; // return nil, cause we threw an error
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : IsGUIModal
//  PARAMETERS : 
//
//  PURPOSE    : Returns true if the GUI is currently modal (and therefore blocking input)
//
bool cyMisc::IsGUIModal()
{
    pfGameGUIMgr* mgr = pfGameGUIMgr::GetInstance();
    if (mgr)
        return mgr->IsModalBlocking();
    return false;
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : GetLocalAvatar
//  Function   : GetLocalPlayer
//  PARAMETERS : 
//
//  PURPOSE    : Return a pySceneobject of the local Avatar
//             : Player - returns ptPlayer object
//
PyObject* cyMisc::GetLocalAvatar()
{
    plSceneObject *so = plSceneObject::ConvertNoRef(plNetClientMgr::GetInstance()->GetLocalPlayer());
    if ( so )
        return pySceneObject::New(so->GetKey());

    char errmsg[256];
    sprintf(errmsg,"Local avatar not found");
    PyErr_SetString(PyExc_NameError, errmsg);
    return nil; // returns nil, cause we threw an error
}

PyObject* cyMisc::GetLocalPlayer()
{
    return pyPlayer::New(plNetClientMgr::GetInstance()->GetLocalPlayerKey(),
                        plNetClientMgr::GetInstance()->GetPlayerName().c_str(),
                        plNetClientMgr::GetInstance()->GetPlayerID(),
                        0.0 );
}



#if 1
#include "plStatusLog/plStatusLog.h"
//
// TEMP SCREEN PRINT CODE FOR NON-DBG TEXT DISPLAY
//
void cyMisc::PrintToScreen(const char* msg)
{
    static plStatusLog* gStatusLog=nil;
    if (gStatusLog==nil)
    {
        gStatusLog = plStatusLogMgr::GetInstance().CreateStatusLog( 32, "", 
            plStatusLog::kDontWriteFile | plStatusLog::kDeleteForMe | plStatusLog::kFilledBackground );
        plStatusLogMgr::GetInstance().ToggleStatusLog(gStatusLog);
    }
    gStatusLog->AddLine(msg, plStatusLog::kBlue);   
}
#endif

#include "plPipeline.h"
#include "plGImage/plMipmap.h"

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : GetPlayerList
//  Function   : GetPlayerListDistanceSorted
//  PARAMETERS : 
//
//  PURPOSE    : Get a list of players (other than self) that are playing the game
//             : optionally get it sorted by distance
//
//  RETURNS    : Python object list of pyPlayer s
//
std::vector<PyObject*> cyMisc::GetPlayerList()
{
    std::vector<PyObject*> pyPL;
    plNetClientMgr* nc=plNetClientMgr::GetInstance();

    if (!nc) // only ever really happens if they try to call us in max... I hope
        return pyPL;

    int i;
    for( i = 0; i < nc->TransportMgr().GetNumMembers(); i++ )
    {
        plNetTransportMember *mbr = nc->TransportMgr().GetMember(i);
        plKey avkey = mbr->GetAvatarKey();
        if (avkey)
        {
            // only non-ignored people in list and not in ignore list
            if ( !VaultAmIgnoringPlayer ( mbr->GetPlayerID()) )
            {
                PyObject* playerObj = pyPlayer::New(avkey, mbr->GetPlayerName().c_str(), mbr->GetPlayerID(), mbr->GetDistSq());
                pyPlayer* player = pyPlayer::ConvertFrom(playerObj); // accesses internal pyPlayer object

                // modifies playerObj
                if ( mbr->IsCCR() )
                    player->SetCCRFlag(true);
                if ( mbr->IsServer() )
                    player->SetServerFlag(true);
                
                pyPL.push_back(playerObj);
            }
        }
    }
    return pyPL;
}

std::vector<PyObject*> cyMisc::GetPlayerListDistanceSorted()
{
    std::vector<PyObject*> pyPL;

    // get the sorted member list from the Net transport manager
    plNetTransportMember **members = nil;
    plNetClientMgr::GetInstance()->TransportMgr().GetMemberListDistSorted( members );
    if( members != nil )
    {
        int i;
        for( i = 0; i < plNetClientMgr::GetInstance()->TransportMgr().GetNumMembers(); i++ )
        {
            plNetTransportMember *mbr = members[ i ];
            plKey avkey = mbr->GetAvatarKey();
            if (avkey)
            {
                // only non-ignored people in list and not in ignore list
                if ( !VaultAmIgnoringPlayer ( mbr->GetPlayerID()) )
                {
                    PyObject* playerObj = pyPlayer::New(avkey, mbr->GetPlayerName().c_str(), mbr->GetPlayerID(), mbr->GetDistSq());
                    pyPlayer* player = pyPlayer::ConvertFrom(playerObj); // accesses internal pyPlayer object

                    // modifies playerObj
                    if ( mbr->IsCCR() )
                        player->SetCCRFlag(true);
                    if ( mbr->IsServer() )
                        player->SetServerFlag(true);

                    pyPL.push_back(playerObj);
                }
            }
        }
        delete [] members;
    }
    return pyPL;
}

uint32_t cyMisc::GetMaxListenListSize()
{
    return plNetListenList::kMaxListenListSize;
}

float cyMisc::GetMaxListenDistSq()
{
    return plNetListenList::kMaxListenDistSq;
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : SendRTChat
//  PARAMETERS : from   - is a pyPlayer of the person who is sending this
//             : tolist  - is a python list object, if empty then broadcast message
//             : message  - text string to send to others
//             : flags   - the flags of destiny... whatever that means
//
//  PURPOSE    : To send a real time chat message to a particualr user, a list of users
//             : or broadcast it to everyone (within hearing distance?)
//
//  RETURNS    : the flags that were sent with the message (may be modified)
//
uint32_t cyMisc::SendRTChat(pyPlayer& from, const std::vector<pyPlayer*> & tolist, const char* message, uint32_t flags)
{
    // create the messge that will contain the chat message
    pfKIMsg *msg = new pfKIMsg( pfKIMsg::kHACKChatMsg );
    msg->SetString( message );
    msg->SetUser( from.GetPlayerName(), from.GetPlayerID() );
    msg->SetFlags( flags );
    msg->SetBCastFlag(plMessage::kNetPropagate | plMessage::kNetForce);
    msg->SetBCastFlag(plMessage::kLocalPropagate, 0);

    if (tolist.size() > 0)
    {
        if (flags & 8/* kRTChatInterAge in PlasmaKITypes.py */)
        {
            // allow inter-age routing of this msg
            msg->SetBCastFlag( plMessage::kNetAllowInterAge );
        }
        // add net rcvrs to msg
        int i;
        for ( i=0 ; i<tolist.size() ; i++ )
        {
            if ( !VaultAmIgnoringPlayer( tolist[i]->GetPlayerID() ) )
                msg->AddNetReceiver(tolist[i]->GetPlayerID());
        }
    }

    uint32_t msgFlags = msg->GetFlags();

    if (tolist.size() == 0 || (msg->GetNetReceivers() && msg->GetNetReceivers()->size() > 0))
        msg->Send();

    return msgFlags;
}

uint32_t cyMisc::SendRTChat(pyPlayer& from, const std::vector<pyPlayer*> & tolist, const wchar_t* message, uint32_t flags)
{
    // create the messge that will contain the chat message
    pfKIMsg *msg = new pfKIMsg( pfKIMsg::kHACKChatMsg );
    msg->SetString( message );
    msg->SetUser( from.GetPlayerName(), from.GetPlayerID() );
    msg->SetFlags( flags );
    msg->SetBCastFlag(plMessage::kNetPropagate | plMessage::kNetForce);
    msg->SetBCastFlag(plMessage::kLocalPropagate, 0);

    if (tolist.size() > 0)
    {
        if (flags & 8/* kRTChatInterAge in PlasmaKITypes.py */)
        {
            // allow inter-age routing of this msg
            msg->SetBCastFlag( plMessage::kNetAllowInterAge );
        }
        // add net rcvrs to msg
        for ( int i = 0 ; i < tolist.size() ; i++ )
        {
            if ( !VaultAmIgnoringPlayer( tolist[i]->GetPlayerID() ) )
                msg->AddNetReceiver(tolist[i]->GetPlayerID());
        }
    }

    uint32_t msgFlags = msg->GetFlags();

    if (tolist.size() == 0 || (msg->GetNetReceivers() && msg->GetNetReceivers()->size() > 0))
        msg->Send();

    return msgFlags;
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : SendKIMessage
//  PARAMETERS : command   - the command type
//             : value     - extra value
//
//  PURPOSE    : Send message to the KI, to tell it things to do
//
//  RETURNS    : nothing
//
void cyMisc::SendKIMessage(uint32_t command, float value)
{
    // create the mesage to send
    pfKIMsg *msg = new pfKIMsg( (uint8_t)command );

    // check to see if the value makes any sense
    if ( command == pfKIMsg::kSetChatFadeDelay )
    {
        msg->SetDelay(value);
    }
    else if ( command == pfKIMsg::kSetTextChatAdminMode )
    {
        msg->SetFlags( value==1.0f ? pfKIMsg::kAdminMsg : 0 );
    }
    // send it off
    plgDispatch::MsgSend( msg );
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : SendKIMessageS
//  PARAMETERS : command   - the command type
//             : value     - extra value as a string
//
//  PURPOSE    : Send message to the KI, to tell it things to do
//
//  RETURNS    : nothing
//
void cyMisc::SendKIMessageS(uint32_t command, const wchar_t* value)
{
    // create the mesage to send
    pfKIMsg *msg = new pfKIMsg( (uint8_t)command );

    msg->SetString( value );

    // send it off
    plgDispatch::MsgSend( msg );
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : SendKIMessageI
//  PARAMETERS : command   - the command type
//             : value     - extra value as an int32_t
//
//  PURPOSE    : Send message to the KI, to tell it things to do
//
//  RETURNS    : nothing
//
void  cyMisc::SendKIMessageI(uint32_t command, int32_t value)
{
    // create the mesage to send
    pfKIMsg *msg = new pfKIMsg( (uint8_t)command );

    msg->SetIntValue(value);

    // send it off
    plgDispatch::MsgSend( msg );
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : SendKIMessageIReply
//  PARAMETERS : command   - the command type
//             : value     - extra value as an int32_t
//
//  PURPOSE    : Send message to the KI, to tell it things to do
//
//  RETURNS    : nothing
//
void  cyMisc::SendKIGZMarkerMsg(int32_t markerNumber, pyKey& sender)
{
    // create the mesage to send
    pfKIMsg *msg = new pfKIMsg( pfKIMsg::kGZInRange );

    msg->SetIntValue(markerNumber);
    msg->SetSender(sender.getKey());

    // send it off
    plgDispatch::MsgSend( msg );
}

void cyMisc::SendKIRegisterImagerMsg(const char* imagerName, pyKey& sender)
{
    pfKIMsg *msg = new pfKIMsg(pfKIMsg::kRegisterImager);

    msg->SetString(imagerName);
    msg->SetSender(sender.getKey());

    plgDispatch::MsgSend( msg );
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : YesNoDialog
//  PARAMETERS : sender    - who set this and will get the notify
//             : message   - message to put up in YesNo dialog
//
//  PURPOSE    : Put up Yes/No dialog
//
//  RETURNS    : nothing
//

void cyMisc::YesNoDialog(pyKey& sender, const char* thestring)
{
    // create the mesage to send
    pfKIMsg *msg = new pfKIMsg( pfKIMsg::kYesNoDialog );

    msg->SetSender(sender.getKey());
    msg->SetString(thestring);
    // send it off
    plgDispatch::MsgSend( msg );
}

void cyMisc::YesNoDialog(pyKey& sender, std::wstring thestring)
{
    char *temp = hsWStringToString(thestring.c_str());
    YesNoDialog(sender,temp);
    delete [] temp;
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : RateIt
//  PARAMETERS : chonicleName    - where to store the rating
//             : thestring       - the message in the RateIt dialog
//             : onceFlag        - flag to tell whether this is a onetime thing or ask everytime
//
//  PURPOSE    : Send message to the KI to tell it to ask the user to Rate something
//
//  RETURNS    : nothing
//
void cyMisc::RateIt(const char* chronicleName, const char* thestring, hsBool onceFlag)
{
    // create the mesage to send
    pfKIMsg *msg = new pfKIMsg( pfKIMsg::kRateIt );

    msg->SetUser(chronicleName,0);
    msg->SetString(thestring);
    msg->SetIntValue(onceFlag);
    // send it off
    plgDispatch::MsgSend( msg );
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : SetPrivateChatList
//  PARAMETERS : key            - who's joining
//
//  PURPOSE    : Lock the local avatar into private vox messaging, and / or add new memebers to his chat list
//
//  RETURNS    : nothing
//

void cyMisc::SetPrivateChatList(const std::vector<pyPlayer*> & tolist)
{
    if (tolist.size() > 0)
    {
        plNetVoiceListMsg* pMsg = new plNetVoiceListMsg(plNetVoiceListMsg::kForcedListenerMode);
        for (int i=0 ; i<tolist.size() ; i++ )
            pMsg->GetClientList()->Append(tolist[i]->GetPlayerID());
        
        plKey k = plNetClientMgr::GetInstance()->GetLocalPlayerKey();
        pMsg->SetRemovedKey(k);
        pMsg->Send();
    }
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : ClearPrivateChatList
//  PARAMETERS : key            - who's leaving
//
//  PURPOSE    : Remove the local avatar from private vox messaging, and / or clear memebers from his chat list
//
//  RETURNS    : nothing
//
void cyMisc::ClearPrivateChatList(pyKey& member)
{
    plNetVoiceListMsg* pMsg = new plNetVoiceListMsg(plNetVoiceListMsg::kDistanceMode);
    plKey k = member.getKey();
    pMsg->SetRemovedKey(k);
    pMsg->Send();
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : ClearCameraStack
//  PURPOSE    : clear the camera stack
//
//  RETURNS    : nothing
//

void cyMisc::ClearCameraStack()
{
    plVirtualCam1::Instance()->ClearStack();
}

bool cyMisc::IsFirstPerson()
{
    return (plVirtualCam1::Instance()->Is1stPersonCamera()!=0);
}
/////////////////////////////////////////////////////////////////////////////
//
//  Function   : SendPetitionToCCR
//  PARAMETERS : message  - message to send to the CCR ("please help me!")
//
//  PURPOSE    : Send a petition to the CCR for help or questions
//
void cyMisc::SendPetitionToCCR(const char* message)
{
    SendPetitionToCCRI(message,plNetCommon::PetitionTypes::kGeneralHelp,nil);
}
void cyMisc::SendPetitionToCCRI(const char* message, uint8_t reason,const char* title)
{
    // create the mesage to send
    plCCRPetitionMsg *msg = new plCCRPetitionMsg();
    msg->SetNote(message);
    msg->SetType(reason);
    if (title)
        msg->SetTitle(title);
    // send it off
    plgDispatch::MsgSend( msg );
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : SendChatToCCR
//  PARAMETERS : message  - message to send to the CCR ("please help me!")
//
//  PURPOSE    : Send a chat message to the CCR for help or questions
//
void cyMisc::SendChatToCCR(const char* message,int32_t CCRPlayerID)
{
    // create the mesage to send
    plCCRCommunicationMsg *msg = new plCCRCommunicationMsg();
    msg->SetMessage(message);
    msg->SetType(plCCRCommunicationMsg::kReturnChatMsg);
    msg->SetBCastFlag(plMessage::kNetAllowInterAge);
    msg->SetBCastFlag(plMessage::kNetPropagate);
    msg->SetBCastFlag(plMessage::kNetForce);
    msg->SetBCastFlag(plMessage::kLocalPropagate,0);
    msg->AddNetReceiver( CCRPlayerID );
    msg->Send();
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : GetNumRemotePlayers
//  
//  PURPOSE    : return the number of remote players connected
//
int cyMisc::GetNumRemotePlayers()
{
    return plNetClientMgr::GetInstance()->RemotePlayerKeys().size();
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : Paging functions
//  PARAMETERS : nodeName  - name of the page to load
//  
//  PURPOSE    : page in, hold or out a particular node
//

void cyMisc::PageInNodes(const std::vector<std::string> & nodeNames, const char* age)
{
    if (hsgResMgr::ResMgr())
    {
        plSynchEnabler ps(false);   // disable dirty tracking while paging in
        plClientMsg* msg = new plClientMsg(plClientMsg::kLoadRoom);
        plKey clientKey = hsgResMgr::ResMgr()->FindKey(kClient_KEY);
        msg->AddReceiver(clientKey);

        int numNames = nodeNames.size();
        for (int i = 0; i < numNames; i++)
            msg->AddRoomLoc(plKeyFinder::Instance().FindLocation(age ? age : NetCommGetAge()->ageDatasetName, nodeNames[i].c_str()));

        msg->Send();
    }
}

void cyMisc::PageOutNode(const char* nodeName)
{
    if ( hsgResMgr::ResMgr() )
    {
        plSynchEnabler ps(false);   // disable dirty tracking while paging out
        plClientMsg* pMsg1 = new plClientMsg(plClientMsg::kUnloadRoom);
        plKey clientKey = hsgResMgr::ResMgr()->FindKey( kClient_KEY );
        pMsg1->AddReceiver( clientKey );
        pMsg1->AddRoomLoc(plKeyFinder::Instance().FindLocation(nil, nodeName));
        plgDispatch::MsgSend(pMsg1);
    }
}


#include "plAvatar/plArmatureMod.h"
/////////////////////////////////////////////////////////////////////////////
//
//  Function   : LimitAvatarLOD
//  PARAMETERS : LODlimit - number of to limit the LOD to
//  
//  PURPOSE    : sets the avatar LOD limit
//
void cyMisc::LimitAvatarLOD(int LODlimit)
{
    if(LODlimit >= 0 && LODlimit <= 2)
        plArmatureLODMod::fMinLOD = LODlimit;
}



#include "plPipeline/plFogEnvironment.h"

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : Set fog default functions
//  PARAMETERS : floats  - the parameters
//  
//  PURPOSE    : sets the fog defaults
//
void cyMisc::FogSetDefColor(pyColor& color)
{
    if ( fPipeline )
    {
        hsColorRGBA hcolor = color.getColor();
        hcolor.a = 1.0f;        // make sure that alpha is 1
        plFogEnvironment env = fPipeline->GetDefaultFogEnviron();
        env.SetColor( hcolor );
        fPipeline->SetDefaultFogEnviron( &env );
    }
}

void cyMisc::FogSetDefLinear(float start, float end, float density)
{
    if ( fPipeline )
    {
        plFogEnvironment env = fPipeline->GetDefaultFogEnviron();
        env.Set( start, end, density ); 
        fPipeline->SetDefaultFogEnviron( &env );
    }
}

void cyMisc::FogSetDefExp(float end, float density)
{
    if ( fPipeline )
    {
        plFogEnvironment env = fPipeline->GetDefaultFogEnviron();
        env.SetExp( plFogEnvironment::kExpFog, end, density ); 
        fPipeline->SetDefaultFogEnviron( &env );
    }
}

void cyMisc::FogSetDefExp2(float end, float density)
{
    if ( fPipeline )
    {
        plFogEnvironment env = fPipeline->GetDefaultFogEnviron();
        env.SetExp( plFogEnvironment::kExp2Fog, end, density ); 
        fPipeline->SetDefaultFogEnviron( &env );
    }
}


void cyMisc::SetClearColor(float red, float green, float blue)
{
    // do this command via the console to keep the maxplugins from barfing
    char command[256];
    sprintf(command,"Graphics.Renderer.SetClearColor %f %f %f",red,green,blue);
    // create message to send to the console
    plControlEventMsg* pMsg = new plControlEventMsg;
    pMsg->SetBCastFlag(plMessage::kBCastByType);
    pMsg->SetControlCode(B_CONTROL_CONSOLE_COMMAND);
    pMsg->SetControlActivated(true);
    pMsg->SetCmdString(command);
    plgDispatch::MsgSend( pMsg );   // whoosh... off it goes
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : Enable / disable cursor fade for avatar
//  PARAMETERS : 
//
//  PURPOSE    : turns avatar fade out on / off 
//

void cyMisc::EnableAvatarCursorFade()
{
    plNetClientMgr* nmgr = plNetClientMgr::GetInstance();
    if (nmgr)
    {
        plIfaceFadeAvatarMsg* iMsg = new plIfaceFadeAvatarMsg;
        iMsg->SetSubjectKey(nmgr->GetLocalPlayerKey());
        iMsg->SetBCastFlag(plMessage::kBCastByExactType);
        iMsg->SetBCastFlag(plMessage::kNetPropagate, false);
        iMsg->Enable();
        iMsg->Send();
    }
}

void cyMisc::DisableAvatarCursorFade()
{
    plNetClientMgr* nmgr = plNetClientMgr::GetInstance();
    if (nmgr)
    {
        plIfaceFadeAvatarMsg* iMsg = new plIfaceFadeAvatarMsg;
        iMsg->SetSubjectKey(nmgr->GetLocalPlayerKey());
        iMsg->SetBCastFlag(plMessage::kBCastByExactType);
        iMsg->SetBCastFlag(plMessage::kNetPropagate, false);
        iMsg->Disable();
        iMsg->Send();
    }
}

void cyMisc::FadeLocalPlayer(hsBool fade)
{
    plNetClientMgr* nmgr = plNetClientMgr::GetInstance();
    if (nmgr)
    {
        plCameraTargetFadeMsg* pMsg = new plCameraTargetFadeMsg;
        pMsg->SetFadeOut(fade);
        pMsg->SetSubjectKey(nmgr->GetLocalPlayerKey());
        pMsg->SetBCastFlag(plMessage::kBCastByExactType);
        pMsg->SetBCastFlag(plMessage::kNetPropagate, false);
        pMsg->AddReceiver(nmgr->GetLocalPlayerKey());
        plgDispatch::MsgSend(pMsg);
    }
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : offer linking book functions
//  PARAMETERS : 
//
//  PURPOSE    : manage offering public (pedestal) books
//

void cyMisc::EnableOfferBookMode(pyKey& selfkey, const char* ageFilename, const char* ageInstanceName)
{
    plInputIfaceMgrMsg* pMsg = new plInputIfaceMgrMsg(plInputIfaceMgrMsg::kSetOfferBookMode);
    pMsg->SetSender(selfkey.getKey());
    pMsg->SetAgeFileName(ageFilename);
    pMsg->SetAgeName(ageInstanceName);  
    pMsg->Send();
}

void cyMisc::DisableOfferBookMode()
{
    plInputIfaceMgrMsg* pMsg = new plInputIfaceMgrMsg(plInputIfaceMgrMsg::kClearOfferBookMode);
    pMsg->Send();
}

void cyMisc::NotifyOffererPublicLinkCompleted(uint32_t offerer)
{
    plInputIfaceMgrMsg* pMsg = new plInputIfaceMgrMsg(plInputIfaceMgrMsg::kNotifyOfferCompleted, plNetClientMgr::GetInstance()->GetPlayerID());
    pMsg->SetSender(plNetClientMgr::GetInstance()->GetLocalPlayerKey());
    if (offerer != plNetClientMgr::GetInstance()->GetPlayerID())
    {
        pMsg->SetBCastFlag( plMessage::kNetPropagate );
        pMsg->SetBCastFlag( plMessage::kNetForce );
        pMsg->SetBCastFlag( plMessage::kLocalPropagate, 0 );
        pMsg->AddNetReceiver( offerer );
    }

    pMsg->Send();
}

void cyMisc::NotifyOffererPublicLinkRejected(uint32_t offerer)
{
    plInputIfaceMgrMsg* pMsg = new plInputIfaceMgrMsg(plInputIfaceMgrMsg::kNotifyOfferRejected);
    pMsg->SetSender(plNetClientMgr::GetInstance()->GetLocalPlayerKey());
    if (offerer != plNetClientMgr::GetInstance()->GetPlayerID())
    {
        pMsg->SetBCastFlag( plMessage::kNetPropagate );
        pMsg->SetBCastFlag( plMessage::kNetForce );
        pMsg->SetBCastFlag( plMessage::kLocalPropagate, 0 );
        pMsg->AddNetReceiver( offerer );
    }

    pMsg->Send();
    ToggleAvatarClickability(true);
}

void cyMisc::NotifyOffererPublicLinkAccepted(uint32_t offerer)
{
    plInputIfaceMgrMsg* pMsg = new plInputIfaceMgrMsg(plInputIfaceMgrMsg::kNotifyOfferAccepted);
    pMsg->SetSender(plNetClientMgr::GetInstance()->GetLocalPlayerKey());
    if (offerer != plNetClientMgr::GetInstance()->GetPlayerID())
    {
        pMsg->SetBCastFlag( plMessage::kNetPropagate );
        pMsg->SetBCastFlag( plMessage::kNetForce );
        pMsg->SetBCastFlag( plMessage::kLocalPropagate, 0 );
        pMsg->AddNetReceiver( offerer );
    }

    pMsg->Send();
}

void cyMisc::ToggleAvatarClickability(hsBool on)
{
    plInputIfaceMgrMsg* pMsg = 0;
    if (on)
        pMsg = new plInputIfaceMgrMsg(plInputIfaceMgrMsg::kGUIEnableAvatarClickable);
    else
        pMsg = new plInputIfaceMgrMsg(plInputIfaceMgrMsg::kGUIDisableAvatarClickable);
    plKey k = plNetClientMgr::GetInstance()->GetLocalPlayerKey();
    pMsg->SetAvKey(k);
    pMsg->SetBCastFlag(plMessage::kNetPropagate);
    pMsg->SetBCastFlag(plMessage::kNetForce);
    pMsg->Send();

}

void cyMisc::SetShareSpawnPoint(const char* spawnPoint)
{
    plInputIfaceMgrMsg* pMsg = new plInputIfaceMgrMsg(plInputIfaceMgrMsg::kSetShareSpawnPoint);
    plKey k = plNetClientMgr::GetInstance()->GetLocalPlayerKey();
    pMsg->SetSender(k);
    pMsg->SetSpawnPoint(spawnPoint);
    pMsg->Send();
}

void cyMisc::SetShareAgeInstanceGuid(const Uuid& guid)
{
    plInputIfaceMgrMsg* pMsg = new plInputIfaceMgrMsg(plInputIfaceMgrMsg::kSetShareAgeInstanceGuid);
    plKey k = plNetClientMgr::GetInstance()->GetLocalPlayerKey();
    pMsg->SetSender(k);
    pMsg->SetAgeInstanceGuid(guid);
    pMsg->Send();
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : GetCCRAwayStatus
// PARAMETERS :
//
// PURPOSE    : Returns current status of CCR dept
//
hsBool cyMisc::IsCCRAwayStatus()
{
    return !VaultGetCCRStatus();
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : AmCCR
// PARAMETERS :
//
// PURPOSE    : Returns true if local player is a CCR
//
hsBool cyMisc::AmCCR()
{
    if ( plNetClientApp::GetInstance() )
        return plNetClientApp::GetInstance()->AmCCR();
    return false;
}


//////////////////////////////////////////////////////////////////////////////
//
// Function   : AcceptInviteInGame
// PARAMETERS : Friend's Name, Invite Key
//
// PURPOSE    : Send's a VaultTask to the server to perform the invite
//
void cyMisc::AcceptInviteInGame(const char* friendName, const char* inviteKey)
{
    hsAssert(false, "eric, implement me");
#if 0
    plNetClientMgr* nc = plNetClientMgr::GetInstance();
    if (nc)
    {
        plNetMsgVaultTask msg;
        msg.SetNetProtocol(kNetProtocolCli2Auth);
        msg.SetTopic(plNetCommon::VaultTasks::kFriendInvite);
        msg.GetArgs()->AddString(plNetCommon::VaultTaskArgs::kFriendName,friendName);
        msg.GetArgs()->AddString(plNetCommon::VaultTaskArgs::kInviteKey,inviteKey);
        nc->SendMsg(&msg);
    }
#endif
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : GetLanguage
// PARAMETERS :
//
// PURPOSE    : Returns the current language the game is in
//
int cyMisc::GetLanguage()
{
    return int(plLocalization::GetLanguage());
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : UsingUnicode
// PARAMETERS :
//
// PURPOSE    : Returns true if the current language uses Unicode (like Japanese)
//
bool cyMisc::UsingUnicode()
{
    return plLocalization::UsingUnicode();
}

//////////////////////////////////////////////////////////////////////////////
//
//
//  particle system management
//
//
//
#include "plMessage/plParticleUpdateMsg.h"
#include "plParticleSystem/plParticleSystem.h"
#include "plParticleSystem/plParticleEffect.h"
void cyMisc::TransferParticlesToKey(pyKey& fromKey, pyKey& toKey, int numParticles)
{
    plKey frKey = fromKey.getKey();
    plSceneObject* so = plSceneObject::ConvertNoRef(toKey.getKey()->ObjectIsLoaded());
    if (so == nil) 
        return;
    
    plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar();
    
    plParticleTransferMsg* pMsg = new plParticleTransferMsg(nil, avMod->GetKey(), 0, frKey, numParticles);
    pMsg->SetBCastFlag(plMessage::kNetPropagate);
    pMsg->SetBCastFlag(plMessage::kNetForce);
    pMsg->Send();
}

void cyMisc::SetParticleDissentPoint(float x, float y, float z, pyKey& particles)
{
    plKey frKey = particles.getKey();
    plSceneObject* pObj = plSceneObject::ConvertNoRef(particles.getKey()->ObjectIsLoaded());
    if (!pObj)
        return;
    const plParticleSystem *sys = plParticleSystem::ConvertNoRef(pObj->GetModifierByType(plParticleSystem::Index()));
    if (sys == nil) 
    {
        const plModifier* pArm = pObj->GetModifierByType(plArmatureMod::Index());
        if (pArm)
        {
            // it's the avatar
            const plArmatureMod* pCArm = (const plArmatureMod*)pArm;
            plArmatureMod* pNonConstArm = const_cast<plArmatureMod*>(pCArm);
            pObj = pNonConstArm->GetFollowerParticleSystemSO();
            if (!pObj)
                return;
            else
                sys = plParticleSystem::ConvertNoRef(pObj->GetModifierByType(plParticleSystem::Index()));

        }
        if (sys == nil)
            return;
        
    }
    plParticleEffect *flock = sys->GetEffect(plParticleFlockEffect::Index());
    if (flock)
    {
        (new plParticleFlockMsg(nil, flock->GetKey(), 0, plParticleFlockMsg::kFlockCmdSetDissentPoint, x, y, z))->Send();
    }
}

void cyMisc::SetParticleOffset(float x, float y, float z, pyKey& particles)
{
    plKey frKey = particles.getKey();
    plSceneObject* pObj = plSceneObject::ConvertNoRef(particles.getKey()->ObjectIsLoaded());
    if (!pObj)
        return;
    const plParticleSystem *sys = plParticleSystem::ConvertNoRef(pObj->GetModifierByType(plParticleSystem::Index()));
    if (sys == nil) 
    {
        const plModifier* pArm = pObj->GetModifierByType(plArmatureMod::Index());
        if (pArm)
        {
            // it's the avatar
            const plArmatureMod* pCArm = (const plArmatureMod*)pArm;
            plArmatureMod* pNonConstArm = const_cast<plArmatureMod*>(pCArm);
            pObj = pNonConstArm->GetFollowerParticleSystemSO();
            if (!pObj)
                return;
            else
                sys = plParticleSystem::ConvertNoRef(pObj->GetModifierByType(plParticleSystem::Index()));

        }
        if (sys == nil)
            return;
        
    }

    plParticleEffect *flock = sys->GetEffect(plParticleFlockEffect::Index());
    if (flock)
    {
        (new plParticleFlockMsg(nil, flock->GetKey(), 0, plParticleFlockMsg::kFlockCmdSetOffset, x, y, z))->Send();
    }
}

void cyMisc::KillParticles(float time, float pct, pyKey& particles)
{
    plKey frKey = particles.getKey();
    plSceneObject* pObj = plSceneObject::ConvertNoRef(particles.getKey()->ObjectIsLoaded());
    if (!pObj)
        return;
    const plParticleSystem *sys = plParticleSystem::ConvertNoRef(pObj->GetModifierByType(plParticleSystem::Index()));
    if (sys == nil) 
    {
        const plModifier* pArm = pObj->GetModifierByType(plArmatureMod::Index());
        if (pArm)
        {
            // it's the avatar
            const plArmatureMod* pCArm = (const plArmatureMod*)pArm;
            plArmatureMod* pNonConstArm = const_cast<plArmatureMod*>(pCArm);
            pObj = pNonConstArm->GetFollowerParticleSystemSO();
            if (!pObj)
                return;
            else
                sys = plParticleSystem::ConvertNoRef(pObj->GetModifierByType(plParticleSystem::Index()));

        }
        if (sys == nil)
            return;
        
    }
    plParticleEffect *flock = sys->GetEffect(plParticleFlockEffect::Index());
    if (flock)
    {
        plParticleKillMsg* pMsg = new plParticleKillMsg(nil, frKey, 0, pct, time, plParticleKillMsg::kParticleKillPercentage | plParticleKillMsg::kParticleKillImmortalOnly);
        pMsg->SetBCastFlag(plMessage::kNetPropagate);
        pMsg->SetBCastFlag(plMessage::kNetForce);
        pMsg->SetBCastFlag(plMessage::kPropagateToChildren);
        pMsg->SetBCastFlag(plMessage::kPropagateToModifiers);
        pMsg->Send();
    }
}

int cyMisc::GetNumParticles(pyKey& host)
{
    plSceneObject* pObj = plSceneObject::ConvertNoRef(host.getKey()->ObjectIsLoaded());
    if (!pObj)
        return 0;
    const plModifier* pArm = pObj->GetModifierByType(plArmatureMod::Index());
    if (pArm)
    {
        // it's the avatar
        const plArmatureMod* pCArm = (const plArmatureMod*)pArm;
        plArmatureMod* pNonConstArm = const_cast<plArmatureMod*>(pCArm);
        pObj = pNonConstArm->GetFollowerParticleSystemSO();
        if (!pObj)
            return 0;
    }
    const plModifier* pPart = pObj->GetModifierByType(plParticleSystem::Index());
    if (!pPart)
        return 0;
    return ((const plParticleSystem*)pPart)->GetNumValidParticles(true);
}


void cyMisc::SetLightColorValue(pyKey& light, const plString& lightName, float r, float g, float b, float a)
{
    // lightName is the name of the light object attached to the light that we want to talk to
    // for the bug lights, this would be "RTOmni-BugLightTest"
    plSceneObject* pObj = plSceneObject::ConvertNoRef(light.getKey()->ObjectIsLoaded());
    if (!pObj)
        return;

    plObjInterface* pIface = pObj->GetGenericInterface(plLightInfo::Index());
    if (pIface)
    {
        // we are a light ourselves... are we the one they are looking for?
        if (lightName != pObj->GetKeyName())
            pIface = nil; // not the one they want, check our children
    }

    if (!pIface)
    {
        // recurse through our children...
        for (int i = 0; i < pObj->GetCoordinateInterface()->GetNumChildren(); i++)
        {
            const plSceneObject* child = pObj->GetCoordinateInterface()->GetChild(i)->GetOwner();
            if (child)
            {
                pIface = child->GetGenericInterface(plLightInfo::Index());
                if (pIface)
                {
                    // found a child... is it the one we want?
                    if (lightName != child->GetKeyName())
                        pIface = nil; // not the child we want, keep looking
                }
            }
            if (pIface)
                break;
        }
    }
    if (pIface)
    {
        plLightInfo* pLight = (plLightInfo*)pIface;
        hsColorRGBA c;
        c.Set(r,g,b,a);
        pLight->SetDiffuse(c);
        pLight->SetDiffuse(c);
        pLight->SetSpecular(c);
    }
}

#include "pnMessage/plEnableMsg.h"
void cyMisc::SetLightAnimationOn(pyKey& light, const plString& lightName, hsBool start)
{
    // lightName is the name of the light object attached to the light that we want to talk to
    // for the bug lights, this would be "RTOmni-BugLightTest"
    plSceneObject* pObj = plSceneObject::ConvertNoRef(light.getKey()->ObjectIsLoaded());
    if (!pObj)
        return;

    plObjInterface* pIface = pObj->GetGenericInterface(plLightInfo::Index());
    if (pIface)
    {
        // we are a light ourselves... are we the one they are looking for?
        if (lightName != pObj->GetKeyName())
            pIface = nil; // not the one they want, check our children
    }

    if (!pIface)
    {
        // recurse through our children...
        for (int i = 0; i < pObj->GetCoordinateInterface()->GetNumChildren(); i++)
        {
            const plSceneObject* child = pObj->GetCoordinateInterface()->GetChild(i)->GetOwner();
            if (child)
            {
                pIface = child->GetGenericInterface(plLightInfo::Index());
                if (pIface)
                {
                    // found a child... is it the one we want?
                    if (lightName != child->GetKeyName())
                        pIface = nil; // not the child we want, keep looking
                }
            }
            if (pIface)
                break;
        }
    }
    if (pIface)
    {
        plEnableMsg* enableMsg = new plEnableMsg;
        enableMsg->AddReceiver(pIface->GetKey());
        enableMsg->SetBCastFlag(plMessage::kNetPropagate);
        enableMsg->SetBCastFlag(plMessage::kNetForce);

        plAnimCmdMsg* animMsg = new plAnimCmdMsg;
        animMsg->AddReceiver(pIface->GetOwnerKey());
        animMsg->SetBCastFlag(plMessage::kPropagateToModifiers);
        animMsg->SetBCastFlag(plMessage::kNetPropagate);
        animMsg->SetBCastFlag(plMessage::kNetForce);

        if (start)
        {
            enableMsg->SetCmd(plEnableMsg::kEnable);
            animMsg->SetCmd(plAnimCmdMsg::kContinue);
        }
        else
        {
            enableMsg->SetCmd(plEnableMsg::kDisable);
            animMsg->SetCmd(plAnimCmdMsg::kStop);
            animMsg->SetCmd(plAnimCmdMsg::kGoToBegin);
        }

        enableMsg->Send();
        animMsg->Send();
    }
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : RegisterForControlEventMessages
// PARAMETERS : switch on or off, registrant
//
// PURPOSE    : let you get control event messages at will (for pseudo-GUI's like the psnl bookshelf or clft imager)

void cyMisc::RegisterForControlEventMessages(hsBool on, pyKey& k)
{
    plCmdIfaceModMsg* pMsg = new plCmdIfaceModMsg;
    pMsg->SetSender(k.getKey());
    if (on)
        pMsg->SetCmd(plCmdIfaceModMsg::kAdd);
    else
        pMsg->SetCmd(plCmdIfaceModMsg::kRemove);
    pMsg->SetBCastFlag(plMessage::kBCastByExactType);
    pMsg->Send();
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : RequestLOSScreen
// PARAMETERS : lots...
//
// PURPOSE    : To request an LOS from a point on the screen
//
#include "plMessage/plLOSRequestMsg.h"
bool cyMisc::RequestLOSScreen(pyKey &selfkey, int32_t ID, float xPos, float yPos, float distance, int what, int reportType)
{
    plPipeline* pipe = selfkey.GetPipeline();
    if (pipe)
    {
        int32_t x=(int32_t) ( xPos * pipe->Width() );
        int32_t y=(int32_t) ( yPos * pipe->Height() );

        hsPoint3 endPos, startPos;
        
        pipe->ScreenToWorldPoint( 1,0, &x, &y, distance, 0, &endPos );
        startPos = pipe->GetViewPositionWorld();

        // move the start pos out a little to avoid backing up against physical objects...
        hsVector3 view(endPos - startPos);
        view.Normalize();
        startPos = startPos + (view * 1.5f);

        plLOSRequestMsg* pMsg = nil;
        switch (what)
        {
            case kClickables:
                pMsg = new plLOSRequestMsg( selfkey.getKey(), startPos, endPos, plSimDefs::kLOSDBUIItems, plLOSRequestMsg::kTestClosest );
                pMsg->SetCullDB(plSimDefs::kLOSDBUIBlockers);
                break;
            case kCameraBlockers:
                pMsg = new plLOSRequestMsg( selfkey.getKey(), startPos, endPos, plSimDefs::kLOSDBCameraBlockers, plLOSRequestMsg::kTestClosest );
                break;
            case kCustom:
                pMsg = new plLOSRequestMsg( selfkey.getKey(), startPos, endPos, plSimDefs::kLOSDBCustom, plLOSRequestMsg::kTestClosest );
                break;
            case kShootable:
                pMsg = new plLOSRequestMsg( selfkey.getKey(), startPos, endPos, plSimDefs::kLOSDBShootableItems, plLOSRequestMsg::kTestClosest );
                break;
        }

        if ( pMsg )
        {
            pMsg->SetReportType( (plLOSRequestMsg::ReportType)reportType );

            pMsg->SetRequestID( ID );

            plgDispatch::MsgSend( pMsg );
            return true;
        }
    }
    return false;
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : CheckVisLOS,CheckVisLOSFromCursor
// PARAMETERS : StartPoint, EndPoint
//
// PURPOSE    : Check is there is something visible in the path from StartPoint to EndPoint
//
#include "plDrawable/plVisLOSMgr.h"
PyObject* cyMisc::CheckVisLOS(pyPoint3 startPoint, pyPoint3 endPoint)
{
    if (plVisLOSMgr::Instance())
    {
        plVisHit hit;
        if( plVisLOSMgr::Instance()->Check(startPoint.fPoint,endPoint.fPoint,hit) )
        {
            return pyPoint3::New(hit.fPos);
        }
    }
    PYTHON_RETURN_NONE;
}

PyObject* cyMisc::CheckVisLOSFromCursor()
{
    if (plVisLOSMgr::Instance())
    {
        plVisHit hit;
        if( plVisLOSMgr::Instance()->CursorCheck(hit) )
        {
            return pyPoint3::New(hit.fPos);
        }
    }
    PYTHON_RETURN_NONE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : IsSinglePlayerMode
// PARAMETERS :
//
// PURPOSE    : Returns whether the game is in Single Player mode
//
bool cyMisc::IsSinglePlayerMode()
{
    return false;
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : IsDemoMode
// PARAMETERS :
//
// PURPOSE    : Returns whether the game is in Single Player mode
//
bool cyMisc::IsDemoMode()
{
    plNetClientApp* nc = plNetClientApp::GetInstance();
    if (nc)
        return nc->InDemoMode();
    // if we couldn't find the net client app, maybe it was because we are single player mode
    return true;
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : IsInternalRelease
// PARAMETERS :
//
// PURPOSE    : Returns true if we are running an internal build
//
bool cyMisc::IsInternalRelease()
{
#ifdef PLASMA_EXTERNAL_RELEASE
    return false;
#else
    return true;
#endif
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : IsEnterChatModeKeyBound
// PARAMETERS :
//
// PURPOSE    : Returns whether the EnterChatMode is bound to a key
//
bool cyMisc::IsEnterChatModeKeyBound()
{
    plAvatarInputInterface* aii = plAvatarInputInterface::GetInstance();
    if (aii)
        return aii->IsEnterChatModeBound();
    // if we couldn't find the net client app, maybe it was because we are single player mode
    return true;
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : ShootBulletFromScreen
// PARAMETERS : lots...
//
// PURPOSE    : Shoots from screen coordinates, a bullet and makes a mark on objects that know about bullet holes
//
#include "plMessage/plBulletMsg.h"
void cyMisc::ShootBulletFromScreen(pyKey &selfkey, float xPos, float yPos, float radius, float range)
{
    plPipeline* pipe = selfkey.GetPipeline();
    if (pipe)
    {
        int32_t x=(int32_t) ( xPos * pipe->Width() );
        int32_t y=(int32_t) ( yPos * pipe->Height() );

        hsPoint3 endPos, startPos;
        
        pipe->ScreenToWorldPoint( 1,0, &x, &y, range, 0, &endPos );
        startPos = pipe->GetViewPositionWorld();

        // move the start pos out a little to avoid backing up against physical objects...
        hsVector3 view(endPos - startPos);
        view.Normalize();
        startPos = startPos + (view * 1.5f);

        plBulletMsg* bull = new plBulletMsg( selfkey.getKey(), nil, nil );
        bull->FireShot(startPos, view, radius, range);
        bull->Send();
    }
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : ShootBulletFromObject
// PARAMETERS : lots...
//
// PURPOSE    : Shoots from an object, a bullet and makes a mark on objects that know about bullet holes
//
void cyMisc::ShootBulletFromObject(pyKey &selfkey, pySceneObject* sobj, float radius, float range)
{
    plSceneObject* so = plSceneObject::ConvertNoRef(sobj->getObjKey()->ObjectIsLoaded());
    if( so )
    {
        // find the direction

        hsMatrix44 l2w = so->GetLocalToWorld();
        hsVector3 dir(-l2w.fMap[0][0], -l2w.fMap[1][0], -l2w.fMap[2][0]);
        dir.Normalize();
        hsPoint3 pos = l2w.GetTranslate();

        plBulletMsg* bull = new plBulletMsg(selfkey.getKey(), nil, nil);
        bull->FireShot(pos, dir, radius, range);

        bull->Send();
    }
}


//////////////////////////////////////////////////////////////////////////////
class NetClientCommCallback : public plNetClientComm::Callback
{
public:
    enum Contexts
    {
        kInvalid,
        kGetPublicAgeList,
        kCreatePublicAge,
        kRemovePublicAge,
    };

    PyObject * fPyObject;
    NetClientCommCallback( PyObject * pyObject )
        : fPyObject( pyObject )
    {
        Py_XINCREF( fPyObject );
    }
    ~NetClientCommCallback()
    {
        Py_XDECREF( fPyObject );
    }
    void OperationStarted( uint32_t context )
    {}
    void OperationComplete( uint32_t context, int resultCode )
    {
        if ( !fPyObject )
            return;



        PyObject* func = nil;

        switch ( context )
        {
        case kGetPublicAgeList:
            // Call the callback.
            func = PyObject_GetAttrString( fPyObject, "gotPublicAgeList" );
            if ( func )
            {
                if ( PyCallable_Check(func)>0 )
                {
                    plCreatableStream * ageInfoStream = plCreatableStream::ConvertNoRef( fCbArgs.GetItem( 0 ) );
                    plCreatableStream * nPlayersStream = plCreatableStream::ConvertNoRef( fCbArgs.GetItem( 1 ) );

                    if ( ageInfoStream && nPlayersStream )
                    {
                        uint16_t nAgeInfoEntries;
                        ageInfoStream->GetStream()->ReadLE( &nAgeInfoEntries );

                        uint16_t nPlayerCountEntries;
                        nPlayersStream->GetStream()->ReadLE( &nPlayerCountEntries );

                        hsAssert( nAgeInfoEntries==nPlayerCountEntries, "huh?" );

                        // convert callback args to a list of tuple(ageInfo,nPlayers)
                        PyObject* pyEL = PyList_New(nAgeInfoEntries);

                        for ( int i=0; i<nAgeInfoEntries; i++ )
                        {
                            plAgeInfoStruct ageInfo;
                            uint32_t nPlayers;
                            ageInfo.Read( ageInfoStream->GetStream(), nil );
                            nPlayersStream->GetStream()->ReadLE( &nPlayers );
                            PyObject* t = PyTuple_New(2);
                            PyTuple_SetItem(t, 0, pyAgeInfoStruct::New(&ageInfo));
                            PyTuple_SetItem(t, 1, PyLong_FromUnsignedLong(nPlayers));
                            PyList_SetItem(pyEL, i, t); // steals the ref
                        }
                        PyObject* retVal = PyObject_CallMethod(fPyObject, "gotPublicAgeList", "O", pyEL);
                        Py_XDECREF(retVal);
                    }
                }
            }
            break;
        case kCreatePublicAge:
            // Call the callback.
            func = PyObject_GetAttrString( fPyObject, "publicAgeCreated" );
            if ( func )
            {
                if ( PyCallable_Check(func)>0 )
                {
                    plAgeInfoStruct * ageInfo = plAgeInfoStruct::ConvertNoRef( fCbArgs.GetItem( 0 ) );

                    if ( ageInfo )
                    {
                        PyObject* ageInfoObj = pyAgeInfoStruct::New(ageInfo);
                        PyObject* retVal = PyObject_CallMethod(fPyObject, "publicAgeCreated", "O", ageInfoObj);
                        Py_XDECREF(retVal);
                        Py_DECREF(ageInfoObj);
                    }
                }
            }
            break;
        case kRemovePublicAge:
            // Call the callback.
            func = PyObject_GetAttrString( fPyObject, "publicAgeRemoved" );
            if ( func )
            {
                if ( PyCallable_Check(func)>0 )
                {
                    plCreatableUuid * guid = plCreatableUuid::ConvertNoRef( fCbArgs.GetItem( 0 ) );

                    if ( guid )
                    {
                        PyObject* retVal = PyObject_CallMethod(fPyObject, "publicAgeRemoved", "s", guid->AsString().c_str());
                        Py_XDECREF(retVal);
                    }
                }
            }
            break;
        }

        delete this;
    }
};


//////////////////////////////////////////////////////////////////////////////
//
// Function   : GetPublicAgeList
// PARAMETERS : ageName, callback object
//
// PURPOSE    : Get the list of public ages for the given age name.
//
void cyMisc::GetPublicAgeList( const char * ageName, PyObject * cbObject )
{
    if (cbObject)
        Py_XINCREF(cbObject);
    NetCommGetPublicAgeList(
        ageName,
        cbObject,
        plNetCommReplyMsg::kParamTypePython
    );
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : CreatePublicAge
// PARAMETERS : ageInfo, callback object
//
// PURPOSE    : Add a public age to the list of available ones.
//
void cyMisc::CreatePublicAge( pyAgeInfoStruct * ageInfo, PyObject * cbObject )
{
    VaultSetOwnedAgePublicAndWait(ageInfo->GetAgeInfo(), true);
    // TODO: make the callback here
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : RemovePublicAge
// PARAMETERS : ageInstanceGuid, callback object
//
// PURPOSE    : Remove a public age from the list of available ones.
//
void cyMisc::RemovePublicAge( const char * ageInstanceGuid, PyObject * cbObject/*=nil */)
{
    plAgeInfoStruct info;
    plUUID uuid(ageInstanceGuid);
    info.SetAgeInstanceGuid(&uuid);
    VaultSetOwnedAgePublicAndWait(&info, false);
    // TODO: make the callback here
}

int cyMisc::GetKILevel()
{
    int result = pfKIMsg::kNanoKI;
    
    wchar_t wStr[MAX_PATH];
    StrToUnicode(wStr, pfKIMsg::kChronicleKILevel, arrsize(wStr));
    if (RelVaultNode * rvn = VaultFindChronicleEntryIncRef(wStr)) {
        VaultChronicleNode chron(rvn);
        result = wcstol(chron.entryValue, nil, 0);
        rvn->DecRef();
    }
    
    return result;
}

//////////////////////////////////////////////////////////////////////////////
//
// the following are for recording and rebuilding the camera stack

#include "pfCamera/plCameraModifier.h"

int cyMisc::GetNumCameras()
{
    return (plVirtualCam1::Instance()->GetNumCameras());
}

const char* cyMisc::GetCameraNumber(int number)
{
    plCameraModifier1* pCam = plVirtualCam1::Instance()->GetCameraNumber(number-1);
    if (pCam->GetTarget())
    {
        const char* ret = pCam->GetTarget()->GetKeyName().c_str();
        plString str = plString::Format("saving camera named %s to chronicle\n",ret);
        plVirtualCam1::Instance()->AddMsgToLog(str.c_str());
        return ret;
    }
    plVirtualCam1::Instance()->AddMsgToLog("sending empty to camera chronicle\n");
    return "empty";
}

void cyMisc::RebuildCameraStack(const plString& name, const char* ageName)
{
    plKey key=nil;
    plString str = plString::Format("attempting to restore camera named %s from chronicle\n",name.c_str());
    plVirtualCam1::Instance()->AddMsgToLog(str.c_str());

    if (name.Compare("empty") == 0)
        return;

    if ( !name.IsEmpty() )
    {
        key=plKeyFinder::Instance().StupidSearch(nil,nil,plSceneObject::Index(), name, false);
    }
    if ( key == nil )
    {
        // try and use this new hack method to find it
        if (!plVirtualCam1::Instance()->RestoreFromName(name))
        {
            // give up and force built in 3rd person
            plVirtualCam1::Instance()->PushThirdPerson();
            plString errmsg = plString::Format("Sceneobject %s not found",name.c_str());
            PyErr_SetString(PyExc_NameError, errmsg.c_str());
        }
    }
    else
    {
        // now we have the scene object, look for it's camera modifier
        const plCameraModifier1* pMod = nil;
        plSceneObject* pObj = plSceneObject::ConvertNoRef(key->ObjectIsLoaded());
        if (pObj)
        {
            for (int i = 1; i < pObj->GetNumModifiers(); i++)
            {
                pMod = plCameraModifier1::ConvertNoRef(pObj->GetModifier(i));
                if (pMod)
                    break;
            }
            if (pMod)
            {   
                plVirtualCam1::Instance()->RebuildStack(pMod->GetKey());
                return;
            }
        }
        plVirtualCam1::Instance()->PushThirdPerson();
        plString errmsg = plString::Format("Sceneobject %s has no camera modifier",name.c_str());
        PyErr_SetString(PyExc_NameError, errmsg.c_str());
    }
    
}

void cyMisc::PyClearCameraStack()
{
    plVirtualCam1::Instance()->ClearStack();
}

void cyMisc::RecenterCamera()
{
    plCameraMsg* pCam = new plCameraMsg;
    pCam->SetBCastFlag(plMessage::kBCastByExactType);
    pCam->SetCmd(plCameraMsg::kResetPanning);
    pCam->Send();
}

#include "plMessage/plTransitionMsg.h"

void cyMisc::FadeIn(float lenTime, bool holdFlag, bool noSound)
{
    plTransitionMsg *msg = new plTransitionMsg( noSound ? plTransitionMsg::kFadeInNoSound : plTransitionMsg::kFadeIn, lenTime, holdFlag );
    plgDispatch::MsgSend( msg );
}

void cyMisc::FadeOut(float lenTime, bool holdFlag, bool noSound)
{
    plTransitionMsg *msg = new plTransitionMsg( noSound ? plTransitionMsg::kFadeOutNoSound : plTransitionMsg::kFadeOut, lenTime, holdFlag );
    plgDispatch::MsgSend( msg );
}

void cyMisc::SetClickability(hsBool b)
{
    plInputIfaceMgrMsg* msg = new plInputIfaceMgrMsg(b ? plInputIfaceMgrMsg::kEnableClickables : plInputIfaceMgrMsg::kDisableClickables );
    plgDispatch::MsgSend(msg);
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : Debug build only: Assert if condition is false.
//
// PURPOSE    : debugging
//
void cyMisc::DebugAssert( bool cond, const char * msg )
{
    hsAssert( cond, msg );
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : Set a python object to be called back after a certain amount of time.
//
// PURPOSE    : script can trigger itself over time w/o having to specify it in the dataset.
//
void cyMisc::SetAlarm( float secs, PyObject * cb, uint32_t cbContext )
{
    pyAlarmMgr::GetInstance()->SetAlarm( secs, cb, cbContext );
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : Save Screen Shot
//
// PURPOSE    : captures the screen and saves it as a jpeg
//
#include "plJPEG/plJPEG.h"
void cyMisc::SaveScreenShot(const char* fileName, int x, int y, int quality)
{
    if ( cyMisc::GetPipeline() )
    {
        if (quality <= 0 || quality > 100)
            quality = 75;

        plMipmap mipmap;
        cyMisc::GetPipeline()->CaptureScreen( &mipmap, false, x, y );

        plJPEG::Instance().SetWriteQuality( quality );
        plJPEG::Instance().WriteToFile( fileName, &mipmap );
    }
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : Start a screen capture
//
// PURPOSE    : This starts a screen capture in motion. It will be capture on the next
//              update and a plCaptureRenderMsg when its ready
//
#include "plPipeline/plCaptureRender.h"
void cyMisc::StartScreenCapture(pyKey& selfkey)
{
    cyMisc::StartScreenCaptureWH(selfkey, 800, 600);
}

void cyMisc::StartScreenCaptureWH(pyKey& selfkey, uint16_t width, uint16_t height)
{
    plCaptureRender::Capture(selfkey.getKey(), width, height);
}


#include "plAvatar/plAvatarClothing.h"
void cyMisc::WearMaintainerSuit(pyKey& key, hsBool wear)
{
    // run on all machines, but only affects us if we call it on our local guy (who props it to others himself)
    if (key.getKey() != plNetClientMgr::GetInstance()->GetLocalPlayerKey())
        return;

    plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar();
    
    if (avMod)
    {
        if (wear)
            avMod->GetClothingOutfit()->WearMaintainerOutfit();
        else
            avMod->GetClothingOutfit()->RemoveMaintainerOutfit();
    }

}

void cyMisc::WearDefaultClothing(pyKey& key)
{
    if (key.getKey() != plNetClientMgr::GetInstance()->GetLocalPlayerKey())
        return;

    plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar();
    
    if (avMod)
    {
        avMod->GetClothingOutfit()->WearDefaultClothing();
    }
}

void cyMisc::WearDefaultClothingType(pyKey& key, uint32_t type)
{
    if (key.getKey() != plNetClientMgr::GetInstance()->GetLocalPlayerKey())
        return;

    plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar();

    if (avMod)
    {
        avMod->GetClothingOutfit()->WearDefaultClothingType(type);
    }
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : Fake link to object
//
// PURPOSE    : takes an avatar key and an object key and fake-links the avatar
//              to that object's position.  appears to be a link to other players
//

void cyMisc::FakeLinkToObject(pyKey& avatar, pyKey& object)
{
    plPseudoLinkEffectMsg* msg = new plPseudoLinkEffectMsg;
    msg->fAvatarKey = avatar.getKey();
    msg->fLinkObjKey = object.getKey();
    plgDispatch::MsgSend(msg);
}

void cyMisc::FakeLinkToObjectNamed(const plString& name)
{
    plKey key = nil;
    if ( !name.IsEmpty() )
    {
        key = plKeyFinder::Instance().StupidSearch(nil,nil,plSceneObject::Index(), name, false);
    }

    if (!key)
        return;
    plPseudoLinkEffectMsg* msg = new plPseudoLinkEffectMsg;
    msg->fAvatarKey = plNetClientMgr::GetInstance()->GetLocalPlayerKey();
    msg->fLinkObjKey = key;
    plgDispatch::MsgSend(msg);
}

PyObject* cyMisc::LoadAvatarModel(const char* modelName, pyKey& spawnPoint, const char* userStr)
{
    plKey SpawnedKey = plAvatarMgr::GetInstance()->LoadAvatar(modelName, "", false, spawnPoint.getKey(), nil, userStr);
    return pyKey::New(SpawnedKey);
}

void cyMisc::UnLoadAvatarModel(pyKey& avatar)
{
    plAvatarMgr::GetInstance()->UnLoadAvatar(avatar.getKey(), false);
}

void cyMisc::ForceCursorHidden()
{
    plMouseDevice::HideCursor(true);
}

void cyMisc::ForceCursorShown()
{
    plMouseDevice::ShowCursor(true);
}

///////////////////////////////////////////////////////////////////////////////
//
// Function   : GetLocalizedString
//
// PURPOSE    : Returns the specified localized string with the parameters
//              properly replaced (the list is a list of unicode strings) Name
//              is in "Age.Set.Name" format
//
std::wstring cyMisc::GetLocalizedString(std::wstring name, const std::vector<std::wstring> & arguments)
{
    if (pfLocalizationMgr::InstanceValid())
        return pfLocalizationMgr::Instance().GetString(name, arguments);
    return L"";
}

void cyMisc::EnablePlanarReflections(bool enable)
{
    plDynamicCamMap::SetEnabled(enable);
}

void cyMisc::GetSupportedDisplayModes(std::vector<plDisplayMode> *res)
{
    fPipeline->GetSupportedDisplayModes(res);
}

int cyMisc::GetDesktopWidth()
{
    return fPipeline->GetDesktopWidth();
}

int cyMisc::GetDesktopHeight()
{
    return fPipeline->GetDesktopHeight();
}

int cyMisc::GetDesktopColorDepth()
{
    return fPipeline->GetDesktopColorDepth();
}

PipelineParams *cyMisc::GetDefaultDisplayParams()
{
    return fPipeline->GetDefaultParams();
}

void cyMisc::SetGraphicsOptions(int Width, int Height, int ColorDepth, hsBool Windowed, int NumAASamples, int MaxAnisotropicSamples, hsBool VSync)
{
    // This has to send a message to plClient because python is loaded in the max plugins

    plKey clientKey = hsgResMgr::ResMgr()->FindKey( kClient_KEY );
    plClientMsg* clientMsg = new plClientMsg(plClientMsg::kResetGraphicsDevice);
    clientMsg->AddReceiver(clientKey);
    //clientMsg->SetBCastFlag(plMessage::kBCastByType);
    clientMsg->fGraphicsSettings.fWidth = Width;
    clientMsg->fGraphicsSettings.fHeight = Height;
    clientMsg->fGraphicsSettings.fColorDepth = ColorDepth;
    clientMsg->fGraphicsSettings.fWindowed = Windowed;
    clientMsg->fGraphicsSettings.fNumAASamples = NumAASamples;
    clientMsg->fGraphicsSettings.fMaxAnisoSamples = MaxAnisotropicSamples;
    clientMsg->fGraphicsSettings.fVSync = VSync;
    clientMsg->Send();

    //plClient::GetInstance()->ResetDisplayDevice(Width, Height, ColorDepth, Windowed, NumAASamples, MaxAnisotropicSamples);
}

bool cyMisc::DumpLogs(const std::wstring & folder)
{
    char* temp = hsWStringToString(folder.c_str());
    bool retVal = plStatusLogMgr::GetInstance().DumpLogs(temp);
    delete [] temp;
    return retVal;
}

bool cyMisc::FileExists(const std::wstring & filename)
{
    return PathDoesFileExist(filename.c_str());
}

bool cyMisc::CreateDir(const std::wstring & directory)
{
    return PathCreateDirectory(directory.c_str(), kPathCreateDirFlagEntireTree) == kPathCreateDirSuccess;
}

std::wstring cyMisc::GetUserPath()
{
    wchar_t path[MAX_PATH];
    PathGetUserDirectory(path, arrsize(path));
    return path;
}

std::wstring cyMisc::GetInitPath()
{
    wchar_t path[MAX_PATH];
    PathGetInitDirectory(path, arrsize(path));
    return path;
}

void cyMisc::SetBehaviorNetFlags(pyKey & behKey, hsBool netForce, hsBool netProp)
{
    if (plMultistageBehMod * behMod = plMultistageBehMod::ConvertNoRef(behKey.getKey()->ObjectIsLoaded()))
    {
        behMod->SetNetForce(netForce);
        behMod->SetNetProp(netProp);
    }
}

void cyMisc::SendFriendInvite(const wchar_t email[], const wchar_t toName[])
{
    if (RelVaultNode* pNode = VaultGetPlayerNodeIncRef())
    {
        VaultPlayerNode player(pNode);
        Uuid inviteUuid = player.inviteUuid;

        // If we don't have an invite UUID set then make a new one
        if (GuidIsNil(inviteUuid))
        {
            inviteUuid = GuidGenerate();
            player.SetInviteUuid(inviteUuid);
        }

        NetCommSendFriendInvite(email, toName, inviteUuid);
        pNode->DecRef();
    }
}

PyObject* cyMisc::PyGuidGenerate()
{
    char guidStr[64];
    Uuid newGuid = GuidGenerate();
    GuidToString(newGuid, guidStr, arrsize(guidStr));

    return PyString_FromString(guidStr);
}

PyObject* cyMisc::GetAIAvatarsByModelName(const char* name)
{
    plAvatarMgr::plArmatureModPtrVec armVec;
    plAvatarMgr::GetInstance()->FindAllAvatarsByModelName(name, armVec);

    PyObject* avList = PyList_New(0);

    for (plAvatarMgr::plArmatureModPtrVec::iterator it = armVec.begin(); it != armVec.end(); ++it)
    {
        plArmatureMod* armMod = (*it);
        plAvBrainCritter* critterBrain = plAvBrainCritter::ConvertNoRef(armMod->FindBrainByClass(plAvBrainCritter::Index()));
        if (critterBrain)
        {
            PyObject* tuple = PyTuple_New(2);
            PyTuple_SetItem(tuple, 0, pyCritterBrain::New(critterBrain));
            PyTuple_SetItem(tuple, 1, PyString_FromString(armMod->GetUserStr()));

            PyList_Append(avList, tuple);
            Py_DECREF(tuple);
        }
    }

    if (PyList_Size(avList) > 0)
        return avList;
    else
    {
        Py_DECREF(avList);
        PYTHON_RETURN_NONE;
    }
}

void cyMisc::ForceVaultNodeUpdate(unsigned nodeId)
{
    VaultFetchNodesAndWait(&nodeId, 1, true);
}

void cyMisc::VaultDownload(unsigned nodeId)
{
    VaultDownloadAndWait(
        L"PyVaultDownload",
        nodeId,
        nil,
        nil
    );
}
