/*==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==*/
#ifndef plResponderModifier_inc
#define plResponderModifier_inc

#include "hsStlUtils.h"
#include "pnModifier/plSingleModifier.h"
#include "pnMessage/plMessage.h"


class plNotifyMsg;
class plAnimCmdMsg;
class plResponderSDLModifier;
class plResponderModifier : public plSingleModifier
{
    friend class plResponderSDLModifier;
protected:
    typedef std::map<Int8,Int8> WaitToCmd;

    class plResponderCmd
    {
    public:
        plResponderCmd() : fMsg(nil), fWaitOn(-1) {}
        plResponderCmd(plMessage *msg, Int8 waitOn) : fMsg(msg), fWaitOn(waitOn) {}

        plMessage *fMsg;
        Int8 fWaitOn;       // Index into fCompletedEvents of who we're waiting on
    };
    class plResponderState
    {
    public:
        hsTArray<plResponderCmd> fCmds;
        Int8 fNumCallbacks;         // So we know how far to search into the bitvector to find out when we're done
        Int8 fSwitchToState;        // State to switch to when all commands complete
        WaitToCmd fWaitToCmd;
    };

    hsTArray<plResponderState> fStates;

    Int8 fCurState;                 // The current state (first index for fCommandList)
    Int8 fCurCommand;               // The command we are currently waiting to send (or -1 if we're not sending)
    bool fNetRequest;               // Was the last trigger a net request
    hsBitVector fCompletedEvents;   // Which events that commands are waiting on have completed
    bool fEnabled;
    plKey fPlayerKey;               // The player who triggered this last
    plKey fTriggerer;               // Whoever triggered us (for sending notify callbacks)
    hsBool fEnter;                  // Is our current trigger a volume enter?
    bool fGotFirstLoad;             // Have we gotten our first SDL load?

    plResponderSDLModifier* fResponderSDLMod;       // handles saving and restoring state

    enum
    {
        kDetectTrigger      = 0x1,
        kDetectUnTrigger    = 0x2,
        kSkipFFSound        = 0x4
    };
    UInt8 fFlags;
    UInt32 fNotifyMsgFlags; // store the msg flags of the notify which triggered us

    void Trigger(plNotifyMsg *msg);
    bool IIsLocalOnlyCmd(plMessage* cmd);
    bool IContinueSending();

    Int8 ICmdFromWait(Int8 waitIdx);

    virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) { return true; }

    static bool fDebugAnimBox;  // Draws a box on screen when an animation is started
    static void IDebugAnimBox(bool start);
    void IDebugPlayMsg(plAnimCmdMsg* msg);

    // Trigger the responder (regardless of what it's doing) and "fast forward" it to the final state
    // If python is true, only run animations
    void IFastForward(bool python);
    // If the message is FF-able, returns it (or a FF-able version)
    plMessage* IGetFastForwardMsg(plMessage* msg, bool python);

    void ISetResponderStateFromNotify(plNotifyMsg* msg);
    void ISetResponderState(Int8 state);

    void ILog(UInt32 color, const char* format, ...);

    friend class plResponderComponent;
    friend class plResponderWait;

public:
    plResponderModifier();
    ~plResponderModifier();

    CLASSNAME_REGISTER( plResponderModifier );
    GETINTERFACE_ANY( plResponderModifier, plSingleModifier );
    
    virtual void Read(hsStream* stream, hsResMgr* mgr);
    virtual void Write(hsStream* stream, hsResMgr* mgr);

    virtual hsBool MsgReceive(plMessage* msg);

    const plResponderSDLModifier* GetSDLModifier() const { return fResponderSDLMod; }

    static bool ToggleDebugAnimBox() { return fDebugAnimBox = !fDebugAnimBox; }
    static void NoLogString(const char* str);

    // Restore callback state after load
    void Restore();
    
    const Int8 GetState() const { return fCurState; }
    //
    // Export time only
    //
    void AddCommand(plMessage* pMsg, int state=0);
    void AddCallback(Int8 state, Int8 cmd, Int8 callback);
};

// Message for changing the enable state in a responder modifier
class plResponderEnableMsg : public plMessage
{
public:
    bool fEnable;

    plResponderEnableMsg() : fEnable(true) {}
    plResponderEnableMsg(bool enable) : fEnable(enable) {}

    CLASSNAME_REGISTER(plResponderEnableMsg);
    GETINTERFACE_ANY(plResponderEnableMsg, plMessage);

    // IO 
    void Read(hsStream* stream, hsResMgr* mgr);
    void Write(hsStream* stream, hsResMgr* mgr);
};

#endif // plResponderModifier_inc