// This is the main module of a terminal emulator program
// to replace PCPLUS for in-house test applications.
// compiler: Borland 3.1, DOS real mode
// author: Timothy Fox

#include <string.h>
#include <conio.h>
#include <stdlib.h>
#include <dir.h>
#include <dos.h>

#include "typedefs.h"
#include "abcconst.h"
#include "commutil.h"
#include "abcscrn.h"
#include "abckbd.h"
#include "abctabs.h"
#include "tsio.h"
#include "dostimer.h"
#include "flashdta.h"
#include "jultime.h"
#include "mmx_info.h"

#pragma option -r-

static BOOL DONE = FALSE, hooked = FALSE;

void  end_program(void)
{
    // TO DO: put up a dialog window
    // Ask the user to confirm the EXIT command
    DONE = TRUE;
}

static
void  pause(UINT ticks)
{
    run_timer(pause_timer, ticks);
    while (!timer_done(pause_timer)) { }
}

static int mm_done;

static
void fkeyproc(void)
{
    switch (getch()) {
        case 0x3b:  // F1 - help
            cputs("\n");
            cputs("Modem Manager commands:\n");
            cputs("  F1 - Help\n");
            cputs("  F2 - Copy non-volatile memory to disk\n");
            cputs("  F3 - Copy disk to non-volatile memory\n");
            cputs("  F4 - Copy flash memory to disk\n");
            cputs("  F5 - Copy disk to flash memory\n");
            cputs("\n");
            break;
        
        case 0x3c:  // F2 - copy non-volatile memory to disk
            read_nvram_data();
            if (nvram_loaded()) {
                if ((fp = fopen("NVRAM.DAT", "wb")) == NULL) {
                    cputs("Failed to open NVRAM.DAT\n");
                }
                else {
                    cputs("Write non-volatile memory to disk\n");
                    fwrite(nvram_data, 0x1800, 1, fp);
                    fclose(fp);
                    cputs("Ready\n");
                }
            }
            break;
        
        case 0x3d:  // F3 - Copy disk to non-volatile memory
            cputs("Read non-volatile data from disk\n");
            if ((fp = fopen("NVRAM.DAT", "rb")) == NULL) {
                cputs("Failed to open NVRAM.DAT\n");
            }
            else {
                load_nv_file(); 
                write_nvram_data();
            }
            break;
        
        case 0x3e:  // F4 - Copy flash memory to disk
            recv_flash_data();
            if (flash_loaded()) {
                save_flash_file();
            }
            break;
        case 0x3f:  // F5 - Copy disk to flash memory
            load_flash_file(current_ID(), current_image_type());
            if (flash_loaded()) {
                xmit_flash_data();
            }
            break;
    }
}

static
void  keyproc(void)
{ 
    if (0 == kbhit()) return;
    switch (getch()) {
        case 0:         // function keys
            fkeyproc();
            break;
        case ESC:
            mm_done = 1;
            break;
        default:
            COM_OUT(c);
    }
}

static
void process_mm (void) {
#define MAXSHUFFLE 16
#define IDLE_LIMIT 30  // seconds
    ULONG jul_start, jul_now;
    
    mm_done = 0;
    
    send_command("READY\r","",0);
    jul_start = julian_now();
    while (!mm_done) {
        jul_now = julian_now();
        if ((jul_now - jul_start) > IDLE_LIMIT) {
            cputs("\nController idle time exceeded. Abandoning update...");
            return;
        }
        if (_shuffle())
            jul_start = julian_now();
      
        keyproc();
      
        // "\x1B[" is the ANSI terminal escape marker 
        if (shuffle_contains("\x1b[")) 
            return;
      
        if (shuffle_contains("DEFINE_BIN\r")) {
            recv_flash_data() ;
            jul_start = julian_now();
        }
        if (shuffle_contains("DEFINE_ID\r")) {
            recv_device_ID() ;
            jul_start = julian_now();
        }
    } // while !mm_done
    return;
}

int main (int _argc, char *_argv[])
{ UINT portx, rxc;
    
    char drive[MAXDRIVE];
    char dir[MAXDIR];
    char file[MAXFILE];
    char ext[MAXEXT];

    if (_argc > 1) {
        portx = atoi(_argv[1]);
        if ((portx < 1) || (portx > 2)) {
            cputs("\r\n The COM port you specify must be either '1' or '2'.");
            cputs("\r\n You asked for port '");
            cputs(_argv[1]);
            cputs("'.\n");
            return 0;
        }
    }
    else {
        fnsplit(_argv[0],drive,dir,file,ext);
        cputs("\r\nWhen you start this program, you must specify");
        cputs("\r\nthe communication port ('1' or '2') to use.");
        cputs("\r\n\nFor example,   ");
        cputs(file);
        cputs(" 1");
        cputs("\r\n\nconnects to the Test Rack via COM1: \n");
        return 0;
    }
    portx--;
    setup_comport(irqnumbr[portx], baseaddr[portx]); 
    set_COMPARMS(0x03); // "8N1"
    SET_BAUD_RATE(12); // 9600 baud
    
    INSTALL_RTC_ISR();
    hooked = TRUE;
    setup_comport(irqnumbr[portx], baseaddr[portx]);
    pause(10);
    teardown_comport();
    pause(10);
    setup_comport(irqnumbr[portx], baseaddr[portx]);
    
    init_vt_scrn();
    refresh_ANSI_stats();
    textattr((BLUE << 4) | LIGHTGRAY);
    erase_all();
    while (host_rx_count())
        COM_IN(); // eat any burst of chars from startup
    cputs("\nABC Terminal ready ");
    if (!DSR_STATUS())
        cputs("\r\n\n waiting for DSR ... ");
    do {
        if (host_rx_count())
            break;
        keyboard();
        if (DONE) break;
    } while (!DSR_STATUS());
    cputs("\r\n\n");
    
    do {
        if (host_rx_count()) {
            rxc = (COM_IN());
            if (rxc == ENQ) {
                COM_OUT(ACK);
                // clear the screen, and wait for rx activity to stop
                clrscr();
                run_timer(COM_timer, 50);
                while (!timer_done(COM_timer)) ;
                cputs("\r\n\n Update server active ...\r\n");
                if (setup_flashbuf()) {
                    process_mm();
                }
                // wait for tx queue to be empty
                while (host_tx_count()) ;
                run_timer(COM_timer, 30);
                while (!timer_done(COM_timer)) ;
            }
            else {
              handle_rx(rxc);
            }
        }
        keyboard();
      
    } while (!DONE);
    clrscr();
    return 0;
}

#ifdef __cplusplus
    #define __CPPARGS ...
#else
    #define __CPPARGS
#endif

#define CTRL_C  0x23

void interrupt (* old_ctrl_C) (__CPPARGS);

void  leave_abc(void)
{
    setvect(CTRL_C, old_ctrl_C);
    if (hooked) {
        teardown_comport();
        REMOVE_RTC_ISR();
        hooked = FALSE;
    }
}

void interrupt ctrl_C(__CPPARGS)
{
    leave_abc();      // restore all the hooked interrupts
    clrscr();
    _exit(1);
}

void  init_abc(void)
{
    old_ctrl_C = getvect(CTRL_C); // save the old ctrl-C vector
    setvect(CTRL_C, ctrl_C);
}

#pragma startup init_abc

#pragma exit leave_abc