#include "std.h"

#define FOREGROUND_BLACK        0
#define FOREGROUND_CYAN         FOREGROUND_BLUE | FOREGROUND_GREEN
#define FOREGROUND_MAGENTA      FOREGROUND_BLUE | FOREGROUND_RED
#define FOREGROUND_YELLOW       FOREGROUND_GREEN | FOREGROUND_RED
#define FOREGROUND_WHITE        FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED

#define BACKGROUND_BLACK        0
#define BACKGROUND_CYAN         BACKGROUND_BLUE | BACKGROUND_GREEN
#define BACKGROUND_MAGENTA      BACKGROUND_BLUE | BACKGROUND_RED
#define BACKGROUND_YELLOW       BACKGROUND_GREEN | BACKGROUND_RED
#define BACKGROUND_WHITE        BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED

#define TERMINAL_ROWS           25
#define TERMINAL_COLUMNS        80

HANDLE termpCreateTerminalBuffer();
PTERMINAL termpCreateTerminal();
void termpDestroyTerminal(PTERMINAL pTerminal);


PTERMINAL termpCreateTerminal()
{
    PTERMINAL pResult;

    pResult = malloc(
        sizeof(TERMINAL));

    if (pResult!=NULL)
    {
        ZeroMemory(
            pResult,
            sizeof(TERMINAL));
        pResult->hInput = INVALID_HANDLE_VALUE;
        pResult->hOutput = INVALID_HANDLE_VALUE;
        pResult->hSavedBuffer = INVALID_HANDLE_VALUE;
        pResult->hNewBuffer = INVALID_HANDLE_VALUE;
        pResult->wAttributes = FOREGROUND_WHITE;
        pResult->fInverse = FALSE;
        pResult->fBold = FALSE;
        pResult->fEscapeValid = FALSE;
        pResult->fEscapeInvalid = TRUE;
        pResult->wEscapeParamCount = 0;
        pResult->chEscapeCommand = 0;
        pResult->chEscapeFirstChar = 0;
        pResult->pTxProc = NULL;
    }
    else
    {
        SetLastError(
            ERROR_NOT_ENOUGH_MEMORY);
    }

    return pResult;
}


void termpDestroyTerminal(PTERMINAL pTerminal)
{
    if (pTerminal!=NULL)
    {
        free(pTerminal);
    }
}


PTERMINAL termInitialize(TERMTXPROC pTxProc)
{
    PTERMINAL pResult;
    BOOL      fResult;

    pResult = termpCreateTerminal();

    if (pResult==NULL)
    {
        goto Error;
    }

    pResult->hSavedBuffer = GetStdHandle(
        STD_OUTPUT_HANDLE);

    if (pResult->hSavedBuffer==INVALID_HANDLE_VALUE)
    {
        goto Error;
    }

    pResult->hNewBuffer = termpCreateTerminalBuffer();

    if (pResult->hNewBuffer==INVALID_HANDLE_VALUE)
    {
        goto Error;
    }

    fResult = SetConsoleActiveScreenBuffer(
        pResult->hNewBuffer);

    if (!fResult)
    {
        goto Error;
    }

    return pResult;

Error:
    termFinalize(pResult);
    return NULL;
}



void termFinalize(PTERMINAL pTerminal)
{
    if (pTerminal!=NULL)
    {
        if (pTerminal->hSavedBuffer!=INVALID_HANDLE_VALUE)
        {
            SetConsoleActiveScreenBuffer(
                pTerminal->hSavedBuffer);
        }

        if (pTerminal->hNewBuffer!=INVALID_HANDLE_VALUE)
        {
            CloseHandle(
                pTerminal->hNewBuffer);
        }

        free(pTerminal);
    }
}


HANDLE termpCreateTerminalBuffer()
{
    HANDLE hNewBuffer;
    BOOL bResult;
    COORD Coord;

    hNewBuffer = CreateConsoleScreenBuffer(
        GENERIC_READ | GENERIC_WRITE,
        0,                              // No sharing allowed
        NULL,                           // No inheritance allowed
        CONSOLE_TEXTMODE_BUFFER,        // The only supported value
        NULL);                          // Reserved, must be null

    if (hNewBuffer==INVALID_HANDLE_VALUE)
    {
        goto Error;
    }

    Coord.Y = TERMINAL_ROWS;
    Coord.X = TERMINAL_COLUMNS;

    bResult = SetConsoleScreenBufferSize(
        hNewBuffer,
        Coord);

    if (!bResult)
    {
        goto Error;
    }

    bResult = SetConsoleTextAttribute(
        hNewBuffer,
        FOREGROUND_WHITE);

    if (!bResult)
    {
        goto Error;
    }

    Coord.X = 0;
    Coord.Y = 0;

    bResult = SetConsoleCursorPosition(
        hNewBuffer,
        Coord);

    if (!bResult)
    {
        goto Error;
    }

    return hNewBuffer;

Error:
    if (hNewBuffer!=INVALID_HANDLE_VALUE)
    {
        CloseHandle(hNewBuffer);
    }

    return INVALID_HANDLE_VALUE;
}