//=============================================================================
// Copyright (C) 2002 Radical Entertainment Ltd.  All rights reserved.
//
// File:        console.h
//
// Description: 
//
// History:     05/06/2002 + Created. Thanks to Mirth. -- Darwin Chau 
//
//=============================================================================

#ifndef CONSOLE_H
#define CONSOLE_H

//========================================
// Nested Includes
//========================================
#include <console/nameinsensitive.h>
#include <loading/loadingmanager.h>
#include <memory/srrmemory.h>
#include <memory/memorypool.h>
// Radcore
#include <raddebug.hpp>

#ifdef RAD_DEBUG
#include <raddebugconsole.hpp>
#endif

#include <p3d/entity.hpp>

#include <map>

//========================================
// Forward References
//========================================
struct IRadFile;

#ifdef RAD_DEBUG
struct IRadDebugConsole;
class DebugConsoleCallback;
#endif

//=============================================================================
//
// Synopsis:    
//
//=============================================================================

class Console : public LoadingManager::ProcessRequestsCallback
{
    public:

        //
        // Static Methods for accessing this singleton.
        //
        static Console* CreateInstance();
        static Console* GetInstance();
        static void DestroyInstance();

        //----------------------------------------------------------------------
        // Register functions with the console.
        //
        // Functions must be of format:
        //
        //   void Foo( int argc, const char** argv );
        //  
        // or  
        // 
        //   bool Foo( int argc, const char** argv );
        //
        typedef void (*CONSOLE_FUNCTION)(int argc, const char** argv);
        bool AddFunction( const char* name,
                          CONSOLE_FUNCTION funcPtr,
                          const char* helpString,
                          int minArgs,
                          int maxArgs );
        
        typedef bool (*CONSOLE_BOOL_FUNCTION)(int argc, const char** argv);
        bool AddFunction( const char* name,
                          CONSOLE_BOOL_FUNCTION funcPtr,
                          const char* helpString,
                          int minArgs,
                          int maxArgs );
        
        //
        // Register an alias to a function.
        //
        bool AddAlias( const char* name, const char* functionName, int argc, const char** argv );
        
        
        //
        // Async load and execute script file.
        //
        struct ExecuteScriptCallback
        {
            virtual void OnExecuteScriptComplete( void* pUserData ) = 0;   
        };
        
        void ExecuteScript( const char* filename, 
                            Console::ExecuteScriptCallback* pCallback, 
                            void* pUserData = 0, 
                            bool reload = false );
        
        //
        // Sync load and execute script file.
        //
        void ExecuteScriptSync( const char* filename, bool reload = false );

        
        //
        // Evaluate a string buffer.
        //
        bool Evaluate( const char* string, const char* inputSourceName );


        void Printf( const char* fmt, ... );
        void Printf( int channel, const char* fmt, ... );
        void Errorf( const char* fmt, ... );
        
        void AssertFatal( const char* fmt, ... );
        void AssertWarn( const char* fmt, ... );
        
        const tNameInsensitive& TabCompleteFunction( const char* tabString, const char* curFunction );

        void ListConsoleFunctions();

        void ConsoleListenChannel( int channel );
        void ConsoleBlockChannel( int channel );

        enum
        {
            LOGMODE_OFF,
            LOGMODE_ON,
            LOGMODE_APPEND
        };
        bool SetConsoleLogMode( int mode );
        void FlushConsoleLogFile();

        //utility functions
        bool atob(const char* value);

        //
        // LoadingManager::ProcessRequestsCallback
        //
        void OnProcessRequestsComplete( void* pUserData );

        enum
        {
            MAX_STRING_LENGTH = 256,
            MAX_FUNCTIONS = 256,
            MAX_FUNCTION_NAME = 32,
            MAX_HELP_LENGTH = 256,
            MAX_ALIAS_NAME = 32,
            MAX_ARGS = 16,
            MAX_ARG_LENGTH = 64,
            MAX_ALIASES = 32,
            MAX_BUFFERS = 16
        };

#ifdef RAD_DEBUG
    public:
        void AddDebugConsoleLine(const char* text, bool warning = false);
        void SetDebugConsoleEntry(const char* text, int cursorPosition);
    private:
        int mDebugConsoleLine;
        IRadDebugConsole* mDebugConsole;
        DebugConsoleCallback* mDebugConsoleCallback;
#endif

    private:
        Console();
        ~Console();

        // Prevent wasteful constructor creation.
        Console( const Console& );
        Console& operator=( const Console& );

        static bool Initialize();

        struct FunctionTableEntry
        {
            union
            {
                CONSOLE_FUNCTION voidReturn;
                CONSOLE_BOOL_FUNCTION boolReturn;
            } functionPointer;

            //char name[MAX_FUNCTION_NAME];
            tNameInsensitive name;
            tName help;
            //char help[MAX_HELP_LENGTH];
            int minArgs;
            int maxArgs;
            bool isConditional;

            static FBMemoryPool sMemoryPool;

            FunctionTableEntry( const char* in_name, CONSOLE_FUNCTION in_func, 
                                const char* in_help, int in_minArgs, int in_maxArgs );

            FunctionTableEntry( const char* in_name, CONSOLE_BOOL_FUNCTION in_func,
                                const char* in_help, int in_minArgs, int in_maxArgs );

            static void* operator new( size_t size ) { return( sMemoryPool.Allocate( size ) ); }
            static void  operator delete( void* deadObject, size_t size ) { sMemoryPool.Free( deadObject, size ); }

            // Declared but not defined to prevent access.
            static void* operator new[]( size_t size );
            static void  operator delete[]( void* pMemory );
        };

        struct AliasTableEntry
        {
            char name[MAX_ALIAS_NAME];
            char functionName[MAX_FUNCTION_NAME];
            int argc;
            char argv[MAX_ARGS][MAX_ARG_LENGTH];

            static FBMemoryPool sMemoryPool;

            AliasTableEntry( const char* srcName, const char* funcName, 
                             int srcArgc, const char** srcArgv );

            static void* operator new( size_t size ) { return( sMemoryPool.Allocate( size ) ); }
            static void  operator delete( void* deadObject, size_t size ) { sMemoryPool.Free( deadObject, size ); }

            // Declared but not defined to prevent access.
            static void* operator new[]( size_t size );
            static void  operator delete[]( void* pMemory );
        };

        // table of functions added by other modules through AddFunction()
        FunctionTableEntry* mFunctionTable[MAX_FUNCTIONS];
        int mFunctionCount;

        // nv:  to speed up the function name lookup
        std::map<radKey32, int> mFunctionLookup;

        AliasTableEntry* mAliasTable[MAX_ALIASES];
        int mAliasCount;

        // argv table for processing the function arguments
        int mArgc;
        char** mArgv;

        int mPrevArgc;
        char** mPrevArgv;

        //line number for reporting console errors
        int mLineNumber;
        char mFileName[256];

        //vars for echoing to a log file
        int mLogMode;
        IRadFile* mLogFile;

        //internal functions for parsing the string
        char* SkipWhiteSpace( const char* string );
        bool FoundComment( char** stringPtr );
        char* FindTokenEnd( const char* string );
        char* GetNextToken( char** stringPtr, char* tokenBuffer );
        bool ReadToken( char** stringPtr, const char* requiredToken );

        bool AddFunc( const char* name,
                      CONSOLE_FUNCTION funcPtr,
                      CONSOLE_BOOL_FUNCTION boolFuncPtr,
                      const char* helpString,
                      int minArgs,
                      int maxArgs );
        bool AddFunctionAlias(const char* name, const char* functionName, int argc, const char** argv);
        const tNameInsensitive& TabComplete(const char* tabString, const char* curFunction);
        void ListFunctions();

        bool SetLogMode(int mode);
        void LogMessage(int channel, char* msg, bool warning = false);
        void FlushLogFile();

        //setting up a channel system so you can eliminate console spam on your local system
        int mChannelMask;
        void ListenChannel(int channel);
        void BlockChannel(int channel);

        static Console* spInstance;
        ExecuteScriptCallback* mpCallback;
};

// A little syntactic sugar for getting at this singleton.
inline Console* GetConsole() { return( Console::GetInstance() ); }


#endif // CONSOLE_H