/*
 * Copyright (C) 1997-2008 ZSNES Team ( zsKnight, _Demo_, pagefault, Nach )
 *
 * http://www.zsnes.com
 * http://sourceforge.net/projects/zsnes
 * https://zsnes.bountysource.com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifndef lengthof
#define lengthof(x) (sizeof(x) / sizeof *(x))
#endif
#include <string.h>

#include "../c_intrf.h"
#include "../c_vcache.h"
#include "../cpu/c_65816d.h"
#include "../cpu/c_execute.h"
#include "../cpu/c_memory.h"
#include "../cpu/execute.h"
#include "../cpu/memory.h"
#include "../cpu/memtable.h"
#include "../cpu/regs.h"
#include "../cpu/spc700.h"
#include "../debugger.h"
#include "../endmem.h"
#include "../init.h"
#include "../initc.h"
#include "../types.h"
#include "../ui.h"
#include "../vcache.h"
#include "../video/procvid.h"
#include "../video/procvidc.h"
#include "../zip/zpng.h"
#include "../zstate.h"
#include "cfg.h"
#include "gui.h"
#include "guifuncs.h"
#include "menu.h"

u1 NoInputRead;
u1 SPCSave;
u1 keyonsn;
u1 nextmenupopup;

static u1 PrevMenuPos;
static u1 MenuNoExit;
static u4 MenuDisplace;
static u4 MenuDisplace16;
static u1 menu16btrans;
static u4 menucloc;
static u2 allred;

static char const menudrawbox_string[] = "MISC OPTIONS";
static char const menudrawbox_stringa[] = "SAVE SNAPSHOT";
static char const menudrawbox_stringb[] = "SHOW FPS";
static char const menudrawbox_stringc[] = "HIDE FPS";
static char const menudrawbox_stringd[] = "SAVE SPC DATA";
static char const menudrawbox_stringe[] = "SOUND BUFFER DUMP";
static char const menudrawbox_stringf[] = "SNAPSHOT/INCR FRM";
static char const menudrawbox_stringg[] = "INCR FRAME ONLY";
static char const menudrawbox_stringh[] = "MOVE THIS WINDOW";
static char menudrawbox_stringi[] = "IMAGE FORMAT: ---";
static char const menudrawbox_stringj[] = "SHOW CPU USAGE";
static char const menudrawbox_stringk[] = "HIDE CPU USAGE";

static void GUIBufferData(void)
{
    // copy to spritetable
    u4 const n = 129536;
    memcpy(spritetablea + 4 * 384, vidbuffer + 4 * 384, n);
    memset(sprlefttot, 0, sizeof(sprlefttot));
    memset(sprleftpr, 0, sizeof(sprleftpr));
    memset(sprleftpr1, 0, sizeof(sprleftpr1));
    memset(sprleftpr2, 0, sizeof(sprleftpr2));
    memset(sprleftpr3, 0, sizeof(sprleftpr3));
}

static void GUIUnBuffer(void)
{
    // copy from spritetable
    u4 const n = 129536;
    memcpy(vidbuffer + 4 * 384, spritetablea + 4 * 384, n);
}

static void menudrawcursor16b(void)
{
    // draw a small red box
    u2* buf = (u2*)vidbuffer + MenuDisplace16 / 2 + menucloc + 41 + 34 * 288;
    u2 const c = allred;
    u4 h = 9;
    do {
        u4 w = 148;
        do
            *buf++ = c;
        while (--w != 0);
        buf += 288 - 148;
    } while (--h != 0);
}

static void menudrawbox16b(void)
{
    if (menu16btrans == 0) { // draw shadow behind box
        menu16btrans = 1;
        u2* buf = (u2*)vidbuffer + MenuDisplace16 / 2 + 50 + 30 * 288;
        u4 h = 95;
        do {
            u4 w = 150;
            do {
                *buf = (*buf & vesa2_clbit) >> 1;
                ++buf;
            } while (--w != 0);
            buf += 288 - 150;
        } while (--h != 0);
    }

    allred = 0x1F << vesa2_rpos;

    // draw a small blue box with a white border
    u2* buf = (u2*)vidbuffer + MenuDisplace16 / 2 + 40 + 20 * 288;
    u2 c = 0x12 << vesa2_bpos;
    u2 const dc = 0x01 << vesa2_gpos | 0x01 << vesa2_rpos;
    u4 step = 5;
    u4 h = 95;
    do {
        u4 w = 150;
        do
            *buf++ = c;
        while (--w != 0);
        buf += 288 - 150;
        if (--step == 0) {
            c += dc;
            step = 5;
        }
    } while (--h != 0);

    // Draw lines
    drawhline16b((u2*)vidbuffer + MenuDisplace16 / 2 + 40 + 20 * 288, 150, 0xFFFF);
    drawvline16b((u2*)vidbuffer + MenuDisplace16 / 2 + 40 + 20 * 288, 95, 0xFFFF);
    drawhline16b((u2*)vidbuffer + MenuDisplace16 / 2 + 40 + 114 * 288, 150, 0xFFFF);
    drawhline16b((u2*)vidbuffer + MenuDisplace16 / 2 + 40 + 32 * 288, 150, 0xFFFF);
    drawvline16b((u2*)vidbuffer + MenuDisplace16 / 2 + 189 + 20 * 288, 95, 0xFFFF);
    menudrawcursor16b();

    OutputGraphicString16b((u2*)vidbuffer + MenuDisplace16 / 2 + 45 + 23 * 288, menudrawbox_string);
    OutputGraphicString16b((u2*)vidbuffer + MenuDisplace16 / 2 + 45 + 35 * 288, menudrawbox_stringa);
    OutputGraphicString16b((u2*)vidbuffer + MenuDisplace16 / 2 + 45 + 45 * 288, FPSOn & 1 ? menudrawbox_stringc : menudrawbox_stringb);
    OutputGraphicString16b((u2*)vidbuffer + MenuDisplace16 / 2 + 45 + 55 * 288, menudrawbox_stringd);
    OutputGraphicString16b((u2*)vidbuffer + MenuDisplace16 / 2 + 45 + 65 * 288, menudrawbox_stringe);
    OutputGraphicString16b((u2*)vidbuffer + MenuDisplace16 / 2 + 45 + 75 * 288, menudrawbox_stringf);
    OutputGraphicString16b((u2*)vidbuffer + MenuDisplace16 / 2 + 45 + 85 * 288, menudrawbox_stringg);
    OutputGraphicString16b((u2*)vidbuffer + MenuDisplace16 / 2 + 45 + 95 * 288, menudrawbox_stringh);
    OutputGraphicString16b((u2*)vidbuffer + MenuDisplace16 / 2 + 45 + 105 * 288, menudrawbox_stringi);
    OutputGraphicString16b((u2*)vidbuffer + MenuDisplace16 / 2 + 45 + 115 * 288, CPUOn & 1 ? menudrawbox_stringj : menudrawbox_stringk);
    copyvid();
}

static void menudrawbox8b(void)
{
    menudrawbox16b();
}

static void saveimage(void)
{
    pressed[1] = 0;
    pressed[59] = 0;

#ifndef NO_PNG
    if (ScreenShotFormat == 1) {
        Grab_PNG_Data();
        return;
    }
#endif
    {
        Grab_BMP_Data();
    }
}

static void breakatsignb(void)
{
    keyonsn = 0;
#ifndef NO_DEBUGGER
    if (SPCSave == 1)
        debuggeron = 1;
#endif

    exiter = 1;
    u4 eax = xpc;
    u4 ebx = xpb;
    u1* esi = eax & 0x8000 ? snesmmap[ebx] : eax < 0x4300 || memtabler8[ebx] != regaccessbankr8 ? snesmap2[ebx]
                                                                                                : (u1*)dmadata - 0x4300; // XXX ugly cast
    initaddrl = esi;
    esi += eax; // add program counter to address
    u1* ebp = spcPCRam;
    u4 edx = curcyc /* cycles */ << 8 | xp /* flags */;
    opfn** edi = Curtableaddr;
    UpdateDPage();
    // execute
    do {
        splitflags(edx);
        execute(&edx, &ebp, &esi, &edi);
        edx = joinflags(edx);
        edx = (edx & 0xFFFF00FF) | pdh << 8;

#ifndef NO_DEBUGGER
        if ((++numinst & 0xFF) == 0 && Check_Key() != 0 && Get_Key() == 27)
            break;
#endif
        if (SPCRAM[6] == 0x40)
            break;
    } while (keyonsn != 1);

    // copy back data
    spcPCRam = ebp;
    Curtableaddr = edi;
    xp = edx;
    curcyc = edx >> 8;
    xpc = esi - initaddrl; // subtract program counter by address
    exiter = 0;

#ifndef NO_DEBUGGER
    if (SPCSave == 1)
        debuggeron = 0;
#endif
}

void showmenu(void)
{
    for (;;) {
        ForceNonTransp = 1;
        NoInputRead = 0;
        if (SSKeyPressed == 1) {
            SSKeyPressed = 0;
            saveimage();
        } else if (SPCKeyPressed == 1) {
            goto savespckey;
        } else if (pressed[14] & 1) {
            saveimage();
        } else {
            menucloc = 0;
            if (nextmenupopup != 0) {
                pressed[0x1C] = 0;
                switch (PrevMenuPos) {
                default:
                    menucloc = 40 * 288;
                    break;
                case 1:
                    menucloc = 50 * 288;
                    break;
                case 2:
                    menucloc = 60 * 288;
                    break;
                }
            }
            if (PrevMenuPos == 3)
                menucloc = 70 * 288;

            char const* fmt = " BMP";
#ifndef NO_PNG
            if (ScreenShotFormat != 0) {
                {
                    fmt = " PNG";
                }
            }
#endif
            memcpy(menudrawbox_stringi + 13, fmt, 4);

            nextmenupopup = 0;
            menu16btrans = 0;
            pressed[1] = 0;
            pressed[59] = 0;
            curblank = 0;
            GUIBufferData();
            // Draw box
            menudrawbox8b();
            if (newengen != 0)
                GUIOn = 1;
            copyvid();
            StopSound();
            for (;;) {
                // GUIUnBuffer();
                menudrawbox8b();
                copyvid();

                JoyRead();
                if (Check_Key() == 0)
                    continue;
                u1 const key = Get_Key();
                if (key == 0) {
                    u1 const ext = Get_Key();
                    if (ext == 72) {
                        if (menucloc == 0)
                            menucloc += 80 * 288;
                        menucloc -= 10 * 288;
                        menudrawbox8b();
                    } else if (ext == 80) {
                        if (menucloc == 70 * 288)
                            menucloc -= 80 * 288;
                        menucloc += 10 * 288;
                        menudrawbox8b();
                        copyvid();
                    }
                } else if (key == 27)
                    goto exitloop;
                else if (key == 13)
                    break;
            }
            GUIUnBuffer();
            copyvid();
            if (menucloc == 0)
                saveimage();
            if (menucloc == 40 * 288) {
                saveimage();
                ExecExitOkay = 0;
                nextmenupopup = 3;
                NoInputRead = 1;
                t1cc = 0;
                PrevMenuPos = 0;
            }
            if (menucloc == 50 * 288) {
                ExecExitOkay = 0;
                nextmenupopup = 3;
                NoInputRead = 1;
                t1cc = 0;
                PrevMenuPos = 1;
            }
            if (menucloc == 70 * 288) {
                {
                    ScreenShotFormat ^= 1;
                    MenuNoExit = 1;
                    ExecExitOkay = 0;
                    nextmenupopup = 1;
                    NoInputRead = 1;
                    t1cc = 0;
                    PrevMenuPos = 3;
                }
            }
            if (menucloc == 60 * 288) {
                MenuNoExit = 1;
                ExecExitOkay = 0;
                nextmenupopup = 1;
                NoInputRead = 1;
                t1cc = 0;
                PrevMenuPos = 2;
                if (MenuDisplace != 0) {
                    MenuDisplace = 0;
                    MenuDisplace16 = 0;
                } else {
                    MenuDisplace = 90 * 288;
                    MenuDisplace16 = 90 * 288 * 2;
                }
            }
            if (menucloc == 10 * 288) {
                if (frameskip != 0) {
                    Msgptr = "NEED AUTO FRAMERATE ON";
                    MessageOn = MsgCount;
                } else {
                    FPSOn ^= 1;
                }
            }
            if (menucloc == 20 * 288) {
            savespckey:
                if (spcon != 0) {
                    Msgptr = "SEARCHING FOR SONG START.";
                    MessageOn = MsgCount;
                    copyvid();
                    SPCSave = 1;
                    breakatsignb();
                    SPCSave = 0;
                    savespcdata();

                    curblank = 0x40;
                    Msgptr = spcsaved;
                    MessageOn = MsgCount;
                } else {
                    Msgptr = "SOUND MUST BE ENABLED.";
                    MessageOn = MsgCount;
                }
            }
            if (menucloc == 30 * 288) {
                dumpsound();
                Msgptr = "BUFFER SAVED AS SOUNDDMP.RAW";
                MessageOn = MsgCount;
            }
            if (SPCKeyPressed == 1) {
                SPCKeyPressed = 0;
            } else {
            exitloop:
                GUIUnBuffer();
                copyvid();
            }
        }
        u1* i = pressed;
        u4 n = 256; // XXX maybe should be lengthof(pressed)
        do {
            if (*i == 1)
                *i = 2;
            ++i;
        } while (--n != 0);
        StartSound();
        ForceNonTransp = 0;
        GUIOn = 0;
        Clear2xSaIBuffer();
        if (MenuNoExit != 1)
            break;
        MenuNoExit = 0;
    }
    continueprognokeys();
}
