/*
 * Copyright (c) 2015-2017 Carsten Sonne Larsen  <cs@innolan.dk>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include "clib.h"
#include "lib/aengine.h"
#include "localize/start.h"
#include "main/evaluator.h"
#include "system/window_amiga.h"
#include "system/base/io.h"

#ifdef AMIGA

AmigaWindow::AmigaWindow(const char *prompt, CharValidator *validator) :
    ConsoleBase(prompt)
{
    proc = new AnsiConoleEngine(prompt, validator);
    window = NULL;
    //menu = NULL;
    writereq.st = NULL;
    writeport = NULL;
    readreq.st = NULL;
    readport = NULL;
    succeed = false;
    openconsole = false;
}

AmigaWindow::~AmigaWindow()
{
    Cleanup();
    delete proc;
}

int AmigaWindow::GetStackSize()
{
    return 100000;
}

void AmigaWindow::Create()
{
    NewWindow nw = {
        10, 20,               // left, top
        WWIDTH, WHEIGHT,      // width, height
        (UBYTE)-1, (UBYTE)-1, // detailpen, blockpen
        IDCMP_CLOSEWINDOW |
        IDCMP_MENUPICK,
        WFLG_SIZEGADGET |
        WFLG_DRAGBAR |
        WFLG_DEPTHGADGET |
        WFLG_CLOSEGADGET |
        WFLG_ACTIVATE |
        WFLG_SIMPLE_REFRESH,
        NULL, NULL,           // user gadgets, user checkmark
        (UBYTE*) TXTTITLE,    // title
        NULL, NULL,           // window screen, super bitmap
        400, 75,              // min width, height
        1600, 1050,           // max width, height
        WBENCHSCREEN          // open on workbench screen
    };

    succeed = true;
    succeed = succeed && (writeport = CreatePort((char*)PORTCR, 0)) != 0;
    succeed = succeed && (readport = CreatePort((char*)PORTCW, 0)) != 0;
    succeed = succeed && (writereq.st = CreateExtIO(writeport, sizeof(IOStdReq))) != 0;
    succeed = succeed && (readreq.st = CreateExtIO(readport, sizeof(IOStdReq))) != 0;
    succeed = succeed && (window = OpenWindow(&nw)) != 0;
    if (!succeed) return;

    windowsig = 1 << window->UserPort->mp_SigBit;
    writereq.io->io_Data = (APTR) window;
    writereq.io->io_Length = sizeof(Window);

    openconsole = OpenDevice(DEVCONSOLE, CONU_SNIPMAP, writereq.st, CONFLAG_DEFAULT) == 0;
    succeed = openconsole;
    if (!openconsole) return;

    readreq.st->io_Device = writereq.st->io_Device;
    readreq.st->io_Unit   = writereq.st->io_Unit;

    // Queue input
    ReadChar(readreq);

    // Do GUI stuff
    //menu = new AmigaMenu(window);
    //menu->Attach();
}

void AmigaWindow::Cleanup()
{
    if(openconsole) {
        if (readreq.st) {
            AbortIO(readreq.st);
            WaitIO(readreq.st);
        }

        CloseDevice(writereq.st);
        openconsole = false;
    }

    if(readreq.st) {
        DeleteExtIO(readreq.st);
        readreq.st = NULL;
    }

    if(readport) {
        DeletePort(readport);
        readport = NULL;
    }

    if(writereq.st) {
        DeleteExtIO(writereq.st);
        writereq.st = NULL;
    }

    if(writeport) {
        DeletePort(writeport);
        writeport = NULL;
    }

    //if (menu) {
    //    delete menu;
    //    menu = NULL;
    //}

    if(window) {
        CloseWindow(window);
        window = NULL;
    }
}

void AmigaWindow::Run()
{
    Create();
    StartMessage();
    exit = false;

    while(!exit) {
        Prompt();
        ReadLine();
        Evaluator *evaluator = new Evaluator(line);
        evaluator->Evaluate();
        const char *out = evaluator->GetResult();
        WriteString(out, StrLen(out));
        delete evaluator;
    }

    Cleanup();
}

void AmigaWindow::Exit()
{
    exit = true;
}

void AmigaWindow::ReadLine()
{
    unsigned long conreadsig = 1L << readport->mp_SigBit;
    proc->StartInput();

    while(!proc->InputDone() && !exit)
    {
        unsigned long signals = Wait(conreadsig | windowsig);

        if (signals & conreadsig)
        {
            unsigned char ch = TryGetChar(readport);
            if (ch != 0)
            {
                const char *out = proc->ProcessChar(ch);
                WriteString(out);
            }
        }

        if (signals & windowsig)
        {
            STMessage w;
            while ((w.msg = GetMsg(window->UserPort)))
            {
                switch(w.imsg->Class)
                {
                case IDCMP_CLOSEWINDOW:
                    exit = true;
                    break;
                    //case IDCMP_MENUPICK:
                    //    menu->Process();
                    //    break;
                default:
                    break;
                }

                ReplyMsg(w.msg);
            }
        }
    }

    line = proc->GetLine();
}

void AmigaWindow::WriteChar(const char character)
{
    writereq.io->io_Command = CMD_WRITE;
    writereq.io->io_Data =    (APTR)&character;
    writereq.io->io_Length =  1;
    DoIO(writereq.st);
}

void AmigaWindow::WriteString(const char *string)
{
    writereq.io->io_Command = CMD_WRITE;
    writereq.io->io_Data =    (APTR)string;
    writereq.io->io_Length =  (ULONG)-1;
    DoIO(writereq.st);
}

void AmigaWindow::WriteString(const char *string, unsigned int length)
{
    writereq.io->io_Command = CMD_WRITE;
    writereq.io->io_Data =    (APTR)string;
    writereq.io->io_Length =  (LONG)length;
    DoIO(writereq.st);
}

unsigned char AmigaWindow::ReadChar(STRequest request)
{
    unsigned char result = inputbuf;
    request.io->io_Command = CMD_READ;
    request.io->io_Data =    (APTR)&inputbuf;
    request.io->io_Length =  1;
    SendIO(request.st);
    return result;
}

unsigned char AmigaWindow::TryGetChar(MsgPort *msgport)
{
    STRequest readreq;
    readreq.msg = GetMsg(msgport);
    return readreq.msg == 0 ? '\0' : ReadChar(readreq);
}

void AmigaWindow::SetPrompt(const char* string)
{
    ConsoleBase::SetPrompt(string);
    proc->SetPrompt(string);
}

#endif



//http://amigadev.elowar.com/read/ADCD_2.1/Libraries_Manual_guide/node046C.html#line6

//#include <devices/keymap.h>
//
//BOOL AskKeyMap(struct IOStdReq *request, struct KeyMap *keymap)
//{
//   request->io_Command = CD_ASKKEYMAP;
//    request->io_Length = sizeof(struct KeyMap);
//    request->io_Data = (APTR)keymap;  /* where to put it */
//    DoIO(request);
//    if(request->io_Error) return(FALSE);
//    else  return(TRUE); /* if no error, it worked. */
//}

//BOOL SetKeyMap(struct IOStdReq *request,struct KeyMap *keymap)
//{
//    request->io_Command = CD_SETKEYMAP;
//    request->io_Length = sizeof(struct KeyMap);
//    request->io_Data = (APTR)keymap;      /* where to get it */
//    DoIO(request);
//    if(request->io_Error) return(FALSE);
//    else  return(TRUE);     /* if no error, it worked. */
//}
