#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/time.h>
#include <curses.h>
#include <keycodes.h>
#include "mfjterm.h"
#include "init.h"
#include "fkeys.h"
#include "scrollback.h"
#include "yapp.h"
#include "inout.h"
#include "rs232.h"
#include "misc.h"
#include "opmode.h"
#include "cq.h"
#include "qso.h"
#include "file.h"
#include "timefuncs.h"


#define TAB 9
#define ESC 27

extern char nofifo;


unsigned char pc2latin[128] =
{
199, 252, 233, 226, 228, 224, 229, 231, 234, 235, 232, 239, 238, 236, 196, 197,
201, 230, 198, 244, 246, 242, 251, 249, 255, 214, 220, 162, 163, 165, 0, 0,
225, 237, 243, 250, 241, 209, 170, 186, 191, 0, 172, 189, 188, 161, 171, 187,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 223, 0, 0, 0, 0, 181, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 177, 0, 0, 0, 0, 247, 0, 176, 0, 183, 0, 0, 178, 0, 160
};


unsigned char latin2pc[128] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
255, 173, 155, 156, 254, 157, 124, 21, 254, 254, 166, 174, 170, 45, 254, 254,
248, 241, 253, 254, 254, 230, 20, 249, 254, 254, 167, 175, 172, 171, 254, 168,
254, 254, 254, 254, 142, 143, 146, 128, 254, 144, 254, 254, 254, 254, 254, 254,
254, 165, 254, 254, 254, 254, 153, 254, 232, 254, 254, 254, 154, 254, 254, 225,
133, 160, 131, 254, 132, 134, 145, 135, 138, 130, 136, 137, 141, 161, 140, 139,
254, 164, 149, 162, 147, 254, 148, 246, 237, 151, 163, 150, 129, 254, 254, 152
};


char tab_move[80] = {
       8,7,6,5,4,3,2,1,
       8,7,6,5,4,3,2,1,
       8,7,6,5,4,3,2,1,
       8,7,6,5,4,3,2,1,
       8,7,6,5,4,3,2,1,
       8,7,6,5,4,3,2,1,
       8,7,6,5,4,3,2,1,
       8,7,6,5,4,3,2,1,
       8,7,6,5,4,3,2,1,
       7,6,5,4,3,2,1,0
};


void toggle_translate(void)
{
       static char *translate_state[2] = {"Off", "On"};
       char tmp[80];

       pc_translate ^= 1;

       curs_set(0);
       attrset(A_REVERSE);
       move(19, 1);
       sprintf(tmp, "PC <-> Latin1 character translation now %s", translate_state[pc_translate]);
       addstr(tmp);
       refresh();
       sleep(1);
       move(19, 1);
       addstr("                                             ");
       move(tnc_y, tnc_x);
       curs_set(1);
       refresh();
}


void toggle_bell(void)
{
       static char *translate_state[2] = {"Off", "On"};
       char tmp[80];

       bell ^= 1;

       curs_set(0);
       attrset(A_REVERSE);
       move(19, 1);
       sprintf(tmp, "Bell now %s", translate_state[bell]);
       addstr(tmp);
       refresh();
       sleep(1);
       move(19, 1);
       addstr("                             ");
       move(tnc_y, tnc_x);
       curs_set(1);
       refresh();
}



void read_key(void)
{
       int key;

       key = getch();

       if (key < KEY_MIN) {
               if (key == ESC) {
                       key = getch();
                       switch (key) {
                               case 'a':
                                       set_bps();
                                       break;
                               case 'b':
                                       scrollback();
                                       break;
                               case 'c':
                                       conn();
                                       break;
                               case 'd':
                                       send_daytime();
                                       break;
                               case 'e':
                                       invoke_editor();
                                       break;
                               case 'f':
                                       if ((opmode == AMTOR) != (opmode == PACTOR))
                                               switch_fec(fec ^= 1);
                                       break;
                               case 'g':
                                       grab_callsign();
                                       break;
                               case 'h':
                                       show_help();
                                       break;
                               case 'k':
                                       set_my_callsign();
                                       break;
                               case 'o':
                                       change_opmode();
                                       break;
                               case 'p':
                                       toggle_translate();
                                       break;
                               case 'q':
                                       send_cq();
                                       break;
                               case 'r':
                                       send_ry();
                                       break;
                               case 's':
                                       save_settings();
                                       break;
                               case 't':
                                       turn_it_over();
                                       break;
                               case 'u':
                                       if (opmode == RTTY)
                                               set_usos(usos ^= 1);
                                       break;
                               case 'w':
                                       save_sb_buf();
                                       break;
                               case 'x':
                                       quit(0);
                                       break;
                               case 'y':
                                       set_scancrit();
                                       break;
                               case 'z':
                                       get_call();
                                       break;
                               default:
                                       curs_set(0);
                                       move(19, 1);
                                       attrset(A_REVERSE);
                                       addstr("Illegal command");
                                       refresh();
                                       sleep(2);
                                       move(19, 1);
                                       addstr("               ");
                                       attrset(A_NORMAL);
                                       move(tnc_y, tnc_x);
                                       curs_set(1);
                                       refresh();
                       }

               } else {
                       switch(key) {
                               case K_CTL_B:
                                       toggle_bell();
                                       break;
                               case K_CTL_X:
                                       quit(1);
                                       break;
                               case K_CTL_Y:
                                       nodelay(stdscr, FALSE);
                                       key = getch();
                                       nodelay(stdscr, TRUE);

                                       switch(key) {
                                               case KEY_PPAGE:
                                                       yapp(YAPP_UL);
                                                       break;
                                               case KEY_NPAGE:
                                                       yapp(YAPP_DL);
                                                       break;
                                       }
                                       break;
                               case K_CTL_F:
                                       fkeys();
                                       break;
                               case K_CTL_G:
                                       bbs_capture();
                                       break;
                               default:
                                       write_tnc((unsigned char)key);
                       }
               }
       } else {
               if ((key >= KEY_F(1)) && key <= KEY_F(10))
                       out_fk(key - KEY_F(1));
               else {
                       switch(key) {
                               case KEY_BACKSPACE:
                                       write_tnc(K_BACKSPACE);
                                       break;
                               case KEY_PPAGE:
                                       send_file();
                                       break;
                               case KEY_NPAGE:
                                       capture();
                                       break;
                               case KEY_IC:
                                       insert_call_sequence(1);
                                       break;
                               case KEY_LEFT:
                               case KEY_RIGHT:
                                       set_spd(key);
                                       break;
                               case KEY_DOWN:
                                       set_inv();
                                       break;
                               case KEY_UP:
                                       set_shift();
                                       break;
                               case KEY_END:
                                       end_qso();
                                       break;
                       }
               }
       }
}


int chk_incoming(void)
{
       struct timeval timeout;

       FD_ZERO(&io_set);
       FD_SET(tnc, &io_set);
       timeout.tv_sec = 0;
       timeout.tv_usec = 0;
       return select(tnc + 1, &io_set, NULL, NULL, &timeout);
}


void send_string(unsigned char *s, int lfeed)
{
       int i;

       for (i = 0; i < strlen(s); i++) {
               if (chk_incoming())
                       read_tnc();
               write_tnc(s[i]);
               zleep(1);
       }

       if (lfeed) {
               if (chk_incoming())
                       read_tnc();
               write_tnc(10);
       }
}


void waitfor_string(unsigned char *s)
{
       fd_set io_set;
       struct timeval timeout;

       w_cmpstr = (s[0] << 24) + (s[1] << 16) + (s[2] << 8) + (s[3]);

       w_instr = 0;
       w_found = FALSE;
       wait_string = TRUE;

       while (! w_found) {
               FD_ZERO(&io_set);
               FD_SET(0, &io_set);
               FD_SET(tnc, &io_set);
               timeout.tv_sec = 0;
               timeout.tv_usec = 400000;

               if (select(tnc+1, &io_set, NULL, NULL, &timeout) > 0) {
                       if (FD_ISSET(tnc, &io_set))
                               read_tnc();
                       if (FD_ISSET(0, &io_set))
                               break;
               }
       }
}



void write_tnc(unsigned char c)
{
       setscrreg(20, 24);

       attrset(A_NORMAL);
       move(console_y, console_x);

       if (c < 0x20) {
               switch(c) {
                       case K_CTL_C:
                       case K_CTL_T:
                       case K_CTL_R:
                       case K_CTL_Y:
                               goto tnc_only;
               }
       }

       if (c == 13) c = K_LINEFEED;
       addch(c);

       switch(c) {
               case K_LINEFEED:
                       console_x = 0;
                       console_y++;
                       c = 13;
                       break;
               case K_BACKSPACE:
                       if (console_x > 0) {
                               console_x--;
                               move(console_y, console_x);
                               addch(' ');
                       }
                       break;
               case K_TAB:
                       console_x += tab_move[console_x];
                       break;
               default:
                       if ((++console_x) == 80) {
                               addch(K_LINEFEED);
                               console_x = 0;
                               console_y++;
                       }
       }

       if (console_y > 24)
               console_y = 24;

       move(console_y, console_x);

tnc_only:
       if ((c >= 128) && (pc_translate)) {
               c = latin2pc[c - 128];
               if (c == 0)
                       c = ' ';
       }
       write(tnc, &c, 1);
       refresh();
}


void new_bufline(void)
{
       sb_bufstr[strno][strptr] = 0;

       if (++strno > 808)
               strno = 0;

       strptr = 0;

       memset(sb_bufstr[strno], 0, 81);

       if (lines_in_buffer < 808)
               lines_in_buffer++;
}


void newline(void)
{
       addch(K_LINEFEED);
       new_bufline();
       tnc_x = 0;
       tnc_y++;
}


void read_tnc(void)
{
       static unsigned char cbuf[128];
       unsigned char c;
       static char old_c = 0xff, cmdflag = FALSE;
       int i, n;

       n = read(tnc, cbuf, 128);
       setscrreg(1, 18);

       attrset(A_NORMAL);
       move(tnc_y, tnc_x);

       for (i = 0; i < n; i++) {
               if (cmdflag) {
                       cmdflag = FALSE;
                       newline();
                       if (tnc_y > 18) tnc_y = 18;
               }

               c = cbuf[i];

               if ((c >= 128) && (pc_translate)) {
                       c = pc2latin[c - 128];
                       if (c == 0)
                               c = ' ';
               }

               if (cap)
                       fputc((char)c, capf);

               if (bbs_cap && bbs_capf_open) {
                       fputc((char)c, bbs_capf);
                       if ((c == 10) && (old_c == 10)) {
                               close_bbs_capf();
                               old_c = 0xff;
                       } else
                               old_c = c;
               }

               if (c >= 0x20) {
                       if ((c != 127) && (c != 128+27)) {
                               addch(c);
                               sb_bufstr[strno][strptr++] = c;
                               if ((++tnc_x) == 80)
                                       newline();
                       }
               } else {
                       switch(c) {
                               case K_CTL_G:           /* Ctrl-G, Bell */
                                       if(bell) beep();
                                       break;
                               case K_LINEFEED:
                                       newline();
                                       break;
                               case K_TAB:
                                       /* addch(c); */   /* buggy */
                                       tnc_x += tab_move[tnc_x];
                                       memset(&sb_bufstr[strno][strptr], 0x20,
                                               tab_move[strptr]);
                                       strptr += tab_move[strptr];
                                       break;
                               case K_BACKSPACE:
                                       if (tnc_x > 0) {
                                               tnc_x--;
                                               move(tnc_y, tnc_x);
                                               addch(' ');
                                       }
                                       if (--strptr == -1)
                                               strptr = 0;
                                       break;
                       }
               }

               if (tnc_y > 18)
                       tnc_y = 18;

               move(tnc_y, tnc_x);

               cmdstr <<= 8;
               cmdstr += (int)c;
               if (cmdstr == 0x636D643A)       /* Check for 'cmd:' string */
                       cmdflag = TRUE;

               if (scan || cs_grab || bbs_cap) {
                       strcpy(scanstr,&scanstr[1]);
                       scanstr[29] = c;

                       if (! cs_grab) {
                               if (scan) {
                                       if (strstr(scanstr,scancrit))
                                               sound_alarm();
                               } else if (! bbs_capf_open) {
                                       char *p;

                                       if ((p = strstr(scanstr, HOME_BBS">FBB")) &&
                                               (strrchr(scanstr, '\012') ==
                                               scanstr + strlen(scanstr) - 1))
                                               open_bbs_capf(FBB);
                                       else if ((p = strstr(scanstr, HOME_BBS">MAIL")) &&
                                               (strrchr(scanstr, '\012') ==
                                               scanstr + strlen(scanstr) - 1))
                                               open_bbs_capf(MAIL);
                               }
                       }
               }


               if (wait_string) {
                       w_instr <<= 8;
                       w_instr += c;
                       if (w_instr == w_cmpstr) {
                               w_found = TRUE;
                               wait_string = FALSE;
                       }
               }
               refresh();
       }
}