void CDebuggerUI::TLBChanged()
{
    Debug_RefreshTLBWindow();
}

#include "stdafx.h"
#include "DebuggerUI.h"

#include "CPULog.h"
#include "DMALog.h"
#include "Symbols.h"
#include <sstream>

CPj64Module _Module;

CDebuggerUI::CDebuggerUI() :
    m_MemoryDump(nullptr),
    m_MemoryView(nullptr),
    m_MemorySearch(nullptr),
    m_DebugTLB(nullptr),
    m_CommandsView(nullptr),
    m_Scripts(nullptr),
    m_Symbols(nullptr),
    m_Breakpoints(nullptr),
    m_ScriptSystem(nullptr),
    m_StackTrace(nullptr),
    m_StackView(nullptr),
    m_DMALogView(nullptr),
    m_CPULogView(nullptr),
    m_ExcBreakpoints(nullptr),
    m_DMALog(nullptr),
    m_CPULog(nullptr),
    m_SymbolTable(nullptr),
    m_StepEvent(false)
{
    g_Debugger = this;

    m_Breakpoints = new CBreakpoints();
    m_ScriptSystem = new CScriptSystem(this);

    m_DMALog = new CDMALog();
    m_CPULog = new CCPULog();
    m_SymbolTable = new CSymbolTable(this);

    g_Settings->RegisterChangeCB(GameRunning_InReset, this, (CSettings::SettingChangedFunc)GameReset);
    g_Settings->RegisterChangeCB(Debugger_SteppingOps, this, (CSettings::SettingChangedFunc)SteppingOpsChanged);
    g_Settings->RegisterChangeCB(GameRunning_CPU_Running, this, (CSettings::SettingChangedFunc)GameCpuRunningChanged);
    g_Settings->RegisterChangeCB(Game_GameName, this, (CSettings::SettingChangedFunc)GameNameChanged);
    g_Settings->RegisterChangeCB(GameRunning_CPU_Paused, this, (CSettings::SettingChangedFunc)GamePausedChanged);
    g_Settings->RegisterChangeCB(Debugger_WaitingForStep, this, (CSettings::SettingChangedFunc)WaitingForStepChanged);
}

CDebuggerUI::~CDebuggerUI(void)
{
    g_Settings->UnregisterChangeCB(Debugger_SteppingOps, this, (CSettings::SettingChangedFunc)SteppingOpsChanged);
    g_Settings->UnregisterChangeCB(GameRunning_InReset, this, (CSettings::SettingChangedFunc)GameReset);
    g_Settings->RegisterChangeCB(GameRunning_CPU_Running, this, (CSettings::SettingChangedFunc)GameCpuRunningChanged);
    g_Settings->UnregisterChangeCB(Game_GameName, this, (CSettings::SettingChangedFunc)GameNameChanged);
    g_Settings->UnregisterChangeCB(GameRunning_CPU_Paused, this, (CSettings::SettingChangedFunc)GamePausedChanged);
    g_Settings->UnregisterChangeCB(Debugger_WaitingForStep, this, (CSettings::SettingChangedFunc)WaitingForStepChanged);
    Debug_Reset();
    delete m_MemoryView;
    delete m_CommandsView;
    delete m_Scripts;
    delete m_ScriptSystem;
    delete m_Breakpoints;
    delete m_Symbols;
    delete m_MemorySearch;
    delete m_StackTrace;
    delete m_DMALogView;
    delete m_CPULogView;
    delete m_ExcBreakpoints;
    delete m_DMALog;
    delete m_CPULog;
    delete m_SymbolTable;
}

void CDebuggerUI::SteppingOpsChanged(CDebuggerUI * _this)
{
    if (g_Settings->LoadBool(Debugger_SteppingOps))
    {
        if (!g_Settings->LoadBool(Debugger_SilentBreak))
        {
            _this->OpenCommandWindow();
        }
        g_Settings->SaveBool(Debugger_SilentBreak, false);
    }
}

void CDebuggerUI::GameCpuRunningChanged(CDebuggerUI * _this)
{
    if (!g_Settings->LoadBool(GameRunning_CPU_Running))
    {
        if (_this->m_MemorySearch)
        {
            _this->m_MemorySearch->GameReset();
        }
    }
}

void CDebuggerUI::GameNameChanged(CDebuggerUI * _this)
{
    if (_this->m_MemorySearch)
    {
        _this->m_MemorySearch->GameReset();
    }

    JSHookEmuStateChangeEnv env;
    env.state = JS_EMU_LOADED_ROM;
    _this->m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_EMUSTATECHANGE, &env);
}

void CDebuggerUI::GamePausedChanged(CDebuggerUI * _this)
{
    JSHookEmuStateChangeEnv env;
    env.state = g_Settings->LoadBool(GameRunning_CPU_Paused) ? JS_EMU_PAUSED : JS_EMU_RESUMED;
    _this->m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_EMUSTATECHANGE, &env);
}

void CDebuggerUI::WaitingForStepChanged(CDebuggerUI* _this)
{
    if (g_Settings->LoadBool(Debugger_WaitingForStep))
    {
        JSHookEmuStateChangeEnv env;
        env.state = JS_EMU_DEBUG_PAUSED;
        _this->ScriptSystem()->InvokeAppCallbacks(JS_HOOK_EMUSTATECHANGE, &env);
    }
    else
    {
        JSHookEmuStateChangeEnv env;
        env.state = JS_EMU_DEBUG_RESUMED;
        _this->ScriptSystem()->InvokeAppCallbacks(JS_HOOK_EMUSTATECHANGE, &env);
    }
}

void CDebuggerUI::GameReset(CDebuggerUI * _this)
{
    if (!g_Settings->LoadBool(GameRunning_InReset))
    {
        JSHookEmuStateChangeEnv env;
        env.state = JS_EMU_RESET;
        _this->m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_EMUSTATECHANGE, &env);
        return;
    }

    JSHookEmuStateChangeEnv env;
    env.state = JS_EMU_RESETTING;
    _this->m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_EMUSTATECHANGE, &env);

    if (_this->m_CommandsView)
    {
        _this->m_CommandsView->Reset();
    }

    if (_this->m_DMALogView)
    {
        _this->m_DMALogView->RefreshDMALogWindow(true);
    }

    if (_this->m_StackTrace)
    {
        _this->m_StackTrace->ClearEntries();
    }

    if (_this->m_SymbolTable)
    {
        _this->m_SymbolTable->Load();
    }

    if (_this->m_Symbols)
    {
        _this->m_Symbols->Refresh();
    }
}

void CDebuggerUI::Debug_Reset(void)
{
    if (m_MemoryDump)
    {
        m_MemoryDump->HideWindow();
        delete m_MemoryDump;
        m_MemoryDump = nullptr;
    }
    if (m_MemorySearch)
    {
        m_MemorySearch->HideWindow();
        delete m_MemorySearch;
        m_MemorySearch = nullptr;
    }
    if (m_DebugTLB)
    {
        m_DebugTLB->HideWindow();
        delete m_DebugTLB;
        m_DebugTLB = nullptr;
    }
    if (m_MemoryView)
    {
        m_MemoryView->HideWindow();
        delete m_MemoryView;
        m_MemoryView = nullptr;
    }
    if (m_CommandsView)
    {
        m_CommandsView->HideWindow();
        delete m_CommandsView;
        m_CommandsView = nullptr;
    }
    if (m_Scripts)
    {
        m_Scripts->HideWindow();
        delete m_Scripts;
        m_Scripts = nullptr;
    }
    if (m_Symbols)
    {
        m_Symbols->HideWindow();
        delete m_Symbols;
        m_Symbols = nullptr;
    }
    if (m_DMALogView)
    {
        m_DMALogView->HideWindow();
        delete m_DMALogView;
        m_DMALogView = nullptr;
    }
    if (m_CPULogView)
    {
        m_CPULogView->HideWindow();
        delete m_CPULogView;
        m_CPULogView = nullptr;
    }
    if (m_StackTrace)
    {
        m_StackTrace->HideWindow();
        delete m_StackTrace;
        m_StackTrace = nullptr;
    }
    if (m_StackView)
    {
        m_StackView->HideWindow();
        delete m_StackView;
        m_StackView = nullptr;
    }
    if (m_ExcBreakpoints)
    {
        m_ExcBreakpoints->HideWindow();
        delete m_ExcBreakpoints;
        m_ExcBreakpoints = nullptr;
    }
}

void CDebuggerUI::OpenMemoryDump()
{
    if (g_MMU == nullptr)
    {
        return;
    }
    if (m_MemoryDump == nullptr)
    {
        m_MemoryDump = new CDumpMemory(this);
    }
    if (m_MemoryDump)
    {
        m_MemoryDump->ShowWindow();
    }
}

void CDebuggerUI::OpenMemoryWindow(void)
{
    if (m_MemoryView == nullptr)
    {
        m_MemoryView = new CDebugMemoryView(this);
    }
    if (m_MemoryView)
    {
        m_MemoryView->ShowWindow();
    }
}

void CDebuggerUI::Debug_ShowMemoryLocation(uint32_t Address, bool VAddr)
{
    OpenMemoryWindow();
    if (m_MemoryView)
    {
        m_MemoryView->ShowAddress(Address, VAddr);
    }
}

void CDebuggerUI::OpenTLBWindow(void)
{
    if (g_MMU == nullptr)
    {
        return;
    }
    if (m_DebugTLB == nullptr)
    {
        m_DebugTLB = new CDebugTlb(this);
    }
    if (m_DebugTLB)
    {
        m_DebugTLB->ShowWindow();
    }
}

void CDebuggerUI::Debug_RefreshTLBWindow(void)
{
    if (m_DebugTLB)
    {
        m_DebugTLB->RefreshTLBWindow();
    }
}

void CDebuggerUI::Debug_RefreshDMALogWindow(void)
{
    if (m_DMALogView)
    {
        m_DMALogView->RefreshDMALogWindow();
    }
}

void CDebuggerUI::OpenMemorySearch()
{
    if (m_MemorySearch == nullptr)
    {
        m_MemorySearch = new CDebugMemorySearch(this);
    }
    if (m_MemorySearch)
    {
        m_MemorySearch->ShowWindow();
    }
}

void CDebuggerUI::OpenCommandWindow()
{
    if (m_CommandsView == nullptr)
    {
        m_CommandsView = new CDebugCommandsView(this, m_StepEvent);
    }
    m_CommandsView->ShowWindow();
}

void CDebuggerUI::Debug_ShowCommandsLocation(uint32_t address, bool top)
{
    OpenCommandWindow();
    if (m_CommandsView)
    {
        m_CommandsView->ShowAddress(address, top);
    }
}

void CDebuggerUI::OpenScriptsWindow()
{
    if (m_Scripts == nullptr)
    {
        m_Scripts = new CDebugScripts(this);
    }
    m_Scripts->ShowWindow();
}

void CDebuggerUI::Debug_RefreshScriptsWindow()
{
    if (m_Scripts != nullptr)
    {
        m_Scripts->RefreshList();
    }
}

void CDebuggerUI::Debug_LogScriptsWindow(const char* text)
{
    if (m_Scripts != nullptr)
    {
        m_Scripts->ConsolePrint(text);
    }
}

void CDebuggerUI::Debug_ClearScriptsWindow()
{
    if (m_Scripts != nullptr)
    {
        m_Scripts->ConsoleClear();
    }
}

void CDebuggerUI::OpenSymbolsWindow()
{
    if (m_Symbols == nullptr)
    {
        m_Symbols = new CDebugSymbols(this);
    }
    m_Symbols->ShowWindow();
}

void CDebuggerUI::Debug_RefreshSymbolsWindow()
{
    if (m_Symbols != nullptr)
    {
        m_Symbols->Refresh();
    }
}

void CDebuggerUI::OpenDMALogWindow(void)
{
    if (m_DMALogView == nullptr)
    {
        m_DMALogView = new CDebugDMALogView(this);
    }
    m_DMALogView->ShowWindow();
}

void CDebuggerUI::OpenCPULogWindow(void)
{
    if (m_CPULogView == nullptr)
    {
        m_CPULogView = new CDebugCPULogView(this);
    }
    m_CPULogView->ShowWindow();
}

void CDebuggerUI::OpenExcBreakpointsWindow(void)
{
    if (m_ExcBreakpoints == nullptr)
    {
        m_ExcBreakpoints = new CDebugExcBreakpoints(this);
    }
    m_ExcBreakpoints->ShowWindow();
}

void CDebuggerUI::OpenStackTraceWindow(void)
{
    if (m_StackTrace == nullptr)
    {
        m_StackTrace = new CDebugStackTrace(this);
    }
    m_StackTrace->ShowWindow();
}

void CDebuggerUI::OpenStackViewWindow(void)
{
    if (m_StackView == nullptr)
    {
        m_StackView = new CDebugStackView(this);
    }
    m_StackView->ShowWindow();
}

void CDebuggerUI::Debug_RefreshStackWindow(void)
{
    if (m_StackView != nullptr)
    {
        m_StackView->Refresh();
    }
}

void CDebuggerUI::Debug_RefreshStackTraceWindow(void)
{
    if (m_StackTrace != nullptr && m_StackTrace->m_hWnd != nullptr)
    {
        m_StackTrace->Refresh();
    }
}

void CDebuggerUI::Debug_RefreshCPULogWindow(void)
{
    if (m_CPULogView != nullptr)
    {
        m_CPULogView->RefreshList();
    }
}

CBreakpoints* CDebuggerUI::Breakpoints()
{
    return m_Breakpoints;
}

CScriptSystem* CDebuggerUI::ScriptSystem()
{
    return m_ScriptSystem;
}

CDebugScripts* CDebuggerUI::ScriptConsole()
{
    return m_Scripts;
}

CDMALog* CDebuggerUI::DMALog()
{
    return m_DMALog;
}

CCPULog* CDebuggerUI::CPULog()
{
    return m_CPULog;
}

CSymbolTable* CDebuggerUI::SymbolTable()
{
    return m_SymbolTable;
}

SyncEvent& CDebuggerUI::StepEvent()
{
    return m_StepEvent;
}

// CDebugger implementation

// Exception handling - break on exception vector if exception breakpoint is set
void CDebuggerUI::HandleCPUException(void)
{
    int exc = (g_Reg->CAUSE_REGISTER >> 2) & 0x1F;
    int intr = (g_Reg->CAUSE_REGISTER >> 8) & 0xFF;
    int fpExc = (g_Reg->m_FPCR[31] >> 12) & 0x3F;
    int rcpIntr = g_Reg->MI_INTR_REG & 0x2F;

    if ((ExceptionBreakpoints() & (1 << exc)))
    {
        if (exc == 15) // Floating-point exception
        {
            if (fpExc & FpExceptionBreakpoints())
            {
                goto have_bp;
            }
            return;
        }
        else if (exc == 0) // Interrupt exception
        {
            if (intr & IntrBreakpoints())
            {
                if (intr & 0x04) // RCP interrupt (IP2)
                {
                    if (rcpIntr & RcpIntrBreakpoints())
                    {
                        goto have_bp;
                    }
                    return;
                }
                else // Other interrupts
                {
                    goto have_bp;
                }
            }
            return;
        }
        else // Other exceptions
        {
            goto have_bp;
        }
    }

    return;

have_bp:
    if (bCPULoggingEnabled())
    {
        g_Debugger->OpenCPULogWindow();
    }

    g_Settings->SaveBool(Debugger_SteppingOps, true);
}

void CDebuggerUI::HandleCartToRamDMA(void)
{
    COpInfo opInfo(R4300iOp::m_Opcode);

    uint32_t dmaRomAddr = g_Reg->PI_CART_ADDR_REG & 0x0FFFFFFF;
    uint32_t dmaRamAddr = g_Reg->PI_DRAM_ADDR_REG | 0x80000000;
    uint32_t dmaLen = opInfo.GetStoreValueUnsigned() + 1;

    m_DMALog->AddEntry(dmaRomAddr, dmaRamAddr, dmaLen);
    Debug_RefreshDMALogWindow();

    // Break if write breakpoint exists anywhere in target buffer
    if (m_Breakpoints->WriteBPExistsInChunk(dmaRamAddr, dmaLen))
    {
        g_Settings->SaveBool(Debugger_SteppingOps, true);
    }
}

// Called from the interpreter core at the beginning of every CPU step
void CDebuggerUI::CPUStepStarted()
{
    if (isStepping() && bCPULoggingEnabled())
    {
        Debug_RefreshCPULogWindow();
    }

    if(m_Breakpoints->NumMemLocks() > 0)
    {
        COpInfo opInfo(R4300iOp::m_Opcode);
        bool bStoreOp = opInfo.IsStoreCommand();

        if (bStoreOp)
        {
            uint32_t storeAddress = bStoreOp ? opInfo.GetLoadStoreAddress() : 0;
            if (m_Breakpoints->MemLockExists(storeAddress, opInfo.NumBytesToStore()))
            {
                // Memory is locked, skip op
                g_Settings->SaveBool(Debugger_SkipOp, true);
                return;
            }
        }
    }

    if (m_ScriptSystem->HaveAppCallbacks())
    {
        JSHookCpuStepEnv hookEnv;
        hookEnv.pc = g_Reg->m_PROGRAM_COUNTER;
        hookEnv.opInfo = COpInfo(R4300iOp::m_Opcode);

        if(m_ScriptSystem->HaveCpuExecCallbacks(hookEnv.pc))
        {
            m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_CPU_EXEC, (void*)&hookEnv);
        }

        if (hookEnv.opInfo.IsLoadCommand() &&
            m_ScriptSystem->HaveCpuReadCallbacks(hookEnv.opInfo.GetLoadStoreAddress()))
        {
            m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_CPU_READ, (void*)&hookEnv);
        }
        else if (hookEnv.opInfo.IsStoreCommand() &&
            m_ScriptSystem->HaveCpuWriteCallbacks(hookEnv.opInfo.GetLoadStoreAddress()))
        {
            m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_CPU_WRITE, (void*)&hookEnv);
        }
    }

    if (CDebugSettings::ExceptionBreakpoints() != 0)
    {
        uint32_t pc = g_Reg->m_PROGRAM_COUNTER;

        if (pc == 0x80000000 || pc == 0x80000080 ||
            pc == 0xA0000100 || pc == 0x80000180)
        {
            if ((g_Reg->STATUS_REGISTER >> 1) & 3) // If EXL/ERL bits are set
            {
                HandleCPUException();
            }
        }
    }

    if (m_Breakpoints->HaveRegBP())
    {
        COpInfo opInfo(R4300iOp::m_Opcode);

        if (m_Breakpoints->HaveAnyGPRWriteBP())
        {
            int nReg = 0;
            opInfo.WritesGPR(&nReg);

            if (nReg != 0 && m_Breakpoints->HaveGPRWriteBP(nReg))
            {
                g_Settings->SaveBool(Debugger_SteppingOps, true);
            }
        }

        if (m_Breakpoints->HaveAnyGPRReadBP())
        {
            int nReg1 = 0, nReg2 = 0;
            opInfo.ReadsGPR(&nReg1, &nReg2);

            if ((nReg1 != 0 && m_Breakpoints->HaveGPRReadBP(nReg1)) ||
                (nReg2 != 0 && m_Breakpoints->HaveGPRReadBP(nReg2)))
            {
                g_Settings->SaveBool(Debugger_SteppingOps, true);
            }
        }

        if (m_Breakpoints->HaveHIWriteBP() && opInfo.WritesHI() ||
            m_Breakpoints->HaveLOWriteBP() && opInfo.WritesLO() ||
            m_Breakpoints->HaveHIReadBP() && opInfo.ReadsHI() ||
            m_Breakpoints->HaveLOReadBP() && opInfo.ReadsLO())
        {
            g_Settings->SaveBool(Debugger_SteppingOps, true);
        }
    }
}

// Called before opcode is executed (not called if SkipOp is set)
void CDebuggerUI::CPUStep()
{
    if (bCPULoggingEnabled())
    {
        m_CPULog->PushState();
    }
}

// Called after opcode has been executed
void CDebuggerUI::CPUStepEnded()
{
    if (m_StackTrace == nullptr)
    {
        return;
    }

    OPCODE Opcode = R4300iOp::m_Opcode;
    uint32_t op = Opcode.op;
    uint32_t funct = Opcode.funct;

    if (op == R4300i_JAL || ((op == R4300i_SPECIAL) && (funct == R4300i_SPECIAL_JALR) && (Opcode.rd == 31))) // JAL or JALR RA, x
    {
        m_StackTrace->PushEntry(R4300iOp::m_JumpToLocation, g_Reg->m_PROGRAM_COUNTER);
    }
    else if (funct == R4300i_SPECIAL_JR && Opcode.rs == 31) // JR RA
    {
        m_StackTrace->PopEntry();
    }
    else if (op == R4300i_CP0 && funct == R4300i_COP0_CO_ERET) // TODO: may need more work
    {
        m_StackTrace->ClearEntries();
    }
}

void CDebuggerUI::FrameDrawn()
{
}

void CDebuggerUI::PIFReadStarted(void)
{
    m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_PIFREAD);
}

void CDebuggerUI::RSPReceivedTask(void)
{
    JSHookSpTaskEnv env;

    DebugLoad_VAddr(0xA4000FC0, env.taskType);
    DebugLoad_VAddr(0xA4000FC4, env.taskFlags);
    DebugLoad_VAddr(0xA4000FC8, env.ucodeBootAddress);
    DebugLoad_VAddr(0xA4000FCC, env.ucodeBootSize);
    DebugLoad_VAddr(0xA4000FD0, env.ucodeAddress);
    DebugLoad_VAddr(0xA4000FD4, env.ucodeSize);
    DebugLoad_VAddr(0xA4000FD8, env.ucodeDataAddress);
    DebugLoad_VAddr(0xA4000FDC, env.ucodeDataSize);
    DebugLoad_VAddr(0xA4000FE0, env.dramStackAddress);
    DebugLoad_VAddr(0xA4000FE4, env.dramStackSize);
    DebugLoad_VAddr(0xA4000FE8, env.outputBuffAddress);
    DebugLoad_VAddr(0xA4000FEC, env.outputBuffSize);
    DebugLoad_VAddr(0xA4000FF0, env.dataAddress);
    DebugLoad_VAddr(0xA4000FF4, env.dataSize);
    DebugLoad_VAddr(0xA4000FF8, env.yieldDataAddress);
    DebugLoad_VAddr(0xA4000FFC, env.yieldDataSize);

    m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_RSPTASK, &env);
}

void CDebuggerUI::PIDMAReadStarted(void)
{
    JSHookPiDmaEnv env;

    env.direction = 1;
    DebugLoad_VAddr(0xA4600000, env.dramAddress);
    DebugLoad_VAddr(0xA4600004, env.cartAddress);
    DebugLoad_VAddr(0xA4600008, env.length);

    m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_PIDMA, &env);
}

void CDebuggerUI::PIDMAWriteStarted(void)
{
    JSHookPiDmaEnv env;

    env.direction = 0;
    DebugLoad_VAddr(0xA4600000, env.dramAddress);
    DebugLoad_VAddr(0xA4600004, env.cartAddress);
    DebugLoad_VAddr(0xA460000C, env.length);

    m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_PIDMA, &env);

    HandleCartToRamDMA();
}

void CDebuggerUI::EmulationStarted(void)
{
    JSHookEmuStateChangeEnv env;
    env.state = JS_EMU_STARTED;
    m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_EMUSTATECHANGE, &env);
}

void CDebuggerUI::EmulationStopped(void)
{
    JSHookEmuStateChangeEnv env;
    env.state = JS_EMU_STOPPED;
    m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_EMUSTATECHANGE, &env);
}

void CDebuggerUI::WaitForStep(void)
{
    g_Settings->SaveBool(Debugger_WaitingForStep, true);
    m_StepEvent.IsTriggered(SyncEvent::INFINITE_TIMEOUT);
    g_Settings->SaveBool(Debugger_WaitingForStep, false);
}

void CDebuggerUI::StartAutorunScripts(void)
{
    if (m_ScriptSystem == nullptr)
    {
        return;
    }

    m_ScriptSystem->ExecAutorunList();
}

bool CDebuggerUI::ExecutionBP(uint32_t address)
{
    return m_Breakpoints != nullptr && m_Breakpoints->ExecutionBPExists(address, true) != CBreakpoints::BP_NOT_SET;
}

bool CDebuggerUI::ReadBP8(uint32_t address)
{
    return m_Breakpoints != nullptr && m_Breakpoints->ReadBPExists8(address) != CBreakpoints::BP_NOT_SET;
}

bool CDebuggerUI::ReadBP16(uint32_t address)
{
    return m_Breakpoints != nullptr && m_Breakpoints->ReadBPExists16(address) != CBreakpoints::BP_NOT_SET;
}

bool CDebuggerUI::ReadBP32(uint32_t address)
{
    return m_Breakpoints != nullptr && m_Breakpoints->ReadBPExists32(address) != CBreakpoints::BP_NOT_SET;
}

bool CDebuggerUI::ReadBP64(uint32_t address)
{
    return m_Breakpoints != nullptr && m_Breakpoints->ReadBPExists64(address) != CBreakpoints::BP_NOT_SET;
}

bool CDebuggerUI::WriteBP8(uint32_t address)
{
    return m_Breakpoints != nullptr && m_Breakpoints->WriteBPExists8(address) != CBreakpoints::BP_NOT_SET;
}

bool CDebuggerUI::WriteBP16(uint32_t address)
{
    return m_Breakpoints != nullptr && m_Breakpoints->WriteBPExists16(address) != CBreakpoints::BP_NOT_SET;
}

bool CDebuggerUI::WriteBP32(uint32_t address)
{
    return m_Breakpoints != nullptr && m_Breakpoints->WriteBPExists32(address) != CBreakpoints::BP_NOT_SET;
}

bool CDebuggerUI::WriteBP64(uint32_t address)
{
    return m_Breakpoints != nullptr && m_Breakpoints->WriteBPExists64(address) != CBreakpoints::BP_NOT_SET;
}


QList<QAbstractNodeFactory *> QAbstractNodeFactory::nodeFactories()
{
    return node_factories;
}

/****************************************************************************
**
**
** This file is part of the Qt3D module of the Qt Toolkit.
**
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: http://example.com and
** http://example.com.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qabstractnodefactory_p.h"

QT_BEGIN_NAMESPACE

namespace Qt3DCore {

/*
 * \internal
 * \class Qt3DCore::QAbstractNodeFactory
 * \inheaderfile Qt3DCore/QAbstractNodeFactory
 * \inmodule Qt3DCore
 *
 * \TODO
 */

/*
 * \fn Qt3DCore::QNode * Qt3DCore::QAbstractNodeFactory::createNode(const char *type)
 * Creates a node of \a type.
 */
QAbstractNodeFactory::~QAbstractNodeFactory()
{
}

QList<QAbstractNodeFactory *> node_factories;

/*
 * Registers the given \a factory
 */
void QAbstractNodeFactory::registerNodeFactory(QAbstractNodeFactory *factory)
{
    node_factories << factory;
}

/*
 * \return the node factories.
 */

} // namespace Qt3DCore

QT_END_NAMESPACE


unsigned long cPlatform::GetApplicationTime()
	{
		return SDL_GetTicks();
	}

/*
 *
 * This file is part of Amnesia: A Machine For Pigs.
 *
 */

#include "system/Platform.h"

#include "system/String.h"

#include "system/LowLevelSystem.h"

#define UNICODE
#include <windows.h>
#include <shlobj.h>

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <time.h>
#include <sys/stat.h>
#include <fstream>
#include <string>
#include <io.h>
#include <direct.h>

#include "SDL/SDL.h"

#include "impl/TimerSDL.h"
#include "impl/ThreadWin32.h"
#include "impl/MutexWin32.h"

#include <algorithm>

namespace hpl {

	// FILE HANDLING

	unsigned long cPlatform::GetFileSize(const tWString& asFileName)
	{
		/*__stat64 fileStat;
		int lErr = _wstat64( asFileName.c_str(), &fileStat );
		if (lErr != 0) return 0;
		return (unsigned long)fileStat.st_size; */

		FILE *pFile = _wfopen(asFileName.c_str(),_W("rb"));
		if(pFile==NULL)	return 0;

		fseek(pFile,0,SEEK_END);
		long lFileSize = ftell(pFile);
		rewind(pFile);

		fclose(pFile);
		return (unsigned long)lFileSize;
	}

	bool cPlatform::CopyFileToBuffer(const tWString& asFileName, void *apBuffer, unsigned long alSize)
	{
		FILE *pFile = _wfopen(asFileName.c_str(),_W("rb"));
		if(pFile==NULL)	return false;

		fread(apBuffer, sizeof(char), alSize, pFile);

		fclose(pFile);
		return true;
	}

	bool cPlatform::FileExists(const tWString& asFileName)
	{
		FILE *f = _wfopen(asFileName.c_str(),_W("rb"));
		if(f==NULL)
		{
			return false;
		}

		fclose(f);
		return true;
	}

	void cPlatform::RemoveFile(const tWString& asFilePath)
	{
		_wremove(asFilePath.c_str());
	}

	bool cPlatform::CloneFile(	const tWString& asSrcFileName,const tWString& asDestFileName,
		bool abFailIfExists)
	{
		return CopyFile(asSrcFileName.c_str(),asDestFileName.c_str(),abFailIfExists)==TRUE;
	}

	bool cPlatform::CreateFolder(const tWString& asPath)
	{
		tWString sPath = cString::ReplaceCharToW(asPath,_W("/"), _W("\\"));

		return CreateDirectory(sPath.c_str(),NULL)==TRUE;
	}

	bool cPlatform::RemoveFolder(const tWString& asPath, bool abDeleteAllFiles, bool abDeleteAllSubFolders)
	{
		tWString sPath = cString::ReplaceCharToW(asPath,_W("/"), _W("\\"));

		// Remove any files in the directory
		if(abDeleteAllFiles)
		{
			tWStringList lstFiles;
            FindFilesInDir(lstFiles,sPath,_W("*.*"), true);
			for(tWStringListIt it = lstFiles.begin(); it != lstFiles.end(); ++it)
			{
				tWString sFilePath = cString::SetFilePathW(*it, sPath);
				RemoveFile(sFilePath);
			}
		}

		// Remove any sub folders in the directory
		if(abDeleteAllSubFolders)
		{
			tWStringList lstFolders;
			FindFoldersInDir(lstFolders, sPath,true, false);
			for(tWStringListIt it = lstFolders.begin(); it != lstFolders.end(); ++it)
			{
				tWString sFolderPath = cString::SetFilePathW(*it, sPath);
				RemoveFolder(sFolderPath, abDeleteAllFiles,abDeleteAllSubFolders);
			}
		}

		if(RemoveDirectory(sPath.c_str())!=TRUE)
		{
			wchar_t sTempString[2048];
			FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,0,GetLastError(),0,sTempString,2048,NULL);
			Error("Could not remove folder: '%s': %s",cString::To8Char(sPath).c_str(), cString::To8Char(sTempString).c_str());
			return false;
		}
		return true;

	}

	bool cPlatform::FolderExists(const tWString& asPath)
	{
		tWString sDir = cString::ReplaceCharToW(asPath, _W("/"), _W("\\"));
		DWORD lFileAttributes = GetFileAttributes(sDir.c_str());

		return (lFileAttributes != INVALID_FILE_ATTRIBUTES && (lFileAttributes & FILE_ATTRIBUTE_DIRECTORY) !=0);
	}

	tWString cPlatform::GetFullFilePath(const tWString& asFilePath)
	{
		wchar_t sOutFilePath[2048];
		int x = GetFullPathNameW(asFilePath.c_str(),2048,sOutFilePath,NULL);

		return sOutFilePath;
	}

	FILE *cPlatform::OpenFile(const tWString& asFileName, const tWString asMode)
	{
		return _wfopen(asFileName.c_str(), asMode.c_str());
	}

	static cDate DateFromGMTime(struct tm* apClock)
	{
		cDate date;

		date.seconds = apClock->tm_sec;
		date.minutes = apClock->tm_min;
		date.hours = apClock->tm_hour;
		date.month_day = apClock->tm_mday;
		date.month = apClock->tm_mon;
		date.year = 1900 + apClock->tm_year;
		date.week_day = apClock->tm_wday;
		date.year_day = apClock->tm_yday;

		return date;
	}

	cDate cPlatform::FileModifiedDate(const tWString& asFilePath)
	{
		struct tm* pClock;

		struct _stat attrib;
		_wstat(asFilePath.c_str(), &attrib);

		pClock = gmtime(&(attrib.st_mtime));	// Get the last modified time and put it into the time structure

		cDate date = DateFromGMTime(pClock);

		return date;
	}

	cDate cPlatform::FileCreationDate(const tWString& asFilePath)
	{
		struct tm* pClock;
		struct _stat attrib;
		_wstat(asFilePath.c_str(), &attrib);

		pClock = gmtime(&(attrib.st_ctime));	// Get the last modified time and put it into the time structure

		cDate date = DateFromGMTime(pClock);

		return date;
	}

	static tm GMTimeFromDate(cDate aDate)
	{
		tm time;

		time.tm_sec = aDate.seconds;
		time.tm_min = aDate.minutes;
		time.tm_hour = aDate.hours;
		time.tm_mday = aDate.month_day;
		time.tm_mon = aDate.month;
		time.tm_year = aDate.year - 1900;
		time.tm_wday = aDate.week_day;
		time.tm_yday = aDate.year_day;
		time.tm_isdst = -1;

		return time;
	}

	void cPlatform::SetFileModifiedDate(const tWString& asFilePath, cDate aDate)
	{
		tm time = GMTimeFromDate(aDate);

		// To global time
		__time64_t gmttolocal = 1000000;
		struct tm* pGmtClock = gmtime(&gmttolocal);
		struct tm* pLocalClock = localtime(&gmttolocal);

		if(pGmtClock && pLocalClock)
		{
			// Convert gmt to local time
			time.tm_sec    += pLocalClock->tm_sec  - pGmtClock->tm_sec;
			time.tm_min	   += pLocalClock->tm_min  - pGmtClock->tm_min;
			time.tm_hour   += pLocalClock->tm_hour - pGmtClock->tm_hour;
			time.tm_mday   += pLocalClock->tm_mday - pGmtClock->tm_mday;
			time.tm_mon	   += pLocalClock->tm_mon  - pGmtClock->tm_mon;
			time.tm_year   += pLocalClock->tm_year - pGmtClock->tm_year;
			time.tm_wday   += pLocalClock->tm_wday - pGmtClock->tm_wday;
			time.tm_yday   += pLocalClock->tm_yday - pGmtClock->tm_yday;

			__time64_t utctime = mktime(&time);

			// Convert to windows file time
			FILETIME filetime;
			LONGLONG ll = Int32x32To64(utctime, 10000000) + 116444736000000000;
			filetime.dwLowDateTime = (DWORD) ll;
			filetime.dwHighDateTime = ll >>32;

			HANDLE lFile = CreateFile(asFilePath.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_DIRECTORY | FILE_FLAG_BACKUP_SEMANTICS, NULL);
			SetFileTime(lFile, NULL, NULL, &filetime);
			CloseHandle(lFile);
		}
	}

	void cPlatform::SetFileModifiedDate(const tWString& asFilePath, unsigned long long aUTime)
	{

		FILETIME filetime;
		LONGLONG ll = Int32x32To64(aUTime, 10000000) + 116444736000000000;
		filetime.dwLowDateTime = (DWORD) ll;
		filetime.dwHighDateTime = ll >>32;

		HANDLE lFile = CreateFile(asFilePath.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_DIRECTORY | FILE_FLAG_BACKUP_SEMANTICS, NULL);
		SetFileTime(lFile, NULL, NULL, &filetime);
		CloseHandle(lFile);
	}

	void cPlatform::FindFilesInDir(tWStringList &alstStrings,const tWString& asDir, const tWString& asMask, bool abAddHidden)
	{
		//these windows functions only works with "\".. sucks ...
		tWString sDir = cString::ReplaceCharToW(asDir,_W("/"),_W("\\"));

		int lLastMaskAsteriskPos = cString::GetLastStringPosW(asMask, _W("*"));
		tWString sMaskExt = cString::ToLowerCaseW(cString::SubW(asMask,lLastMaskAsteriskPos+2));
		bool bMaskNotNull = (sMaskExt!=_W(""));

		//Get the search string
		wchar_t sSpec[256];
		wchar_t end = sDir[sDir.size()-1];

		if(end == _W('\\') || end== _W('/'))
			swprintf(sSpec,256,_W("%s%s"),sDir.c_str(),asMask.c_str());
		else
		{
			//these windows functions only works with "\".. sucks ...
			swprintf(sSpec,256,_W("%s\\%s"),sDir.c_str(),asMask.c_str());
		}

		//The needed structs
		intptr_t lHandle;
		struct _wfinddata_t FileInfo;

		//Find the first file:
		lHandle = _wfindfirst(sSpec, &FileInfo );
		if(lHandle==-1L)return;

		//Check so it is not a subdir
		if((FileInfo.attrib & _A_SUBDIR)==0 && (abAddHidden || (FileInfo.attrib & _A_HIDDEN)==0) )
		{
			if(bMaskNotNull)
			{
				if(cString::ToLowerCaseW(cString::GetFileExtW(FileInfo.name))==sMaskExt) alstStrings.push_back(FileInfo.name);
			}
			else
				alstStrings.push_back(FileInfo.name);
		}

		//Get the other files.
		while( _wfindnext( lHandle, &FileInfo ) == 0 )
		{
			if((FileInfo.attrib & _A_SUBDIR)==0 && (abAddHidden || (FileInfo.attrib & _A_HIDDEN)==0) )
			{
				if(bMaskNotNull)
				{
					if(cString::ToLowerCaseW(cString::GetFileExtW(FileInfo.name))==sMaskExt) alstStrings.push_back(FileInfo.name);
				}
				else
					alstStrings.push_back(FileInfo.name);
			}
		}

		//Close the handle to release reosuces and unlock files
		_findclose(lHandle);
	}

	static bool IsGoodFolder(struct _wfinddata_t* pFileInfo, bool abAddHidden, bool abAddUpFolder)
	{
		if( (pFileInfo->attrib & _A_SUBDIR) ==0) return false;
		if( abAddHidden==false && (pFileInfo->attrib & _A_HIDDEN)) return false;

		tWString sName = pFileInfo->name;
		if( sName == _W("..") ) return abAddUpFolder;
		if( sName == _W(".") ) return false;

		return true;
	}

	void cPlatform::FindFoldersInDir(tWStringList &alstStrings,const tWString& asDir, bool abAddHidden, bool abAddUpFolder)
	{
		//these windows functions only works with "\".. sucks ...
		tWString sDir = cString::ReplaceCharToW(asDir,_W("/"),_W("\\"));

		//Get the search string
		wchar_t sSpec[256];
		wchar_t end = sDir[sDir.size()-1];

		if(end == _W('\\') || end== _W('/'))
			swprintf(sSpec,256,_W("%s%s"),sDir.c_str(),_W("*.*"));
		else
		{
			//these windows functions only works with "\".. sucks ...
			swprintf(sSpec,256,_W("%s\\%s"),sDir.c_str(),_W("*.*"));
		}

		//The needed structs
		intptr_t lHandle;
		struct _wfinddata_t FileInfo;

		//Find the first file:
		lHandle = _wfindfirst(sSpec, &FileInfo );
		if(lHandle==-1L)return;

		//Check so it IS a sub dir
		if( IsGoodFolder(&FileInfo, abAddHidden, abAddUpFolder))
		{
			alstStrings.push_back(FileInfo.name);
		}

		//Get the other files.
		while( _wfindnext( lHandle, &FileInfo ) == 0 )
		{
			if( IsGoodFolder(&FileInfo, abAddHidden, abAddUpFolder) )
			{
				alstStrings.push_back(FileInfo.name);
			}

		}

		//Close the handle to release reosuces and unlock files
		_findclose(lHandle);
	}

	tWString cPlatform::GetWorkingDir()
	{
		tWString sDir = tWString( _wgetcwd(NULL,0));

		return sDir;
	}

	// APPLICATION

	void cPlatform::Sleep ( const unsigned int alMillisecs )
	{
		SDL_Delay ( alMillisecs );
	}

	// DIALOG

	void cPlatform::CreateMessageBoxBase(eMsgBoxType eType, const wchar_t* asCaption, const wchar_t* fmt, va_list ap)
	{
		wchar_t text[2048];

		if (fmt == NULL)
			return;
		vswprintf(text, 2047, fmt, ap);

		tWString sMess = _W("");

		sMess += text;

		UINT lType = MB_OK;

		switch (eType)
		{
		case eMsgBoxType_Info:
			lType += MB_ICONINFORMATION;
			break;
		case eMsgBoxType_Error:
			lType += MB_ICONERROR;
			break;
		case eMsgBoxType_Warning:
			lType += MB_ICONWARNING;
			break;
		default:
			break;
		}

		MessageBox( NULL, sMess.c_str(), asCaption, lType );
	}

	// SYSTEM STATS

	ePlatform cPlatform::GetPlatform()
	{
		return ePlatform_Win32;
	}

	tString cPlatform::msName = "Win32";

	iTimer * cPlatform::CreateTimer()
	{
		return hplNew(cTimerSDL, () );
	}

	cDate cPlatform::GetDate()
	{
		time_t lTime;
		time(&lTime);

		struct tm* pClock;
		pClock = localtime(&lTime);

		return DateFromGMTime(pClock);
	}

	void cPlatform::CopyTextToClipboard(const tWString &asText)
	{
		OpenClipboard(NULL);
		EmptyClipboard();

		HGLOBAL clipbuffer;
		wchar_t * pBuffer;
		EmptyClipboard();
		clipbuffer = GlobalAlloc(GMEM_DDESHARE, (asText.size()+1) *sizeof(wchar_t));
		pBuffer = (wchar_t*)GlobalLock(clipbuffer);
		wcscpy(pBuffer, asText.c_str());

		SetClipboardData(CF_UNICODETEXT, clipbuffer);

		GlobalUnlock(clipbuffer);

		CloseClipboard();
	}

	tWString cPlatform::LoadTextFromClipboard()
	{
		tWString sText=_W("");
		OpenClipboard(NULL);

		HGLOBAL clipbuffer = GetClipboardData(CF_UNICODETEXT);

		wchar_t *pBuffer = (wchar_t*)GlobalLock(clipbuffer);

		if(pBuffer != NULL) sText = pBuffer;

		GlobalUnlock(clipbuffer);

		CloseClipboard();

		return sText;
	}

	tWString cPlatform::GetSystemSpecialPath(eSystemPath aPathType)
	{
		tWString sOutput = _W("");

		//Get the correction WIN32 varaible
		int type;
		switch(aPathType)
		{
		case eSystemPath_Personal:	type = CSIDL_PERSONAL;
			break;
		default: return _W("");
		}

		//Get string
		TCHAR sPath[2048];
		if(SUCCEEDED(SHGetFolderPath(NULL, type | CSIDL_FLAG_CREATE, NULL,0,sPath)))
		{
			sOutput = tWString(sPath);
		}
		else
		{
			return _W("");
		}

		//Make sure the last char is a separator
		if(	cString::GetLastCharW(sOutput) != _W("/") &&
			cString::GetLastCharW(sOutput) != _W("\\"))
		{
			sOutput += _W("/");
		}

		return sOutput;
	}

	unsigned long cPlatform::GetSystemAvailableDrives()
	{
		return _getdrives();
	}

	float cPlatform::GetMousePointerSpeed()
	{
		// Retrives mouse pointer speed
		BOOL bResult;
		int vMouseInfo[3];    // Array for mouse information

		// Get the current mouse speed.
		bResult = SystemParametersInfo(SPI_GETMOUSESPEED,   // Get mouse information
									   0,              // Not used
									   &vMouseInfo,    // Holds mouse information
									   0);             // Not used

		if(bResult)
		{
			return float(vMouseInfo[0]) / 10.0f;
		}
		else
		{
			return 1.0f;
		}
	}

	void cPlatform::GetAvailableVideoModes(tVideoModeVec &avDestVidModes, int alMinBpp, int alMinRefreshRate)
	{
		bool bSkipBppCheck = (alMinBpp==-1);
		bool bSkipRateCheck = (alMinRefreshRate==-1);

		avDestVidModes.clear();

		DEVMODE mode;
		int lModeCounter=0;
		while(EnumDisplaySettings(NULL, lModeCounter, &mode))
		{
			if((bSkipBppCheck || alMinBpp<=(int)mode.dmBitsPerPel) &&
				(bSkipRateCheck || alMinRefreshRate<=(int)mode.dmDisplayFrequency))
			{
				avDestVidModes.push_back(cVideoMode(cVector2l(mode.dmPelsWidth, mode.dmPelsHeight),
													mode.dmBitsPerPel,
													mode.dmDisplayFrequency));
			}

			++lModeCounter;
		}

		sort(avDestVidModes.begin(), avDestVidModes.end(), VideoComp());
	}

	tWString cPlatform::GetDisplayName(int alDisplay)
	{
		return _W("");
	}

	void cPlatform::GetDisplayResolution(int alDisplay, int& alHorizontal, int& alVertical)
	{
		if(alDisplay != 0)
		{
			// Only main monitor supported
			alHorizontal = -1;
			alVertical = -1;
		}
		else
		{
			// Get current screen resolution
			RECT desktop;
			HWND pDesktop = GetDesktopWindow();
			GetWindowRect(pDesktop, &desktop);

			alHorizontal = desktop.right;
			alVertical = desktop.bottom;
		}
	}

	// SYSTEM COMMANDS

	void cPlatform::OpenBrowserWindow ( const tWString& asURL )
	{
		ShellExecute ( NULL, _W("open"), asURL.c_str(), NULL, NULL, SW_SHOWNORMAL );
	}

	bool cPlatform::RunProgram( const tWString& asPath, const tWString& asParams )
	{
		PROCESS_INFORMATION pi;
		STARTUPINFO si;

		memset(&si,0,sizeof(si));
		si.cb= sizeof(si);
		si.wShowWindow = SW_SHOW;

		tWString sCommandLine = asPath + _W(" ") + asParams;

		return CreateProcess(asPath.c_str(), (LPWSTR)sCommandLine.c_str(), NULL, NULL, false, 0, NULL,NULL, &si,&pi)==TRUE;
	}

	bool cPlatform::OpenFileOnShell( const tWString& asPath )
	{
		int lStatusCode = (int)ShellExecute(NULL, _W("open"), asPath.c_str(), NULL, NULL, SW_SHOWMAXIMIZED);
		bool bRet = (lStatusCode > 32);

		tWString sMessage = _W("Cannot open file '") + asPath + _W("' - \n");
		switch(lStatusCode)
		{
		case SE_ERR_NOASSOC:
			sMessage += _W("No associated app found to open files of type: ") + cString::ToUpperCaseW(cString::GetFileExtW(asPath));
			break;
		case ERROR_FILE_NOT_FOUND:
		case ERROR_PATH_NOT_FOUND:
			sMessage += _W("File not found");
			break;
		};

		if(bRet==false)
			cPlatform::CreateMessageBox(eMsgBoxType_Error, _W("Error"), sMessage.c_str());

		return bRet;
	}

	// THREADING

	iThread* cPlatform::CreateThread(iThreadClass* apThreadClass)
	{
		iThread* pThread = hplNew(cThreadWin32, ());
		pThread->SetThreadClass(apThreadClass);

		return pThread;
	}

	iMutex* cPlatform::CreateMutEx()
	{
		return hplNew(cMutexWin32, ());
	}

}


try
    {
      build_style::BuildIfNecessaryAndApply(mapcssFilePath);
    }
    catch (std::exception const & e)
    {
      QMessageBox msgBox;
      msgBox.setWindowTitle("Error");
      msgBox.setText(e.what());
      msgBox.setStandardButtons(QMessageBox::Ok);
      msgBox.setDefaultButton(QMessageBox::Ok);
      msgBox.exec();
      return returnCode;
    }

#include "qt/info_dialog.hpp"
#include "qt/mainwindow.hpp"
#include "qt/screenshoter.hpp"

#include "map/framework.hpp"

#include "platform/platform.hpp"
#include "platform/settings.hpp"

#include "coding/file_reader.hpp"

#include "base/logging.hpp"
#include "base/macros.hpp"

#include "build_style/build_style.h"

#include <cstdio>
#include <cstdlib>
#include <sstream>

#include "3party/Alohalytics/src/alohalytics.h"
#include "3party/gflags/src/gflags/gflags.h"

#include <QtCore/QDir>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QApplication>
#include <QtWidgets/QFileDialog>

DEFINE_string(data_path, "", "Path to data directory.");
DEFINE_string(log_abort_level, base::ToString(base::GetDefaultLogAbortLevel()),
              "Log messages severity that causes termination.");
DEFINE_string(resources_path, "", "Path to resources directory.");
DEFINE_string(kml_path, "", "Activates screenshot mode. Path to a kml file or a directory "
              "with kml files to take screenshots.");
DEFINE_string(points, "", "Activates screenshot mode. Points on the map and zoom level "
              "[1..18] in format \"lat,lon,zoom[;lat,lon,zoom]\" or path to a file with points in "
              "the same format. Each point and zoom define a place on the map to take screenshot.");
DEFINE_string(rects, "", "Activates screenshot mode. Rects on the map in format"
              "\"lat_leftBottom,lon_leftBottom,lat_rightTop,lon_rightTop"
              "[;lat_leftBottom,lon_leftBottom,lat_rightTop,lon_rightTop]\" or path to a file with "
              "rects in the same format. Each rect defines a place on the map to take screenshot.");
DEFINE_string(dst_path, "", "Path to a directory to save screenshots.");
DEFINE_string(lang, "", "Device language.");
DEFINE_int32(width, 0, "Screenshot width.");
DEFINE_int32(height, 0, "Screenshot height.");
DEFINE_double(dpi_scale, 0.0, "Screenshot dpi scale (mdpi = 1.0, hdpi = 1.5, "
              "xhdpiScale = 2.0, 6plus = 2.4, xxhdpi = 3.0, xxxhdpi = 3.5).");

using namespace std;

namespace
{
bool ValidateLogAbortLevel(char const * flagname, string const & value)
{
  base::LogLevel level;
  if (!base::FromString(value, level))
  {
    ostringstream os;
    auto const & names = base::GetLogLevelNames();
    for (size_t i = 0; i < names.size(); ++i)
    {
      if (i != 0)
        os << ", ";
      os << names[i];
    }

    printf("Invalid value for --%s: %s, must be one of: %s\n", flagname, value.c_str(),
           os.str().c_str());
    return false;
  }
  return true;
}

bool const g_logAbortLevelDummy =
    google::RegisterFlagValidator(&FLAGS_log_abort_level, &ValidateLogAbortLevel);

class FinalizeBase
{
public:
  ~FinalizeBase()
  {
    // optional - clean allocated data in protobuf library
    // useful when using memory and resource leak utilites
  }
  };

#if defined(OMIM_OS_WINDOWS) //&& defined(PROFILER_COMMON)
  class InitializeFinalize : public FinalizeBase
  {
    FILE * m_errFile;
    base::ScopedLogLevelChanger const m_debugLog;
  public:
    InitializeFinalize() : m_debugLog(LDEBUG)
    {
      // App runs without error console under win32.
      m_errFile = ::freopen(".\\mapsme.log", "w", stderr);

    }
    ~InitializeFinalize()
    {
      ::fclose(m_errFile);
    }
  };
#else
  typedef FinalizeBase InitializeFinalize;
#endif
}  // namespace

int main(int argc, char * argv[])
{
  google::SetUsageMessage("Desktop application.");
  google::ParseCommandLineFlags(&argc, &argv, true);

  Platform & platform = GetPlatform();
  if (!FLAGS_resources_path.empty())
    platform.SetResourceDir(FLAGS_resources_path);
  if (!FLAGS_data_path.empty())
    platform.SetWritableDirForTests(FLAGS_data_path);

  base::LogLevel level;
  CHECK(base::FromString(FLAGS_log_abort_level, level), ());
  base::g_LogAbortLevel = level;

  Q_INIT_RESOURCE(resources_common);

  // Our double parsing code (base/string_utils.hpp) needs dots as a floating point delimiters, not commas.
  // TODO: Refactor our doubles parsing code to use locale-independent delimiters.
  // For example, http://example.com can be used.
  (void)::setenv("LC_NUMERIC", "C", 1);

  InitializeFinalize mainGuard;
  UNUSED_VALUE(mainGuard);

  QApplication a(argc, argv);

#ifdef DEBUG
  alohalytics::Stats::Instance().SetDebugMode(true);
#endif

  platform.SetupMeasurementSystem();

  // display EULA if needed
  char const * settingsEULA = "EulaAccepted";
  bool eulaAccepted = false;
  if (!settings::Get(settingsEULA, eulaAccepted) || !eulaAccepted)
  {
    QStringList buttons;
    buttons << "Accept" << "Decline";

    string buffer;
    {
      ReaderPtr<Reader> reader = platform.GetReader("eula.html");
      reader.ReadAsString(buffer);
    }
    qt::InfoDialog eulaDialog(qAppName() + QString(" End User Licensing Agreement"), buffer.c_str(), NULL, buttons);
    eulaAccepted = (eulaDialog.exec() == 1);
    settings::Set(settingsEULA, eulaAccepted);
  }

  int returnCode = -1;
  QString mapcssFilePath;
  if (eulaAccepted)   // User has accepted EULA
  {
    bool apiOpenGLES3 = false;
    std::unique_ptr<qt::ScreenshotParams> screenshotParams;

#if defined(OMIM_OS_MAC)
    apiOpenGLES3 = a.arguments().contains("es3", Qt::CaseInsensitive);
#endif

    if (!FLAGS_lang.empty())
      (void)::setenv("LANGUAGE", FLAGS_lang.c_str(), 1);

    if (!FLAGS_kml_path.empty() || !FLAGS_points.empty() || !FLAGS_rects.empty())
    {
      screenshotParams = std::make_unique<qt::ScreenshotParams>();
      if (!FLAGS_kml_path.empty())
      {
        screenshotParams->m_kmlPath = FLAGS_kml_path;
        screenshotParams->m_mode = qt::ScreenshotParams::Mode::KmlFiles;
      }
      else if (!FLAGS_points.empty())
      {
        screenshotParams->m_points = FLAGS_points;
        screenshotParams->m_mode = qt::ScreenshotParams::Mode::Points;
      }
      else if (!FLAGS_rects.empty())
      {
        screenshotParams->m_rects = FLAGS_rects;
        screenshotParams->m_mode = qt::ScreenshotParams::Mode::Rects;
      }
      if (!FLAGS_dst_path.empty())
        screenshotParams->m_dstPath = FLAGS_dst_path;
      if (FLAGS_width > 0)
        screenshotParams->m_width = FLAGS_width;
      if (FLAGS_height > 0)
        screenshotParams->m_height = FLAGS_height;
      if (FLAGS_dpi_scale >= df::VisualParams::kMdpiScale && FLAGS_dpi_scale <= df::VisualParams::kXxxhdpiScale)
        screenshotParams->m_dpiScale = FLAGS_dpi_scale;
    }

    qt::MainWindow::SetDefaultSurfaceFormat(apiOpenGLES3);

    FrameworkParams frameworkParams;

#ifdef BUILD_DESIGNER
    if (argc >= 2 && platform.IsFileExistsByFullPath(argv[1]))
        mapcssFilePath = argv[1];
    if (0 == mapcssFilePath.length())
    {
      mapcssFilePath = QFileDialog::getOpenFileName(nullptr, "Open style.mapcss file", "~/",
                                                    "MapCSS Files (*.mapcss)");
    }
    if (mapcssFilePath.isEmpty())
      return returnCode;

    // Designer tool can regenerate geometry index, so local ads can't work.
    frameworkParams.m_enableLocalAds = false;
#endif // BUILD_DESIGNER

    Framework framework(frameworkParams);
    qt::MainWindow w(framework, apiOpenGLES3, std::move(screenshotParams), mapcssFilePath);
    w.show();
    returnCode = a.exec();
  }

#ifdef BUILD_DESIGNER
  if (build_style::NeedRecalculate && !mapcssFilePath.isEmpty())
  {
    try
    {
      build_style::RunRecalculationGeometryScript(mapcssFilePath);
    }
    catch (std::exception & e)
    {
      QMessageBox msgBox;
      msgBox.setWindowTitle("Error");
      msgBox.setText(e.what());
      msgBox.setStandardButtons(QMessageBox::Ok);
      msgBox.setDefaultButton(QMessageBox::Ok);
      msgBox.exec();
    }
  }
#endif // BUILD_DESIGNER

  LOG_SHORT(LINFO, ("MapsWithMe finished with code", returnCode));
  return returnCode;
}


void AnimResolver::SubsampleAnimTrack(std::vector<aiVectorKey>& /*out*/,
    double /*time*/ ,double /*sample_delta*/ )
{

}

/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------

All rights reserved.

----------------------------------------------------------------------
*/

/** @file  LWOAnimation.cpp
 *  @brief LWOAnimationResolver utility class
 *
 *  It's a very generic implementation of LightWave's system of
 *  componentwise-animated stuff. The one and only fully free
 *  implementation of LightWave envelopes of which I know.
*/

#if (!defined ASSIMP_BUILD_NO_LWO_IMPORTER) && (!defined ASSIMP_BUILD_NO_LWS_IMPORTER)

#include <functional>

// internal headers
#include "LWOFileData.h"
#include <assimp/anim.h>

using namespace Assimp;
using namespace Assimp::LWO;

// Construct an animation resolver from a given list of envelopes
AnimResolver::AnimResolver(std::list<Envelope>& _envelopes,double tick)
    : envelopes   (_envelopes)
    , sample_rate (0.)
    , envl_x(), envl_y(), envl_z()
    , end_x(), end_y(), end_z()
    , flags()
    , sample_delta()
{
    trans_x = trans_y = trans_z = NULL;
    rotat_x = rotat_y = rotat_z = NULL;
    scale_x = scale_y = scale_z = NULL;

    first = last = 150392.;

    // find transformation envelopes
    for (std::list<LWO::Envelope>::iterator it = envelopes.begin(); it != envelopes.end(); ++it) {

        (*it).old_first = 0;
        (*it).old_last  = (*it).keys.size()-1;

        if ((*it).keys.empty()) continue;
        switch ((*it).type) {

            // translation
            case LWO::EnvelopeType_Position_X:
                trans_x = &*it;break;
            case LWO::EnvelopeType_Position_Y:
                trans_y = &*it;break;
            case LWO::EnvelopeType_Position_Z:
                trans_z = &*it;break;

                // rotation
            case LWO::EnvelopeType_Rotation_Heading:
                rotat_x = &*it;break;
            case LWO::EnvelopeType_Rotation_Pitch:
                rotat_y = &*it;break;
            case LWO::EnvelopeType_Rotation_Bank:
                rotat_z = &*it;break;

                // scaling
            case LWO::EnvelopeType_Scaling_X:
                scale_x = &*it;break;
            case LWO::EnvelopeType_Scaling_Y:
                scale_y = &*it;break;
            case LWO::EnvelopeType_Scaling_Z:
                scale_z = &*it;break;
            default:
                continue;
        };

        // convert from seconds to ticks
        for (std::vector<LWO::Key>::iterator d = (*it).keys.begin(); d != (*it).keys.end(); ++d)
            (*d).time *= tick;

        // set default animation range (minimum and maximum time value for which we have a keyframe)
        first = std::min(first, (*it).keys.front().time );
        last  = std::max(last,  (*it).keys.back().time );
    }

    // deferred setup of animation range to increase performance.
    // typically the application will want to specify its own.
    need_to_setup = true;
}

// Reset all envelopes to their original contents
void AnimResolver::ClearAnimRangeSetup()
{
    for (std::list<LWO::Envelope>::iterator it = envelopes.begin(); it != envelopes.end(); ++it) {

        (*it).keys.erase((*it).keys.begin(),(*it).keys.begin()+(*it).old_first);
        (*it).keys.erase((*it).keys.begin()+(*it).old_last+1,(*it).keys.end());
    }
}

// Insert additional keys to match LWO's pre& post behaviours.
void AnimResolver::UpdateAnimRangeSetup()
{
    // XXX doesn't work yet (hangs if more than one envelope channels needs to be interpolated)

    for (std::list<LWO::Envelope>::iterator it = envelopes.begin(); it != envelopes.end(); ++it) {
        if ((*it).keys.empty()) continue;

        const double my_first = (*it).keys.front().time;
        const double my_last  = (*it).keys.back().time;

        const double delta = my_last-my_first;
        const size_t old_size = (*it).keys.size();

        const float value_delta = (*it).keys.back().value - (*it).keys.front().value;

        // NOTE: We won't handle reset, linear and constant here.

        // process pre behaviour
        switch ((*it).pre) {
            case LWO::PrePostBehaviour_OffsetRepeat:
            case LWO::PrePostBehaviour_Repeat:
            case LWO::PrePostBehaviour_Oscillate:
                {
                const double start_time = delta - std::fmod(my_first-first,delta);
                std::vector<LWO::Key>::iterator n = std::find_if((*it).keys.begin(),(*it).keys.end(),
                    [start_time](double t) { return start_time > t; }),m;

                size_t ofs = 0;
                if (n != (*it).keys.end()) {
                    // copy from here - don't use iterators, insert() would invalidate them
                    ofs = (*it).keys.end()-n;
                    (*it).keys.insert((*it).keys.begin(),ofs,LWO::Key());

                    std::copy((*it).keys.end()-ofs,(*it).keys.end(),(*it).keys.begin());
                }

                // do full copies. again, no iterators
                const unsigned int num = (unsigned int)((my_first-first) / delta);
                (*it).keys.resize((*it).keys.size() + num*old_size);

                n = (*it).keys.begin()+ofs;
                bool reverse = false;
                for (unsigned int i = 0; i < num; ++i) {
                    m = n+old_size*(i+1);
                    std::copy(n,n+old_size,m);

                    if ((*it).pre == LWO::PrePostBehaviour_Oscillate && (reverse = !reverse))
                        std::reverse(m,m+old_size-1);
                }

                // update time values
                n = (*it).keys.end() - (old_size+1);
                double cur_minus = delta;
                unsigned int tt = 1;
                for (const double tmp =  delta*(num+1);cur_minus <= tmp;cur_minus += delta,++tt) {
                    m = (delta == tmp ? (*it).keys.begin() :  n - (old_size+1));
                    for (;m != n; --n) {
                        (*n).time -= cur_minus;

                        // offset repeat? add delta offset to key value
                        if ((*it).pre == LWO::PrePostBehaviour_OffsetRepeat) {
                            (*n).value += tt * value_delta;
                        }
                    }
                }
                break;
                }
            default:
                // silence compiler warning
                break;
        }

        // process post behaviour
        switch ((*it).post) {

            case LWO::PrePostBehaviour_OffsetRepeat:
            case LWO::PrePostBehaviour_Repeat:
            case LWO::PrePostBehaviour_Oscillate:

                break;

            default:
                // silence compiler warning
                break;
        }
    }
}

// Extract bind pose matrix
void AnimResolver::ExtractBindPose(aiMatrix4x4& out)
{
    // If we have no envelopes, return identity
    if (envelopes.empty()) {
        out = aiMatrix4x4();
        return;
    }
    aiVector3D angles, scaling(1.f,1.f,1.f), translation;

    if (trans_x) translation.x = trans_x->keys[0].value;
    if (trans_y) translation.y = trans_y->keys[0].value;
    if (trans_z) translation.z = trans_z->keys[0].value;

    if (rotat_x) angles.x = rotat_x->keys[0].value;
    if (rotat_y) angles.y = rotat_y->keys[0].value;
    if (rotat_z) angles.z = rotat_z->keys[0].value;

    if (scale_x) scaling.x = scale_x->keys[0].value;
    if (scale_y) scaling.y = scale_y->keys[0].value;
    if (scale_z) scaling.z = scale_z->keys[0].value;

    // build the final matrix
    aiMatrix4x4 s,rx,ry,rz,t;
    aiMatrix4x4::RotationZ(angles.z, rz);
    aiMatrix4x4::RotationX(angles.y, rx);
    aiMatrix4x4::RotationY(angles.x, ry);
    aiMatrix4x4::Translation(translation,t);
    aiMatrix4x4::Scaling(scaling,s);
    out = t*ry*rx*rz*s;
}

// Do a single interpolation on a channel
void AnimResolver::DoInterpolation(std::vector<LWO::Key>::const_iterator cur,
    LWO::Envelope* envl,double time, float& fill)
{
    if (envl->keys.size() == 1) {
        fill = envl->keys[0].value;
        return;
    }

    // check whether we're at the beginning of the animation track
    if (cur == envl->keys.begin()) {

        // ok ... this depends on pre behaviour now
        // we don't need to handle repeat&offset repeat&oszillate here, see UpdateAnimRangeSetup()
        switch (envl->pre)
        {
        case LWO::PrePostBehaviour_Linear:
            DoInterpolation2(cur,cur+1,time,fill);
            return;

        case LWO::PrePostBehaviour_Reset:
            fill = 0.f;
            return;

        default : //case LWO::PrePostBehaviour_Constant:
            fill = (*cur).value;
            return;
        }
    }
    // check whether we're at the end of the animation track
    else if (cur == envl->keys.end()-1 && time > envl->keys.rbegin()->time) {
        // ok ... this depends on post behaviour now
        switch (envl->post)
        {
        case LWO::PrePostBehaviour_Linear:
            DoInterpolation2(cur,cur-1,time,fill);
            return;

        case LWO::PrePostBehaviour_Reset:
            fill = 0.f;
            return;

        default : //case LWO::PrePostBehaviour_Constant:
            fill = (*cur).value;
            return;
        }
    }

    // Otherwise do a simple interpolation
    DoInterpolation2(cur-1,cur,time,fill);
}

// Almost the same, except we won't handle pre/post conditions here
void AnimResolver::DoInterpolation2(std::vector<LWO::Key>::const_iterator beg,
    std::vector<LWO::Key>::const_iterator end,double time, float& fill)
{
    switch ((*end).inter) {

        case LWO::IT_STEP:
            // no interpolation at all - take the value of the last key
            fill = (*beg).value;
            return;
        default:

            // silence compiler warning
            break;
    }
    double duration = (*end).time - (*beg).time;
    if (duration > 0.0) {
        fill = (*beg).value + ((*end).value - (*beg).value)*(float)(((time - (*beg).time) / duration));
    } else {
        fill = (*beg).value;
    }
}

// Subsample animation track by given key values

// Track interpolation
void AnimResolver::InterpolateTrack(std::vector<aiVectorKey>& out,aiVectorKey& fill,double time)
{
    // subsample animation track?
    if (flags & AI_LWO_ANIM_FLAG_SAMPLE_ANIMS) {
        SubsampleAnimTrack(out,time, sample_delta);
    }

    fill.mTime = time;

    // get x
    if ((*cur_x).time == time) {
        fill.mValue.x = (*cur_x).value;

        if (cur_x != envl_x->keys.end()-1) /* increment x */
            ++cur_x;
        else end_x = true;
    }
    else DoInterpolation(cur_x,envl_x,time,(float&)fill.mValue.x);

    // get y
    if ((*cur_y).time == time) {
        fill.mValue.y = (*cur_y).value;

        if (cur_y != envl_y->keys.end()-1) /* increment y */
            ++cur_y;
        else end_y = true;
    }
    else DoInterpolation(cur_y,envl_y,time,(float&)fill.mValue.y);

    // get z
    if ((*cur_z).time == time) {
        fill.mValue.z = (*cur_z).value;

        if (cur_z != envl_z->keys.end()-1) /* increment z */
            ++cur_z;
        else end_x = true;
    }
    else DoInterpolation(cur_z,envl_z,time,(float&)fill.mValue.z);
}

// Build linearly subsampled keys from three single envelopes, one for each component (x,y,z)
void AnimResolver::GetKeys(std::vector<aiVectorKey>& out,
    LWO::Envelope* _envl_x,
    LWO::Envelope* _envl_y,
    LWO::Envelope* _envl_z,
    unsigned int _flags)
{
    envl_x = _envl_x;
    envl_y = _envl_y;
    envl_z = _envl_z;
    flags  = _flags;

    // generate default channels if none are given
    LWO::Envelope def_x, def_y, def_z;
    LWO::Key key_dummy;
    key_dummy.time = 0.f;
    if ((envl_x && envl_x->type == LWO::EnvelopeType_Scaling_X) ||
        (envl_y && envl_y->type == LWO::EnvelopeType_Scaling_Y) ||
        (envl_z && envl_z->type == LWO::EnvelopeType_Scaling_Z)) {
        key_dummy.value = 1.f;
    }
    else key_dummy.value = 0.f;

    if (!envl_x) {
        envl_x = &def_x;
        envl_x->keys.push_back(key_dummy);
    }
    if (!envl_y) {
        envl_y = &def_y;
        envl_y->keys.push_back(key_dummy);
    }
    if (!envl_z) {
        envl_z = &def_z;
        envl_z->keys.push_back(key_dummy);
    }

    // guess how many keys we'll get
    size_t reserve;
    double sr = 1.;
    if (flags & AI_LWO_ANIM_FLAG_SAMPLE_ANIMS) {
        if (!sample_rate)
            sr = 100.f;
        else sr = sample_rate;
        sample_delta = 1.f / sr;

        reserve = (size_t)(
            std::max( envl_x->keys.rbegin()->time,
            std::max( envl_y->keys.rbegin()->time, envl_z->keys.rbegin()->time )) * sr);
    }
    else reserve = std::max(envl_x->keys.size(),std::max(envl_x->keys.size(),envl_z->keys.size()));
    out.reserve(reserve+(reserve>>1));

    // Iterate through all three arrays at once - it's tricky, but
    // rather interesting to implement.
    cur_x = envl_x->keys.begin();
    cur_y = envl_y->keys.begin();
    cur_z = envl_z->keys.begin();

    end_x = end_y = end_z = false;
    while (1) {

        aiVectorKey fill;

        if ((*cur_x).time == (*cur_y).time && (*cur_x).time == (*cur_z).time ) {

            // we have a keyframe for all of them defined .. this means
            // we don't need to interpolate here.
            fill.mTime = (*cur_x).time;

            fill.mValue.x = (*cur_x).value;
            fill.mValue.y = (*cur_y).value;
            fill.mValue.z = (*cur_z).value;

            // subsample animation track
            if (flags & AI_LWO_ANIM_FLAG_SAMPLE_ANIMS) {
            }
        }

        // Find key with lowest time value
        else if ((*cur_x).time <= (*cur_y).time && !end_x) {

            if ((*cur_z).time <= (*cur_x).time && !end_z) {
                InterpolateTrack(out,fill,(*cur_z).time);
            }
            else {
                InterpolateTrack(out,fill,(*cur_x).time);
            }
        }
        else if ((*cur_z).time <= (*cur_y).time && !end_y)  {
            InterpolateTrack(out,fill,(*cur_y).time);
        }
        else if (!end_y) {
            // welcome on the server, y
            InterpolateTrack(out,fill,(*cur_y).time);
        }
        else {
            // we have reached the end of at least 2 channels,
            // only one is remaining. Extrapolate the 2.
            if (end_y) {
                InterpolateTrack(out,fill,(end_x ? (*cur_z) : (*cur_x)).time);
            }
            else if (end_x) {
                InterpolateTrack(out,fill,(end_z ? (*cur_y) : (*cur_z)).time);
            }
            else { // if (end_z)
                InterpolateTrack(out,fill,(end_y ? (*cur_x) : (*cur_y)).time);
            }
        }
        double lasttime = fill.mTime;
        out.push_back(fill);

        if (lasttime >= (*cur_x).time) {
            if (cur_x != envl_x->keys.end()-1)
                ++cur_x;
            else end_x = true;
        }
        if (lasttime >= (*cur_y).time) {
            if (cur_y != envl_y->keys.end()-1)
                ++cur_y;
            else end_y = true;
        }
        if (lasttime >= (*cur_z).time) {
            if (cur_z != envl_z->keys.end()-1)
                ++cur_z;
            else end_z = true;
        }

        if( end_x && end_y && end_z ) /* finished? */
            break;
    }

    if (flags & AI_LWO_ANIM_FLAG_START_AT_ZERO) {
        for (std::vector<aiVectorKey>::iterator it = out.begin(); it != out.end(); ++it)
            (*it).mTime -= first;
    }
}

// Extract animation channel
void AnimResolver::ExtractAnimChannel(aiNodeAnim** out, unsigned int flags /*= 0*/)
{
    *out = NULL;

    //FIXME: crashes if more than one component is animated at different timings, to be resolved.

    // If we have no envelopes, return NULL
    if (envelopes.empty()) {
        return;
    }

    // We won't spawn an animation channel if we don't have at least one envelope with more than one keyframe defined.
    const bool trans = ((trans_x && trans_x->keys.size() > 1) || (trans_y && trans_y->keys.size() > 1) || (trans_z && trans_z->keys.size() > 1));
    const bool rotat = ((rotat_x && rotat_x->keys.size() > 1) || (rotat_y && rotat_y->keys.size() > 1) || (rotat_z && rotat_z->keys.size() > 1));
    const bool scale = ((scale_x && scale_x->keys.size() > 1) || (scale_y && scale_y->keys.size() > 1) || (scale_z && scale_z->keys.size() > 1));
    if (!trans && !rotat && !scale)
        return;

    // Allocate the output animation
    aiNodeAnim* anim = *out = new aiNodeAnim();

    // Setup default animation setup if necessary
    if (need_to_setup) {
        UpdateAnimRangeSetup();
        need_to_setup = false;
    }

    // copy translation keys
    if (trans) {
        std::vector<aiVectorKey> keys;
        GetKeys(keys,trans_x,trans_y,trans_z,flags);

        anim->mPositionKeys = new aiVectorKey[ anim->mNumPositionKeys = static_cast<unsigned int>(keys.size()) ];
        std::copy(keys.begin(),keys.end(),anim->mPositionKeys);
    }

    // copy rotation keys
    if (rotat) {
        std::vector<aiVectorKey> keys;
        GetKeys(keys,rotat_x,rotat_y,rotat_z,flags);

        anim->mRotationKeys = new aiQuatKey[ anim->mNumRotationKeys = static_cast<unsigned int>(keys.size()) ];

        // convert heading, pitch, bank to quaternion
        // Lightwave's rotation order is ZXY
        aiVector3D X(1.0,0.0,0.0);
        aiVector3D Y(0.0,1.0,0.0);
        aiVector3D Z(0.0,0.0,1.0);
        for (unsigned int i = 0; i < anim->mNumRotationKeys; ++i) {
            aiQuatKey& qk = anim->mRotationKeys[i];
            qk.mTime  = keys[i].mTime;
            qk.mValue = aiQuaternion(Y,keys[i].mValue.x)*aiQuaternion(X,keys[i].mValue.y)*aiQuaternion(Z,keys[i].mValue.z);
        }
    }

    // copy scaling keys
    if (scale) {
        std::vector<aiVectorKey> keys;
        GetKeys(keys,scale_x,scale_y,scale_z,flags);

        anim->mScalingKeys = new aiVectorKey[ anim->mNumScalingKeys = static_cast<unsigned int>(keys.size()) ];
        std::copy(keys.begin(),keys.end(),anim->mScalingKeys);
    }
}

#endif // no lwo or no lws


static bool CharacteristicsMatch(const Procedure &lhs, const Procedure &rhs) {
  using Attr = Procedure::Attr;
  auto lhsAttrs{rhs.attrs};
  lhsAttrs.set(
      Attr::Pure, lhs.attrs.test(Attr::Pure) | rhs.attrs.test(Attr::Pure));
  lhsAttrs.set(Attr::Elemental,
      lhs.attrs.test(Attr::Elemental) | rhs.attrs.test(Attr::Elemental));
  return lhsAttrs == rhs.attrs && lhs.functionResult == rhs.functionResult &&
      lhs.dummyArguments == rhs.dummyArguments;
}

#include "pointer-assignment.h"
#include "flang/Common/idioms.h"
#include "flang/Common/restorer.h"
#include "flang/Evaluate/characteristics.h"
#include "flang/Evaluate/expression.h"
#include "flang/Evaluate/fold.h"
#include "flang/Evaluate/tools.h"
#include "flang/Parser/message.h"
#include "flang/Parser/parse-tree-visitor.h"
#include "flang/Parser/parse-tree.h"
#include "flang/Semantics/expression.h"
#include "flang/Semantics/symbol.h"
#include "flang/Semantics/tools.h"
#include "llvm/Support/raw_ostream.h"
#include <optional>
#include <set>
#include <string>
#include <type_traits>

// Semantic checks for pointer assignment.

namespace Fortran::semantics {

using namespace parser::literals;
using evaluate::characteristics::DummyDataObject;
using evaluate::characteristics::FunctionResult;
using evaluate::characteristics::Procedure;
using evaluate::characteristics::TypeAndShape;
using parser::MessageFixedText;
using parser::MessageFormattedText;

class PointerAssignmentChecker {
public:
  PointerAssignmentChecker(evaluate::FoldingContext &context,
      parser::CharBlock source, const std::string &description)
      : context_{context}, source_{source}, description_{description} {}
  PointerAssignmentChecker(evaluate::FoldingContext &context, const Symbol &lhs)
      : context_{context}, source_{lhs.name()},
        description_{"pointer '"s + lhs.name().ToString() + '\''}, lhs_{&lhs},
        procedure_{Procedure::Characterize(lhs, context.intrinsics())} {
    set_lhsType(TypeAndShape::Characterize(lhs, context));
    set_isContiguous(lhs.attrs().test(Attr::CONTIGUOUS));
    set_isVolatile(lhs.attrs().test(Attr::VOLATILE));
  }
  PointerAssignmentChecker &set_lhsType(std::optional<TypeAndShape> &&);
  PointerAssignmentChecker &set_isContiguous(bool);
  PointerAssignmentChecker &set_isVolatile(bool);
  PointerAssignmentChecker &set_isBoundsRemapping(bool);
  bool Check(const SomeExpr &);

private:
  template <typename T> bool Check(const T &);
  template <typename T> bool Check(const evaluate::Expr<T> &);
  template <typename T> bool Check(const evaluate::FunctionRef<T> &);
  template <typename T> bool Check(const evaluate::Designator<T> &);
  bool Check(const evaluate::NullPointer &);
  bool Check(const evaluate::ProcedureDesignator &);
  bool Check(const evaluate::ProcedureRef &);
  // Target is a procedure
  bool Check(
      parser::CharBlock rhsName, bool isCall, const Procedure * = nullptr);
  bool LhsOkForUnlimitedPoly() const;
  template <typename... A> parser::Message *Say(A &&...);

  evaluate::FoldingContext &context_;
  const parser::CharBlock source_;
  const std::string description_;
  const Symbol *lhs_{nullptr};
  std::optional<TypeAndShape> lhsType_;
  std::optional<Procedure> procedure_;
  bool isContiguous_{false};
  bool isVolatile_{false};
  bool isBoundsRemapping_{false};
};

PointerAssignmentChecker &PointerAssignmentChecker::set_lhsType(
    std::optional<TypeAndShape> &&lhsType) {
  lhsType_ = std::move(lhsType);
  return *this;
}

PointerAssignmentChecker &PointerAssignmentChecker::set_isContiguous(
    bool isContiguous) {
  isContiguous_ = isContiguous;
  return *this;
}

PointerAssignmentChecker &PointerAssignmentChecker::set_isVolatile(
    bool isVolatile) {
  isVolatile_ = isVolatile;
  return *this;
}

PointerAssignmentChecker &PointerAssignmentChecker::set_isBoundsRemapping(
    bool isBoundsRemapping) {
  isBoundsRemapping_ = isBoundsRemapping;
  return *this;
}

template <typename T> bool PointerAssignmentChecker::Check(const T &) {
  // Catch-all case for really bad target expression
  Say("Target associated with %s must be a designator or a call to a"
      " pointer-valued function"_err_en_US,
      description_);
  return false;
}

template <typename T>
bool PointerAssignmentChecker::Check(const evaluate::Expr<T> &x) {
  return std::visit([&](const auto &x) { return Check(x); }, x.u);
}

bool PointerAssignmentChecker::Check(const SomeExpr &rhs) {
  if (HasVectorSubscript(rhs)) { // C1025
    Say("An array section with a vector subscript may not be a pointer target"_err_en_US);
    return false;
  } else if (ExtractCoarrayRef(rhs)) { // C1026
    Say("A coindexed object may not be a pointer target"_err_en_US);
    return false;
  } else {
    return std::visit([&](const auto &x) { return Check(x); }, rhs.u);
  }
}

bool PointerAssignmentChecker::Check(const evaluate::NullPointer &) {
  return true; // P => NULL() without MOLD=; always OK
}

template <typename T>
bool PointerAssignmentChecker::Check(const evaluate::FunctionRef<T> &f) {
  std::string funcName;
  const auto *symbol{f.proc().GetSymbol()};
  if (symbol) {
    funcName = symbol->name().ToString();
  } else if (const auto *intrinsic{f.proc().GetSpecificIntrinsic()}) {
    funcName = intrinsic->name;
  }
  auto proc{Procedure::Characterize(f.proc(), context_.intrinsics())};
  if (!proc) {
    return false;
  }
  std::optional<MessageFixedText> msg;
  const auto &funcResult{proc->functionResult}; // C1025
  if (!funcResult) {
    msg = "%s is associated with the non-existent result of reference to"
          " procedure"_err_en_US;
  } else if (procedure_) {
    // Shouldn't be here in this function unless lhs is an object pointer.
    msg = "Procedure %s is associated with the result of a reference to"
          " function '%s' that does not return a procedure pointer"_err_en_US;
  } else if (funcResult->IsProcedurePointer()) {
    msg = "Object %s is associated with the result of a reference to"
          " function '%s' that is a procedure pointer"_err_en_US;
  } else if (!funcResult->attrs.test(FunctionResult::Attr::Pointer)) {
    msg = "%s is associated with the result of a reference to function '%s'"
          " that is a not a pointer"_err_en_US;
  } else if (isContiguous_ &&
      !funcResult->attrs.test(FunctionResult::Attr::Contiguous)) {
    msg = "CONTIGUOUS %s is associated with the result of reference to"
          " function '%s' that is not contiguous"_err_en_US;
  } else if (lhsType_) {
    const auto *frTypeAndShape{funcResult->GetTypeAndShape()};
    CHECK(frTypeAndShape);
    if (!lhsType_->IsCompatibleWith(context_.messages(), *frTypeAndShape)) {
      msg = "%s is associated with the result of a reference to function '%s'"
            " whose pointer result has an incompatible type or shape"_err_en_US;
    }
  }
  if (msg) {
    auto restorer{common::ScopedSet(lhs_, symbol)};
    Say(*msg, description_, funcName);
    return false;
  }
  return true;
}

template <typename T>
bool PointerAssignmentChecker::Check(const evaluate::Designator<T> &d) {
  const Symbol *last{d.GetLastSymbol()};
  const Symbol *base{d.GetBaseObject().symbol()};
  if (!last || !base) {
    context_.messages().Say("Pointer target is not a named entity"_err_en_US);
    return false;
  }
  std::optional<std::variant<MessageFixedText, MessageFormattedText>> msg;
  if (procedure_) {
    // Shouldn't be here in this function unless lhs is an object pointer.
    msg = "In assignment to procedure %s, the target is not a procedure or"
          " procedure pointer"_err_en_US;
  } else if (!evaluate::GetLastTarget(GetSymbolVector(d))) { // C1025
    msg = "In assignment to object %s, the target '%s' is not an object with"
          " POINTER or TARGET attributes"_err_en_US;
  } else if (auto rhsType{TypeAndShape::Characterize(d, context_)}) {
    if (!lhsType_) {
      msg = "%s associated with object '%s' with incompatible type or"
            " shape"_err_en_US;
    } else if (rhsType->corank() > 0 &&
        (isVolatile_ != last->attrs().test(Attr::VOLATILE))) { // C1020
      // TODO: what if A is VOLATILE in A%B%C?  need a better test here
      if (isVolatile_) {
        msg = "Pointer may not be VOLATILE when target is a"
              " non-VOLATILE coarray"_err_en_US;
      } else {
        msg = "Pointer must be VOLATILE when target is a"
              " VOLATILE coarray"_err_en_US;
      }
    } else if (rhsType->type().IsUnlimitedPolymorphic()) {
      if (!LhsOkForUnlimitedPoly()) {
        msg = "Pointer type must be unlimited polymorphic or non-extensible"
              " derived type when target is unlimited polymorphic"_err_en_US;
      }
    } else {
      if (!lhsType_->type().IsTypeCompatibleWith(rhsType->type())) {
        msg = MessageFormattedText{
            "Target type %s is not compatible with pointer type %s"_err_en_US,
            rhsType->type().AsFortran(), lhsType_->type().AsFortran()};

      } else if (!isBoundsRemapping_) {
        int lhsRank{evaluate::GetRank(lhsType_->shape())};
        int rhsRank{evaluate::GetRank(rhsType->shape())};
        if (lhsRank != rhsRank) {
          msg = MessageFormattedText{
              "Pointer has rank %d but target has rank %d"_err_en_US, lhsRank,
              rhsRank};
        }
      }
    }
  }
  if (msg) {
    auto restorer{common::ScopedSet(lhs_, last)};
    if (auto *m{std::get_if<MessageFixedText>(&*msg)}) {
      std::string buf;
      llvm::raw_string_ostream ss{buf};
      d.AsFortran(ss);
      Say(*m, description_, ss.str());
    } else {
      Say(std::get<MessageFormattedText>(*msg));
    }
    return false;
  }
  return true;
}

// Compare procedure characteristics for equality except that lhs may be
// Pure or Elemental when rhs is not.

// Common handling for procedure pointer right-hand sides
bool PointerAssignmentChecker::Check(
    parser::CharBlock rhsName, bool isCall, const Procedure *rhsProcedure) {
  std::optional<MessageFixedText> msg;
  if (!procedure_) {
    msg = "In assignment to object %s, the target '%s' is a procedure"
          " designator"_err_en_US;
  } else if (!rhsProcedure) {
    msg = "In assignment to procedure %s, the characteristics of the target"
          " procedure '%s' could not be determined"_err_en_US;
  } else if (CharacteristicsMatch(*procedure_, *rhsProcedure)) {
    // OK
  } else if (isCall) {
    msg = "Procedure %s associated with result of reference to function '%s'"
          " that is an incompatible procedure pointer"_err_en_US;
  } else if (procedure_->IsPure() && !rhsProcedure->IsPure()) {
    msg = "PURE procedure %s may not be associated with non-PURE"
          " procedure designator '%s'"_err_en_US;
  } else if (procedure_->IsElemental() && !rhsProcedure->IsElemental()) {
    msg = "ELEMENTAL procedure %s may not be associated with non-ELEMENTAL"
          " procedure designator '%s'"_err_en_US;
  } else if (procedure_->IsFunction() && !rhsProcedure->IsFunction()) {
    msg = "Function %s may not be associated with subroutine"
          " designator '%s'"_err_en_US;
  } else if (!procedure_->IsFunction() && rhsProcedure->IsFunction()) {
    msg = "Subroutine %s may not be associated with function"
          " designator '%s'"_err_en_US;
  } else if (procedure_->HasExplicitInterface() &&
      !rhsProcedure->HasExplicitInterface()) {
    msg = "Procedure %s with explicit interface may not be associated with"
          " procedure designator '%s' with implicit interface"_err_en_US;
  } else if (!procedure_->HasExplicitInterface() &&
      rhsProcedure->HasExplicitInterface()) {
    msg = "Procedure %s with implicit interface may not be associated with"
          " procedure designator '%s' with explicit interface"_err_en_US;
  } else {
    msg = "Procedure %s associated with incompatible procedure"
          " designator '%s'"_err_en_US;
  }
  if (msg) {
    Say(std::move(*msg), description_, rhsName);
    return false;
  }
  return true;
}

bool PointerAssignmentChecker::Check(const evaluate::ProcedureDesignator &d) {
  if (auto chars{Procedure::Characterize(d, context_.intrinsics())}) {
    return Check(d.GetName(), false, &*chars);
  } else {
    return Check(d.GetName(), false);
  }
}

bool PointerAssignmentChecker::Check(const evaluate::ProcedureRef &ref) {
  const Procedure *procedure{nullptr};
  auto chars{Procedure::Characterize(ref, context_.intrinsics())};
  if (chars) {
    procedure = &*chars;
    if (chars->functionResult) {
      if (const auto *proc{chars->functionResult->IsProcedurePointer()}) {
        procedure = proc;
      }
    }
  }
  return Check(ref.proc().GetName(), true, procedure);
}

// The target can be unlimited polymorphic if the pointer is, or if it is
// a non-extensible derived type.
bool PointerAssignmentChecker::LhsOkForUnlimitedPoly() const {
  const auto &type{lhsType_->type()};
  if (type.category() != TypeCategory::Derived || type.IsAssumedType()) {
    return false;
  } else if (type.IsUnlimitedPolymorphic()) {
    return true;
  } else {
    return !IsExtensibleType(&type.GetDerivedTypeSpec());
  }
}

template <typename... A>
parser::Message *PointerAssignmentChecker::Say(A &&...x) {
  auto *msg{context_.messages().Say(std::forward<A>(x)...)};
  if (lhs_) {
    return evaluate::AttachDeclaration(msg, *lhs_);
  } else if (!source_.empty()) {
    msg->Attach(source_, "Declaration of %s"_en_US, description_);
  }
  return msg;
}

// Verify that any bounds on the LHS of a pointer assignment are valid.
// Return true if it is a bound-remapping so we can perform further checks.
static bool CheckPointerBounds(
    evaluate::FoldingContext &context, const evaluate::Assignment &assignment) {
  auto &messages{context.messages()};
  const SomeExpr &lhs{assignment.lhs};
  const SomeExpr &rhs{assignment.rhs};
  bool isBoundsRemapping{false};
  std::size_t numBounds{std::visit(
      common::visitors{
          [&](const evaluate::Assignment::BoundsSpec &bounds) {
            return bounds.size();
          },
          [&](const evaluate::Assignment::BoundsRemapping &bounds) {
            isBoundsRemapping = true;
            evaluate::ExtentExpr lhsSizeExpr{1};
            for (const auto &bound : bounds) {
              lhsSizeExpr = std::move(lhsSizeExpr) *
                  (common::Clone(bound.second) - common::Clone(bound.first) +
                      evaluate::ExtentExpr{1});
            }
            if (std::optional<std::int64_t> lhsSize{evaluate::ToInt64(
                    evaluate::Fold(context, std::move(lhsSizeExpr)))}) {
              if (auto shape{evaluate::GetShape(context, rhs)}) {
                if (std::optional<std::int64_t> rhsSize{
                        evaluate::ToInt64(evaluate::Fold(
                            context, evaluate::GetSize(std::move(*shape))))}) {
                  if (*lhsSize > *rhsSize) {
                    messages.Say(
                        "Pointer bounds require %d elements but target has"
                        " only %d"_err_en_US,
                        *lhsSize, *rhsSize); // 10.2.2.3(9)
                  }
                }
              }
            }
            return bounds.size();
          },
          [](const auto &) -> std::size_t {
            DIE("not valid for pointer assignment");
          },
      },
      assignment.u)};
  if (numBounds > 0) {
    if (lhs.Rank() != static_cast<int>(numBounds)) {
      messages.Say("Pointer '%s' has rank %d but the number of bounds specified"
                   " is %d"_err_en_US,
          lhs.AsFortran(), lhs.Rank(), numBounds); // C1018
    }
  }
  if (isBoundsRemapping && rhs.Rank() != 1 &&
      !evaluate::IsSimplyContiguous(rhs, context.intrinsics())) {
    messages.Say("Pointer bounds remapping target must have rank 1 or be"
                 " simply contiguous"_err_en_US); // 10.2.2.3(9)
  }
  return isBoundsRemapping;
}

bool CheckPointerAssignment(
    evaluate::FoldingContext &context, const evaluate::Assignment &assignment) {
  return CheckPointerAssignment(context, assignment.lhs, assignment.rhs,
      CheckPointerBounds(context, assignment));
}

bool CheckPointerAssignment(evaluate::FoldingContext &context,
    const SomeExpr &lhs, const SomeExpr &rhs, bool isBoundsRemapping) {
  const Symbol *pointer{GetLastSymbol(lhs)};
  if (!pointer) {
    return false; // error was reported
  }
  if (!IsPointer(*pointer)) {
    evaluate::SayWithDeclaration(context.messages(), *pointer,
        "'%s' is not a pointer"_err_en_US, pointer->name());
    return false;
  }
  if (pointer->has<ProcEntityDetails>() && evaluate::ExtractCoarrayRef(lhs)) {
    context.messages().Say( // C1027
        "Procedure pointer may not be a coindexed object"_err_en_US);
    return false;
  }
  return PointerAssignmentChecker{context, *pointer}
      .set_isBoundsRemapping(isBoundsRemapping)
      .Check(rhs);
}

bool CheckPointerAssignment(
    evaluate::FoldingContext &context, const Symbol &lhs, const SomeExpr &rhs) {
  CHECK(IsPointer(lhs));
  return PointerAssignmentChecker{context, lhs}.Check(rhs);
}

bool CheckPointerAssignment(evaluate::FoldingContext &context,
    parser::CharBlock source, const std::string &description,
    const DummyDataObject &lhs, const SomeExpr &rhs) {
  return PointerAssignmentChecker{context, source, description}
      .set_lhsType(common::Clone(lhs.type))
      .set_isContiguous(lhs.attrs.test(DummyDataObject::Attr::Contiguous))
      .set_isVolatile(lhs.attrs.test(DummyDataObject::Attr::Volatile))
      .Check(rhs);
}

bool CheckInitialTarget(evaluate::FoldingContext &context,
    const SomeExpr &pointer, const SomeExpr &init) {
  return evaluate::IsInitialDataTarget(init, &context.messages()) &&
      CheckPointerAssignment(context, pointer, init);
}

} // namespace Fortran::semantics


bool IntFeatureSpace::DeSerialize(bool swap, FILE* fp) {
  if (fread(&x_buckets_, sizeof(x_buckets_), 1, fp) != 1)
    return false;
  if (fread(&y_buckets_, sizeof(y_buckets_), 1, fp) != 1)
    return false;
  if (fread(&theta_buckets_, sizeof(theta_buckets_), 1, fp) != 1)
    return false;
  return true;
}

// Author: example@nnthink.com (Ray Smith)
// Description: Indexed feature space based on INT_FEATURE_STRUCT.

#include "intfeaturespace.h"
#include "intfx.h"

namespace tesseract {

IntFeatureSpace::IntFeatureSpace()
  : x_buckets_(0), y_buckets_(0), theta_buckets_(0) {
}

void IntFeatureSpace::Init(uinT8 xbuckets, uinT8 ybuckets, uinT8 thetabuckets) {
  x_buckets_ = xbuckets;
  y_buckets_ = ybuckets;
  theta_buckets_ = thetabuckets;
}

// Serializes the feature space definition to the given file.
// Returns false on error.
bool IntFeatureSpace::Serialize(FILE* fp) const {
  if (fwrite(&x_buckets_, sizeof(x_buckets_), 1, fp) != 1)
    return false;
  if (fwrite(&y_buckets_, sizeof(y_buckets_), 1, fp) != 1)
    return false;
  if (fwrite(&theta_buckets_, sizeof(theta_buckets_), 1, fp) != 1)
    return false;
  return true;
}

// DeSerializes the feature space definition from the given file.
// If swap is true, the data is big/little-endian swapped.
// Returns false on error.

// Returns an INT_FEATURE_STRUCT corresponding to the given index.
// This is the inverse of the Index member.
INT_FEATURE_STRUCT IntFeatureSpace::PositionFromIndex(int index) const {
  return PositionFromBuckets(index / (y_buckets_ * theta_buckets_),
                             index / theta_buckets_ % y_buckets_,
                             index % theta_buckets_);
}

// Bulk calls to Index. Maps the given array of features to a vector of
// inT32 indices in the same order as the input.
void IntFeatureSpace::IndexFeatures(const INT_FEATURE_STRUCT* features,
                                    int num_features,
                                    GenericVector<int>* mapped_features) const {
  mapped_features->truncate(0);
  for (int f = 0; f < num_features; ++f)
    mapped_features->push_back(Index(features[f]));
}

// Bulk calls to Index. Maps the given array of features to a vector of
// sorted inT32 indices.
void IntFeatureSpace::IndexAndSortFeatures(
    const INT_FEATURE_STRUCT* features, int num_features,
    GenericVector<int>* sorted_features) const {
  sorted_features->truncate(0);
  for (int f = 0; f < num_features; ++f)
    sorted_features->push_back(Index(features[f]));
  sorted_features->sort();
}

// Returns a feature space index for the given x,y position in a display
// window, or -1 if the feature is a miss.
int IntFeatureSpace::XYToFeatureIndex(int x, int y) const {
  // Round the x,y position to a feature. Search for a valid theta.
  INT_FEATURE_STRUCT feature(x, y, 0);
  int index = -1;
  for (int theta = 0; theta <= MAX_UINT8 && index < 0; ++theta) {
    feature.Theta = theta;
    index = Index(feature);
  }
  if (index < 0) {
    tprintf("(%d,%d) does not exist in feature space!\n", x, y);
    return -1;
  }
  feature = PositionFromIndex(index);
  tprintf("Click at (%d, %d) ->(%d, %d), ->(%d, %d)\n",
          x, y, feature.X, feature.Y, x - feature.X, y - feature.Y);
  // Get the relative position of x,y from the rounded feature.
  x -= feature.X;
  y -= feature.Y;
  if (x != 0 || y != 0) {
    double angle = atan2(static_cast<double>(y), static_cast<double>(x)) + PI;
    angle *= kIntFeatureExtent / (2.0 * PI);
    feature.Theta = static_cast<uinT8>(angle + 0.5);
    index = Index(feature);
    if (index < 0) {
      tprintf("Feature failed to map to a valid index:");
      feature.print();
      return -1;
    }
    feature = PositionFromIndex(index);
  }
  feature.print();
  return index;
}

// Returns an INT_FEATURE_STRUCT corresponding to the given bucket coords.
INT_FEATURE_STRUCT IntFeatureSpace::PositionFromBuckets(int x,
                                                        int y,
                                                        int theta) const {
  INT_FEATURE_STRUCT pos(
      (x * kIntFeatureExtent + kIntFeatureExtent / 2) / x_buckets_,
      (y * kIntFeatureExtent + kIntFeatureExtent / 2) / y_buckets_,
      DivRounded(theta * kIntFeatureExtent, theta_buckets_));
  return pos;
}

}  // namespace tesseract.


void updateView()
{

    auto policy = g_eglView->getResolutionPolicy();
    auto designSize = g_eglView->getDesignResolutionSize();

    if (g_landscape)
    {
        g_eglView->setFrameSize(g_screenSize.width, g_screenSize.height);
    }
    else
    {
        g_eglView->setFrameSize(g_screenSize.height, g_screenSize.width);
    }

    g_eglView->setDesignResolutionSize(designSize.width, designSize.height, policy);

    updateMenu();
}

/****************************************************************************

http://www.cocos2d-x.org

****************************************************************************/

#include "SimulatorWindow.h"

#include "cocos2d.h"
#include "glfw3native.h"
#include "resource.h"
#include "Runtime.h"
#include "ConfigParser.h"

#include <string>
#include <vector>
using namespace std;
using namespace cocos2d;

WNDPROC g_oldProc=NULL;
bool g_landscape=false;
CCSize g_screenSize;
GLView* g_eglView=NULL;
INT_PTR CALLBACK AboutDialogCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);

void createViewMenu()
{
    HMENU hSysMenu = GetSystemMenu(glfwGetWin32Window(g_eglView->getWindow()), FALSE);
    HMENU viewMenu = GetSubMenu(hSysMenu, 8);
    for (int i = ConfigParser::getInstance()->getScreenSizeCount() - 1; i >= 0; --i)
    {
        SimulatorScreenSize size = ConfigParser::getInstance()->getScreenSize(i);
        wstring menuName;
        menuName.assign(size.title.begin(), size.title.end());

        MENUITEMINFO item;
        ZeroMemory(&item, sizeof(item));
        item.cbSize = sizeof(item);
        item.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
        item.fType = MFT_STRING;
        item.wID = ID_VIEW_SIZE + i;
        item.dwTypeData = (LPTSTR)menuName.c_str();
        item.cch = menuName.length();

        InsertMenuItem(viewMenu, 0, TRUE, &item);
    }
}

void updateMenu()
{
    HMENU hSysMenu = GetSystemMenu(glfwGetWin32Window(g_eglView->getWindow()), FALSE);
    HMENU viewMenu = GetSubMenu(hSysMenu, 8);

    if (g_landscape)
    {
        CheckMenuItem(viewMenu, ID_VIEW_PORTRAIT, MF_BYCOMMAND | MF_UNCHECKED);
        CheckMenuItem(viewMenu, ID_VIEW_LANDSCAPE, MF_BYCOMMAND | MF_CHECKED);
    }
    else
    {
        CheckMenuItem(viewMenu, ID_VIEW_PORTRAIT, MF_BYCOMMAND | MF_CHECKED);
        CheckMenuItem(viewMenu, ID_VIEW_LANDSCAPE, MF_BYCOMMAND | MF_UNCHECKED);
    }

    int width = g_screenSize.width;
    int height = g_screenSize.height;
    if (height > width)
    {
        int w = width;
        width = height;
        height = w;
    }

    int count = ConfigParser::getInstance()->getScreenSizeCount();
    for (int i = 0; i < count; ++i)
    {
        bool bSel = false;

        SimulatorScreenSize size = ConfigParser::getInstance()->getScreenSize(i);
        if (size.width == width && size.height == height)
        {
            bSel = true;
        }
        CheckMenuItem(viewMenu, i, MF_BYPOSITION | (bSel? MF_CHECKED : MF_UNCHECKED));
    }

    int scale=g_eglView->getFrameZoomFactor()*100;
    CheckMenuItem(viewMenu, ID_VIEW_ZOOMOUT100, MF_BYCOMMAND | MF_UNCHECKED);
    CheckMenuItem(viewMenu, ID_VIEW_ZOOMOUT75, MF_BYCOMMAND | MF_UNCHECKED);
    CheckMenuItem(viewMenu, ID_VIEW_ZOOMOUT50, MF_BYCOMMAND | MF_UNCHECKED);
    CheckMenuItem(viewMenu, ID_VIEW_ZOOMOUT25, MF_BYCOMMAND | MF_UNCHECKED);
    switch (scale)
    {
    case 100:
        CheckMenuItem(viewMenu, ID_VIEW_ZOOMOUT100, MF_BYCOMMAND | MF_CHECKED);
        break;
    case 75:
        CheckMenuItem(viewMenu, ID_VIEW_ZOOMOUT75, MF_BYCOMMAND | MF_CHECKED);
        break;
    case 50:
        CheckMenuItem(viewMenu, ID_VIEW_ZOOMOUT50, MF_BYCOMMAND | MF_CHECKED);
        break;
    case 25:
        CheckMenuItem(viewMenu, ID_VIEW_ZOOMOUT25, MF_BYCOMMAND | MF_CHECKED);
        break;
    default:
        break;
    }
}

/*@brief updateView*/

void onViewChangeOrientation(int viewMenuID)
{
    if (viewMenuID == ID_VIEW_PORTRAIT && g_landscape)
    {
        g_landscape = false;
        updateView();
    }
    else if (viewMenuID == ID_VIEW_LANDSCAPE && !g_landscape)
    {
        g_landscape = true;
        updateView();
    }
}

void onViewZoomOut(int viewMenuID)
{
    float scale = 1.0;
    switch (viewMenuID)
    {
    case ID_VIEW_ZOOMOUT100:
        scale=1.0;
        break;
    case ID_VIEW_ZOOMOUT75:
        scale=0.75;
        break;
    case ID_VIEW_ZOOMOUT50:
        scale=0.50;
        break;
    case ID_VIEW_ZOOMOUT25:
        scale=0.25;
        break;
    default:
        break;
    }
    g_eglView->setFrameZoomFactor(scale);
    updateView();
}

void onViewChangeFrameSize(int viewMenuID)
{
    int index = viewMenuID - ID_VIEW_SIZE;
    if (index >= 0 && index < ConfigParser::getInstance()->getScreenSizeCount())
    {
        SimulatorScreenSize size = ConfigParser::getInstance()->getScreenSize(index);
        g_screenSize.width = size.width;
        g_screenSize.height = size.height;
        updateView();
    }
}

void onHelpAbout()
{
    DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG_ABOUT), glfwGetWin32Window(g_eglView->getWindow()), AboutDialogCallback);
}

void shutDownApp()
{
    HWND hWnd=glfwGetWin32Window(g_eglView->getWindow());
    ::SendMessage(hWnd,WM_CLOSE,NULL,NULL);
}

void reStart()
{
    PROCESS_INFORMATION info;
    STARTUPINFO startup;
    TCHAR szPath[128]={0};
    TCHAR *szCmdLine=NULL;
    GetModuleFileName(NULL, szPath, sizeof(szPath));
    szCmdLine = GetCommandLine();
    GetStartupInfo(&startup);
    BOOL bSucc = CreateProcess(szPath, szCmdLine, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &startup, &info);
    if(bSucc)
    {
        ExitProcess(-1);
    }
}
/*@brief new windows process*/
LRESULT CALLBACK SNewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    switch (message)
    {
    case WM_KEYDOWN:
        if (wParam == VK_F5)
        {
            reStart();
             break;
        }
    case WM_SYSCOMMAND:
        {
            wmId    = LOWORD(wParam);
            wmEvent = HIWORD(wParam);

            switch (wmId)
            {
            case ID_FILE_EXIT:
                shutDownApp();
                break;

            case ID_VIEW_PORTRAIT:
            case ID_VIEW_LANDSCAPE:
                onViewChangeOrientation(wmId);
                break;

            case ID_VIEW_ZOOMOUT100:
            case ID_VIEW_ZOOMOUT75:
            case ID_VIEW_ZOOMOUT50:
            case ID_VIEW_ZOOMOUT25:
                onViewZoomOut(wmId);
                break;

            case ID_CONTROL_RELOAD:
                reStart();
                break;

            case ID_HELP_ABOUT:
                onHelpAbout();
                break;
            default:
                if (wmId >= ID_VIEW_SIZE && wmId <= ID_VIEW_SIZE + ConfigParser::getInstance()->getScreenSizeCount() - 1)
                {
                    onViewChangeFrameSize(wmId);
                    break;
                }
            }
        }
        break;
    }
    return g_oldProc(hWnd, message, wParam, lParam);
}

/*@brief AboutDialog Callback*/
INT_PTR CALLBACK AboutDialogCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}

void createSimulator(const char* viewName, float width, float height, bool isLandscape, float frameZoomFactor)
{
    if (g_eglView)
    {
        return;
    }

    g_landscape = isLandscape;
    if(height > width)
    {
        float tmpvalue =width;
        width = height;
        height = tmpvalue;
    }
    g_screenSize.width = width;
    g_screenSize.height = height;

    if(!g_landscape)
    {
        float tmpvalue =width;
        width = height;
        height = tmpvalue;
    }

    g_eglView = GLView::createWithRect(viewName,Rect(0,0,width,height),frameZoomFactor);
    auto director = Director::getInstance();
    director->setOpenGLView(g_eglView);

    HWND hWnd=glfwGetWin32Window(g_eglView->getWindow());
    HMENU hMenu = LoadMenu(GetModuleHandle(NULL), MAKEINTRESOURCE(IDR_MENU_COCOS));
    HMENU hSysMenu = GetSystemMenu(hWnd, FALSE);
    HMENU hviewMenu = GetSubMenu(hMenu,1);
    HMENU hcontrolMenu = GetSubMenu(hMenu,2);
    AppendMenu(hSysMenu,MF_SEPARATOR,0,NULL);
    if (hSysMenu != INVALID_HANDLE_VALUE && hMenu != INVALID_HANDLE_VALUE)
    {
        AppendMenu(hSysMenu, MF_POPUP, (UINT)hviewMenu, TEXT("view"));
        AppendMenu(hSysMenu, MF_POPUP, (UINT)hcontrolMenu, TEXT("control"));
    }
    createViewMenu();
    updateMenu();

    g_oldProc = (WNDPROC)SetWindowLong(hWnd, GWL_WNDPROC, (LONG)SNewWndProc);
    if (g_oldProc==0)
    {
        printf("SetWindowLong NewWndProc Error:%d\n",GetLastError());
    }

}


CXFA_MeridiemNames::CXFA_MeridiemNames(CXFA_Document* doc,
                                       XFA_PacketType packet)
    : CXFA_Node(doc,
                packet,
                XFA_XDPPACKET_LocaleSet,
                XFA_ObjectType::Node,
                XFA_Element::MeridiemNames,
                kMeridiemNamesPropertyData,
                {},
                pdfium::MakeUnique<CJX_Node>(this)) {}

#include "xfa/fxfa/parser/cxfa_meridiemnames.h"

#include "fxjs/xfa/cjx_node.h"
#include "third_party/base/ptr_util.h"

namespace {

const CXFA_Node::PropertyData kMeridiemNamesPropertyData[] = {
    {XFA_Element::Meridiem, 2, 0},
};

}  // namespace


const QwtPlot *QwtPlotMagnifier::plot() const
{
    const QWidget *w = canvas();
    if ( w )
        w = w->parentWidget();

    return qobject_cast<const QwtPlot *>( w );
}

/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
 * Qwt Widget Library
 *
 *****************************************************************************/

#include "qwt_plot.h"
#include "qwt_scale_div.h"
#include "qwt_plot_magnifier.h"
#include <qevent.h>

class QwtPlotMagnifier::PrivateData
{
public:
    PrivateData()
    {
        for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
            isAxisEnabled[axis] = true;
    }

    bool isAxisEnabled[QwtPlot::axisCnt];
};

/*!
   Constructor
   \param canvas Plot canvas to be magnified
*/
QwtPlotMagnifier::QwtPlotMagnifier( QWidget *canvas ):
    QwtMagnifier( canvas )
{
    d_data = new PrivateData();
}

QwtPlotMagnifier::~QwtPlotMagnifier()
{
    delete d_data;
}

/*!
   \brief En/Disable an axis

   Only Axes that are enabled will be zoomed.
   All other axes will remain unchanged.

   \param axis Axis, see QwtPlot::Axis
   \param on On/Off

   \sa isAxisEnabled()
*/
void QwtPlotMagnifier::setAxisEnabled( int axis, bool on )
{
    if ( axis >= 0 && axis < QwtPlot::axisCnt )
        d_data->isAxisEnabled[axis] = on;
}

/*!
   Test if an axis is enabled

   \param axis Axis, see QwtPlot::Axis
   \return True, if the axis is enabled

   \sa setAxisEnabled()
*/
bool QwtPlotMagnifier::isAxisEnabled( int axis ) const
{
    if ( axis >= 0 && axis < QwtPlot::axisCnt )
        return d_data->isAxisEnabled[axis];

    return true;
}

//! Return observed plot canvas
QWidget *QwtPlotMagnifier::canvas()
{
    return parentWidget();
}

//! Return Observed plot canvas
const QWidget *QwtPlotMagnifier::canvas() const
{
    return parentWidget();
}

//! Return plot widget, containing the observed plot canvas
QwtPlot *QwtPlotMagnifier::plot()
{
    QWidget *w = canvas();
    if ( w )
        w = w->parentWidget();

    return qobject_cast<QwtPlot *>( w );
}

//! Return plot widget, containing the observed plot canvas

/*!
   Zoom in/out the axes scales
   \param factor A value < 1.0 zooms in, a value > 1.0 zooms out.
*/
void QwtPlotMagnifier::rescale( double factor )
{
    QwtPlot* plt = plot();
    if ( plt == NULL )
        return;

    factor = qAbs( factor );
    if ( factor == 1.0 || factor == 0.0 )
        return;

    bool doReplot = false;

    const bool autoReplot = plt->autoReplot();
    plt->setAutoReplot( false );

    for ( int axisId = 0; axisId < QwtPlot::axisCnt; axisId++ )
    {
        const QwtScaleDiv &scaleDiv = plt->axisScaleDiv( axisId );
        if ( isAxisEnabled( axisId ) )
        {
            const double center =
                scaleDiv.lowerBound() + scaleDiv.range() / 2;
            const double width_2 = scaleDiv.range() / 2 * factor;

            plt->setAxisScale( axisId, center - width_2, center + width_2 );
            doReplot = true;
        }
    }

    plt->setAutoReplot( autoReplot );

    if ( doReplot )
        plt->replot();
}