/**
 * Sets the new recompile supervisor code flag.
 *
 * @returns COM status code
 * @param   aEnable new recompile supervisor code flag
 */
STDMETHODIMP MachineDebugger::COMSETTER(RecompileSupervisor)(BOOL aEnable)
{
    LogFlowThisFunc(("enable=%d\n", aEnable));

    AutoCaller autoCaller(this);
    HRESULT hrc = autoCaller.rc();
    if (SUCCEEDED(hrc))
    {
        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
        if (queueSettings())
            mRecompileSupervisorQueued = aEnable; // queue the request
        else
        {
            Console::SafeVMPtr ptrVM(mParent);
            hrc = ptrVM.rc();
            if (SUCCEEDED(hrc))
            {
                int vrc = EMR3SetExecutionPolicy(ptrVM.raw(), EMEXECPOLICY_RECOMPILE_RING0, RT_BOOL(aEnable));
                if (RT_FAILURE(vrc))
                    hrc = setError(VBOX_E_VM_ERROR, tr("EMR3SetExecutionPolicy failed with %Rrc"), vrc);
            }
        }
    }
    return hrc;
}
/**
 * Sets the new recompile supervisor code flag.
 *
 * @returns COM status code
 * @param   enable new recompile supervisor code flag
 */
STDMETHODIMP MachineDebugger::COMSETTER(RecompileSupervisor)(BOOL enable)
{
    LogFlow(("MachineDebugger:: set supervisor mode recompiler to %d\n", enable));

    if (!fFlushMode)
    {
        // check if the machine is running
        if (machineState != VMSTATE_RUNNING)
        {
            // queue the request
            recompileSupervisorQueued = enable;
            return S_OK;
        }
    }
    if (!gpVM)
        return E_FAIL;

    int rcVBox = EMR3SetExecutionPolicy(gpVM, EMEXECPOLICY_RECOMPILE_RING0, enable);
    AssertRCReturn(rcVBox, E_FAIL);
    return S_OK;
}
/**
 * Internal worker for setting an EM executable policy.
 *
 * @returns COM status code.
 * @param   enmPolicy           Which policy to change.
 * @param   fEnforce            Whether to enforce the policy or not.
 */
HRESULT MachineDebugger::i_setEmExecPolicyProperty(EMEXECPOLICY enmPolicy, BOOL fEnforce)
{
    AutoCaller autoCaller(this);
    HRESULT hrc = autoCaller.rc();
    if (SUCCEEDED(hrc))
    {
        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
        if (i_queueSettings())
            maiQueuedEmExecPolicyParams[enmPolicy] = fEnforce ? 1 : 0;
        else
        {
            Console::SafeVMPtrQuiet ptrVM(mParent);
            hrc = ptrVM.rc();
            if (SUCCEEDED(hrc))
            {
                int vrc = EMR3SetExecutionPolicy(ptrVM.rawUVM(), enmPolicy, fEnforce != FALSE);
                if (RT_FAILURE(vrc))
                    hrc = setError(VBOX_E_VM_ERROR, tr("EMR3SetExecutionPolicy failed with %Rrc"), vrc);
            }
        }
    }
    return hrc;
}
int main(int argc, char **argv)
{
    int rcRet = 1;
    int rc;
    RTR3InitAndSUPLib();

    /*
     * Parse input.
     */
    if (argc <= 1)
    {
        syntax();
        return 1;
    }

    bool        fPowerOn = false;
    uint32_t    u32WarpDrive = 100; /* % */
    uint64_t    cbMem = ~0ULL;
    const char *pszSavedState = NULL;
    const char *pszRawMem = NULL;
    uint64_t    offRawMem = 0;
    const char *pszScript = NULL;
    for (int i = 1; i < argc; i++)
    {
        if (argv[i][0] == '-')
        {
            /* check that it's on short form */
            if (argv[i][2])
            {
                if (    strcmp(argv[i], "--help")
                    &&  strcmp(argv[i], "-help"))
                    RTPrintf("tstAnimate: Syntax error: Unknown argument '%s'.\n", argv[i]);
                else
                    syntax();
                return 1;
            }

            /* check for 2nd argument */
            switch (argv[i][1])
            {
                case 'r':
                case 'o':
                case 'c':
                case 'm':
                case 'w':
                case 'z':
                    if (i + 1 < argc)
                        break;
                    RTPrintf("tstAnimate: Syntax error: '%s' takes a 2nd argument.\n", argv[i]);
                    return 1;
            }

            /* process argument */
            switch (argv[i][1])
            {
                case 'r':
                    pszRawMem = argv[++i];
                    break;

                case 'z':
                    pszSavedState = argv[++i];
                    break;

                case 'o':
                {
                    rc = RTStrToUInt64Ex(argv[++i], NULL, 0, &offRawMem);
                    if (RT_FAILURE(rc))
                    {
                        RTPrintf("tstAnimate: Syntax error: Invalid offset given to -o.\n");
                        return 1;
                    }
                    break;
                }

                case 'm':
                {
                    char *pszNext;
                    rc = RTStrToUInt64Ex(argv[++i], &pszNext, 0, &cbMem);
                    if (RT_FAILURE(rc))
                    {
                        RTPrintf("tstAnimate: Syntax error: Invalid memory size given to -m.\n");
                        return 1;
                    }
                    switch (*pszNext)
                    {
                        case 'G':   cbMem *= _1G; pszNext++; break;
                        case 'M':   cbMem *= _1M; pszNext++; break;
                        case 'K':   cbMem *= _1K; pszNext++; break;
                        case '\0':  break;
                        default:
                            RTPrintf("tstAnimate: Syntax error: Invalid memory size given to -m.\n");
                            return 1;
                    }
                    if (*pszNext)
                    {
                        RTPrintf("tstAnimate: Syntax error: Invalid memory size given to -m.\n");
                        return 1;
                    }
                    break;
                }

                case 's':
                    pszScript = argv[++i];
                    break;

                case 'p':
                    fPowerOn = true;
                    break;

                case 'w':
                {
                    rc = RTStrToUInt32Ex(argv[++i], NULL, 0, &u32WarpDrive);
                    if (RT_FAILURE(rc))
                    {
                        RTPrintf("tstAnimate: Syntax error: Invalid number given to -w.\n");
                        return 1;
                    }
                    break;
                }

                case 'h':
                case 'H':
                case '?':
                    syntax();
                    return 1;

                default:
                    RTPrintf("tstAnimate: Syntax error: Unknown argument '%s'.\n", argv[i]);
                    return 1;
            }
        }
        else
        {
            RTPrintf("tstAnimate: Syntax error at arg no. %d '%s'.\n", i, argv[i]);
            syntax();
            return 1;
        }
    }

    /*
     * Check that the basic requirements are met.
     */
    if (pszRawMem && pszSavedState)
    {
        RTPrintf("tstAnimate: Syntax error: Either -z or -r, not both.\n");
        return 1;
    }
    if (!pszRawMem && !pszSavedState)
    {
        RTPrintf("tstAnimate: Syntax error: The -r argument is compulsory.\n");
        return 1;
    }

    /*
     * Open the files.
     */
    RTFILE FileRawMem = NIL_RTFILE;
    if (pszRawMem)
    {
        rc = RTFileOpen(&FileRawMem, pszRawMem, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
        if (RT_FAILURE(rc))
        {
            RTPrintf("tstAnimate: error: Failed to open '%s': %Rrc\n", pszRawMem, rc);
            return 1;
        }
    }
    RTFILE FileScript = NIL_RTFILE;
    if (pszScript)
    {
        rc = RTFileOpen(&FileScript, pszScript, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
        if (RT_FAILURE(rc))
        {
            RTPrintf("tstAnimate: error: Failed to open '%s': %Rrc\n", pszScript, rc);
            return 1;
        }
    }

    /*
     * Figure the memsize if not specified.
     */
    if (cbMem == ~0ULL)
    {
        if (FileRawMem != NIL_RTFILE)
        {
            rc = RTFileGetSize(FileRawMem, &cbMem);
            AssertReleaseRC(rc);
            cbMem -= offRawMem;
            cbMem &= ~(PAGE_SIZE - 1);
        }
        else
        {
            RTPrintf("tstAnimate: error: too lazy to figure out the memsize in a saved state.\n");
            return 1;
        }
    }
    RTPrintf("tstAnimate: info: cbMem=0x%llx bytes\n", cbMem);

    /*
     * Open a release log.
     */
    static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
    PRTLOGGER pRelLogger;
    rc = RTLogCreate(&pRelLogger, RTLOGFLAGS_PREFIX_TIME_PROG, "all", "VBOX_RELEASE_LOG",
                     RT_ELEMENTS(s_apszGroups), s_apszGroups, RTLOGDEST_FILE, "./tstAnimate.log");
    if (RT_SUCCESS(rc))
        RTLogRelSetDefaultInstance(pRelLogger);
    else
        RTPrintf("tstAnimate: rtLogCreateEx failed - %Rrc\n", rc);

    /*
     * Create empty VM.
     */
    PVM pVM;
    rc = VMR3Create(1, NULL, NULL, NULL, cfgmR3CreateDefault, &cbMem, &pVM);
    if (RT_SUCCESS(rc))
    {
        /*
         * Load memory.
         */
        if (FileRawMem != NIL_RTFILE)
            rc = VMR3ReqCallWait(pVM, VMCPUID_ANY, (PFNRT)loadMem, 3, pVM, FileRawMem, &offRawMem);
        else
            rc = VMR3ReqCallWait(pVM, VMCPUID_ANY, (PFNRT)SSMR3Load,
                                 7, pVM, pszSavedState, (uintptr_t)NULL /*pStreamOps*/, (uintptr_t)NULL /*pvUser*/,
                                 SSMAFTER_DEBUG_IT, (uintptr_t)NULL /*pfnProgress*/, (uintptr_t)NULL /*pvProgressUser*/);
        if (RT_SUCCESS(rc))
        {
            /*
             * Load register script.
             */
            if (FileScript != NIL_RTFILE)
                rc = VMR3ReqCallWait(pVM, VMCPUID_ANY, (PFNRT)scriptRun, 2, pVM, FileScript);
            if (RT_SUCCESS(rc))
            {
                if (fPowerOn)
                {
                    /*
                     * Adjust warpspeed?
                     */
                    if (u32WarpDrive != 100)
                    {
                        rc = TMR3SetWarpDrive(pVM, u32WarpDrive);
                        if (RT_FAILURE(rc))
                            RTPrintf("warning: TMVirtualSetWarpDrive(,%u) -> %Rrc\n", u32WarpDrive, rc);
                    }

                    /*
                     * Start the thing with single stepping and stuff enabled.
                     * (Try make sure we don't execute anything in raw mode.)
                     */
                    RTPrintf("info: powering on the VM...\n");
                    RTLogGroupSettings(NULL, "+REM_DISAS.e.l.f");
                    rc = REMR3DisasEnableStepping(pVM, true);
                    if (RT_SUCCESS(rc))
                    {
                        rc = EMR3SetExecutionPolicy(pVM, EMEXECPOLICY_RECOMPILE_RING0, true); AssertReleaseRC(rc);
                        rc = EMR3SetExecutionPolicy(pVM, EMEXECPOLICY_RECOMPILE_RING3, true); AssertReleaseRC(rc);
                        DBGFR3Info(pVM, "cpumguest", "verbose", NULL);
                        if (fPowerOn)
                            rc = VMR3PowerOn(pVM);
                        if (RT_SUCCESS(rc))
                        {
                            RTPrintf("info: VM is running\n");
                            signal(SIGINT, SigInterrupt);
                            while (!g_fSignaled)
                                RTThreadSleep(1000);
                        }
                        else
                            RTPrintf("error: Failed to power on the VM: %Rrc\n", rc);
                    }
                    else
                        RTPrintf("error: Failed to enabled singlestepping: %Rrc\n", rc);
                }
                else
                {
                    /*
                     * Don't start it, just enter the debugger.
                     */
                    RTPrintf("info: entering debugger...\n");
                    DBGFR3Info(pVM, "cpumguest", "verbose", NULL);
                    signal(SIGINT, SigInterrupt);
                    while (!g_fSignaled)
                        RTThreadSleep(1000);
                }
                RTPrintf("info: shutting down the VM...\n");
            }
            /* execScript complains */
        }
        else if (FileRawMem == NIL_RTFILE) /* loadMem complains, SSMR3Load doesn't */
            RTPrintf("tstAnimate: error: SSMR3Load failed: rc=%Rrc\n", rc);
        rcRet = RT_SUCCESS(rc) ? 0 : 1;

        /*
         * Cleanup.
         */
        rc = VMR3Destroy(pVM);
        if (!RT_SUCCESS(rc))
        {
            RTPrintf("tstAnimate: error: failed to destroy vm! rc=%Rrc\n", rc);
            rcRet++;
        }
    }
    else
    {
        RTPrintf("tstAnimate: fatal error: failed to create vm! rc=%Rrc\n", rc);
        rcRet++;
    }

    return rcRet;
}