#include <string.h>
#include "vt_hal.hpp"

VTerminalBaseDevice::VTerminalBaseDevice(io_t* io)
    //: inherited()
    //, HAL_Device()
{
    init(io);
}

VTerminalBaseDevice::VTerminalBaseDevice(const_string name, io_t* io)
    //: inherited()
    //, HAL_Device(name)
{
    (void)name;
    init(io);
}

void VTerminalBaseDevice::init(io_t* io){
    _io = io;
    reset();
}

void VTerminalBaseDevice::reset(){
    st = ssCHAR;
}

int VTerminalBaseDevice::get_char ()
{
    using namespace cli;

    static const char  CSIMark[] = "\x1b[";    ///<Управляющий символ состоит из трех байт, где третий и определяет символ
    static const char  CSI4Mark[] ="\x1b[4";    ///<Управляющий символ состоит из трех байт, где третий и определяет символ
    static const char  CSOMark[] = "\x1bO";    ///<Управляющий символ состоит из трех байт, где третий и определяет символ

    char ch;

    switch (st){
    case ssNOCS1:
        st = ssCHAR;
        return peekchar;

    case ssNOCS2:
        if (peeks == nullptr){
            st = ssCHAR;
            return peekchar;
        }
        ch = *peeks++;
        return ch;

    default: break;
    }

    int c = -1;

    while (c < 0) {
    c = io()->get_char ();
    if (c < 0)
        return -1;

    ch = (char)c;

    switch (st){
    case ssCHAR:
        if (ch != keyESC)
            return c;
        st = ssESC;
        c = -1;
        break;

    case ssESC:
        if (ch == CSIMark[1]){
            st = ssCSI;
            c = -1;
            break;
        }
        else if (ch == CSOMark[1] ) {
                st = ssCSO;
                c = -1;
                break;
        }
        else {
            peekchar = ch;
            st = ssNOCS1;
            return CSIMark[0];
        }

    case ssCSI:
        switch( ch) {
        case 'A':
        case 'B':
        case 'C':
        case 'D':
        case 'F':
        case 'H': return accepted_state(ch | kmSCI);

        case '4':
            // TODO for better supprt functions, need implement parsing esc[d*,d*F  format, 
            //   with saving d*,d*, and function check by F
            st = ssCSI4FUN;
            c = -1;
            break;

        default: return rejected_state_pass(CSIMark, ch);
        };
        break;

    case ssCSI4FUN:
        switch (ch){
        case 'l': return accepted_state(modeOVR);
        case 'h': return accepted_state(modeINS);
        default: return rejected_state_pass(CSI4Mark, ch);
        };
            

    case ssCSO:
        // arrow keys on keypad
        switch( ch) {
        case 'A':
        case 'B':
        case 'C':
        case 'D':
            st = ssCHAR;
            return ch | kmSCI;

        default: return rejected_state_pass(CSOMark, ch);
        };
        

    default:
        // on any error just reset
        return accepted_state(ch);
    };

    } // while true;

    // impossibl ?
    return -2;
}

int VTerminalBaseDevice::rejected_state_pass( const char* collected, char ch){
    peekchar = ch;
    peeks = collected + 1;
    st = ssNOCS2;
    return collected[0];
}

int VTerminalBaseDevice::accepted_state( int ch){
    st = ssCHAR;
    return ch;
}

#include <stdio.h>
#include <stdlib.h>

void VTerminalBaseDevice::send_cursor_pos(unsigned x, unsigned y){
    char tmp[16];
    int len = snprintf(tmp, sizeof(tmp) ,"\x1b[%d;%dH", x,y);
    io()->postData(tmp, len);
}

void VTerminalBaseDevice::send_cursor_move(unsigned x, cli::KeyCode dir){
    char tmp[16];
    int len = snprintf(tmp, sizeof(tmp) ,"\x1b[%d%c", x, (char)dir);
    io()->postData(tmp, len);
}


void VTerminalBaseDevice::send_cursor_lf(unsigned x){
    send_cursor_move(x, cli::keyL);
}

void VTerminalBaseDevice::send_cursor_rt(unsigned x){
    send_cursor_move(x, cli::keyR);
}

void VTerminalBaseDevice::send_csi(char code){
    char tmp[3] = "\x1b[";    //<Управляющий символ состоит из трех байт, где третий и определяет символ
    tmp[2] = code;
    io()->postData(tmp, sizeof(tmp));
}

void VTerminalBaseDevice::send_csi(char code, char mode){
    char tmp[4] = "\x1b[";    //<Управляющий символ состоит из трех байт, где третий и определяет символ
    tmp[2] = mode;
    tmp[3] = code;
    io()->postData(tmp, sizeof(tmp));
}

void VTerminalBaseDevice::send_cursor_save(){
    io()->postData("\x1bs", 2);
}

void VTerminalBaseDevice::send_cursor_restore(){
    io()->postData("\x1bu", 2);
}

enum EraseModeID{
    eraseAFTER=0, //from cursor to end
    erasePRE = 1, //before cursor
    eraseALL = 2, //all screen/line
};
void VTerminalBaseDevice::send_erase_line( EraseModeID how){
    send_csi('K', how);
}

void VTerminalBaseDevice::send_erase_screen( EraseModeID how){
    send_csi('J', how);
}




// @return - amount of availiable data.
int VTerminalBaseDevice::get_wait(unsigned to){
    return io()->get_wait(to);
}

/*
 * Telnet stream interface is implemented as a proxy to tcp-stream.
 */
int VTerminalBaseDevice::putChar (int c)
{
    return io()->putChar (c);
}

//*  неблокирующая печать
int VTerminalBaseDevice::postData ( const void* src, unsigned len){
    return io()->postData( src, len );
}

//* блокирующая печать
int VTerminalBaseDevice::puts( const char* str){
    return putData(str, strlen(str) );
}

//*  \return - длинна отправленного участка
int VTerminalBaseDevice::putData ( const void* src, unsigned len){
    return io()->putData( src, len );
}


//*  ожидание доступности печати
//*  \return - количество байт возможных для неблокирующей печати
int VTerminalBaseDevice::put_wait(unsigned to){
    return io()->put_wait(to);
}

//*  почти тоже put_wait, ждет полного опустошения
int VTerminalBaseDevice::put_flush(unsigned to){
    return io()->put_flush(to);
}

//*  очищает буфер, прерывая текущую отправку
int VTerminalBaseDevice::put_drop(){
    return io()->put_drop();
}


//* монополизация вывода (puts, putData предпочтительно использую ее )
//* \arg onoff - захват/освобождение
//* \return    - состояние захвачн ли вывод
bool VTerminalBaseDevice::put_access(bool onoff, unsigned to){
    return io()->put_access(onoff, to);
}

