/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef __avmshell_core__
#define __avmshell_core__

// Ugly, ugly hack
namespace avmplus
{
    namespace ST_avmplus_vector_accessors
    {
        class ST_avmplus_vector_accessors;
    }
    namespace ST_mmgc_dependent
    {
        class ST_mmgc_dependent;
    }
}

namespace avmshell
{
    /**
     * Settings for ShellCore.  The command line parser uses this, but the initial state
     * is set in ShellCore.cpp, and it's propagated throughout various parts of the
     * system.  Partly duplicates the functionality in AvmCore::config, and some settings
     * are copied from ShellCoreSettings into that structure when a new core is created.
     *
     * We try to avoid conditional compilation here, as there's only ever one of these
     * structures floating around and the savings would be minuscule.
     *
     * FIXME: the naming of many fields could be much better, and they should be documented.
     */
    class ShellCoreSettings
    {
    public:
        ShellCoreSettings();

        char** arguments;               // non-terminated array of argument values, never NULL
        int numargs;                    // number of entries in 'arguments'
        bool nodebugger;
        int  astrace_console;
        uint32_t do_verbose;            // copy to config
        const char* verboseOnlyArg;     // copy to config (raw unprocessed)
        bool enter_debugger_on_launch;
        bool interrupts;                // copy to config
        bool verifyall;                 // copy to config
        bool verifyonly;                // copy to config
        bool verifyquiet;               // copy to config
        bool greedy;                    // copy to each GC
        bool nogc;                      // copy to each GC
        bool incremental;               // copy to each GC
        bool exactgc;                   // copy to each GC
        bool drc;                       // copy to each GC
        bool drcValidation;             // copy to each GC
        int32_t markstackAllowance;     // copy to each GC;
        bool fixedcheck;                // copy to each GC
        int gcthreshold;                // copy to each GC
        int langID;                     // copy to ShellCore?
        bool jitordie;                  // copy to config
        bool do_testSWFHasAS3;
        avmplus::Runmode runmode;       // copy to config
#ifdef VMCFG_NANOJIT
        nanojit::Config njconfig;       // copy to config
        avmplus::JitConfig jitconfig;   // copy to config
        bool osr_enabled;               // copy to config
        uint32_t osr_threshold;         // copy to config
        uint32_t jitprof_level;         // Log level for jit profiling
        const char* policyRulesArg;     // copy to config (raw unprocessed)
#endif
        avmplus::AvmCore::CacheSizes cacheSizes; // Default to unlimited
        const char* st_component;
        const char* st_category;
        const char* st_name;
        avmplus::ApiVersionSeries apiVersionSeries;
        avmplus::ApiVersion apiVersion;
        avmplus::BugCompatibility::Version swfVersion;

        MMgc::GCConfig::GCMode gcMode()
        {
            if (nogc)               return MMgc::GCConfig::kDisableGC;
            else if (greedy)        return MMgc::GCConfig::kGreedyGC;
            else if (incremental)   return MMgc::GCConfig::kIncrementalGC;
            else                    return MMgc::GCConfig::kNonincrementalGC;
        }
    };

    class ShellCodeContext : public avmplus::CodeContext
    {
    public:
        inline ShellCodeContext(avmplus::DomainEnv* env, const avmplus::BugCompatibility* bugCompatibility)
            : avmplus::CodeContext(env, bugCompatibility) { }
    };

    /**
     * ShellCore provides typical housekeeping tasks around an AvmCore, lightweight
     * embedders may prefer to use this rather than to subclass AvmCore themselves
     * and do all the housekeeping that results.  The shell uses this; see the
     * shell code for typical usage patterns.
     */
    class ShellCore : public avmplus::AvmCore
    {
        friend class SystemClass;
        friend class avmplus::DomainObject;
        friend class avmplus::ST_avmplus_vector_accessors::ST_avmplus_vector_accessors;
        friend class avmplus::ST_mmgc_dependent::ST_mmgc_dependent;
        friend class Shell; // access to shell_toplevel
    public:
        /**
         * Create a new core with the given GC (one gc per core).
         *
         * Requires: MMGC_ENTER and MMGC_GCENTER(gc) on the stack.
         */
        ShellCore(MMgc::GC *gc, avmplus::ApiVersionSeries apiVersionSeries);

        /**
         * Initialize the new core from the settings.  This creates a top-level
         * environment and performs other housekeeping.
         *
         * Requires: MMGC_ENTER and MMGC_GCENTER(gc) on the stack.
         */
        ShellToplevel* setup(const ShellCoreSettings& settings);

        /**
         * Load the contents from the file and run them in the context of this core's
         * top level.  The file may contain ABC bytecode, a SWF containing a single DoABC
         * tag, or (if the run-time compiler is present) ActionScript source code.
         *
         * Requires: MMGC_ENTER and MMGC_GCENTER(gc) on the stack.
         */
        int evaluateFile(ShellCoreSettings& settings, const char* filename);

        int evaluateScriptBuffer(avmplus::ScriptBuffer& buffer, bool enter_debugger_on_launch);


#ifdef VMCFG_EVAL
        /**
         * Load ActionScript source code from the string and run it in the
         * context of this core's top level.  Note the string must have been
         * created in the context of the GC in this ShellCore instance.
         *
         * Requires: MMGC_ENTER and MMGC_GCENTER(gc) on the stack.
         */
        void evaluateString(avmplus::String* input, bool record_time=false);
#endif

#ifdef AVMSHELL_PROJECTOR_SUPPORT
        int executeProjector(char *executablePath);
#endif

#ifdef VMCFG_SELFTEST
        void executeSelftest(ShellCoreSettings& settings);
#endif

        avmplus::PoolObject* getShellPool() { return shellPool; }

        avmshell::SystemClass* getSystemClass() { return systemClass; }

#ifdef AVMSHELL_PROJECTOR_SUPPORT
        static bool isValidProjectorFile(const char* filename);
#endif

        /*virtual*/ inline avmplus::ApiVersion getDefaultAPI() { return this->defaultAPIVersion; }

        inline avmplus::BugCompatibility::Version getDefaultBugCompatibilityVersion() const { return defaultBugCompatibilityVersion; }

    protected:
        virtual void setStackLimit() = 0;

        virtual avmplus::Toplevel* createToplevel(avmplus::AbcEnv* abcEnv);
#ifdef DEBUGGER
        virtual avmplus::Debugger* createDebugger(int tracelevel)
        {
            AvmAssert(allowDebugger >= 0);
            return allowDebugger ? DebugCLI::create(GetGC(), this, (avmplus::Debugger::TraceLevel)tracelevel) : NULL;
        }

#endif
#ifdef VMCFG_EVAL
        virtual avmplus::String* readFileForEval(avmplus::String* referencing_filename, avmplus::String* filename);
#endif

    private:
        static void interruptTimerCallback(void* data);

        ShellToplevel* createShellToplevel();

        void interrupt(avmplus::Toplevel*, InterruptReason);
        void stackOverflow(avmplus::Toplevel *toplevel);
        void setEnv(avmplus::Toplevel *toplevel, int argc, char *argv[]);
        void initShellPool();
        int handleArbitraryExecutableContent(bool do_testSWFHasAS3, avmplus::ScriptBuffer& code, const char * filename);
#ifdef VMCFG_EVAL
        avmplus::String* decodeBytesAsUTF16String(uint8_t* bytes, uint32_t nbytes, bool terminate=false);
#endif // VMCFG_EVAL
#ifdef DEBUGGER
        DebugCLI* debugCLI() { return (DebugCLI*)debugger(); }
#endif

#ifdef AVMPLUS_VERBOSE
        virtual const char* identifyDomain(avmplus::Domain* domain);
#endif
        SystemClass* systemClass;
        avmplus::PoolObject* shellPool;
        avmplus::GCOutputStream *consoleOutputStream;
        bool gracePeriod;
        bool inStackOverflow;
        int allowDebugger;
        ShellToplevel* shell_toplevel;
        avmplus::DomainEnv* shell_domainEnv;
        avmplus::Domain* shell_domain;
        // Note that this has been renamed to emphasize the fact that it is
        // the CodeContext/DomainEnv that user code will run in (as opposed
        // to the Shell's builtin classes, eg System, File, Domain).
        ShellCodeContext* user_codeContext;
        avmplus::ApiVersion defaultAPIVersion;
        avmplus::BugCompatibility::Version defaultBugCompatibilityVersion;
    };

    class GC_CPP_EXACT(ShellToplevel, avmplus::Toplevel)
    {
        friend class ShellCore;

    private:
        ShellToplevel(avmplus::AbcEnv* abcEnv);

    public:
        REALLY_INLINE static ShellToplevel* create(MMgc::GC* gc, avmplus::AbcEnv* abcEnv)
        {
            return new (gc, MMgc::kExact) ShellToplevel(abcEnv);
        }

        ShellCore* core() const {
            return (ShellCore*)Toplevel::core();
        }

        GCRef<avmplus::ClassClosure> workerClass() const 
        {
            return shellClasses->get_WorkerClass();
        }

        GCRef<avmplus::ClassClosure> workerDomainClass() const 
        {
            return shellClasses->get_WorkerDomainClass();
        }

        GCRef<avmplus::ScriptObject> constructWorkerObject() const;
        
    private:
        GC_DATA_BEGIN(ShellToplevel)

        GCMember<avmplus::shell_toplevelClassManifest> GC_POINTER(shellClasses);

        GC_DATA_END(ShellToplevel)
    };
}

#endif /* __avmshell_core__ */
