// C++ code Copyright (C) David R. Evans G4AMJ/NQ0I

#include <config.h>
#include <sm_vga_mode.h>
#include <surf_vga.h>

#include <conio.h>
#include <direct.h>
#include <dos.h>
#include <graphics.h>

vga_mode* vmode;

#include <stdlib.h>

const boolean CENTERED = true;

const int COMMAND_COLOUR = YELLOW;

// constructor
vga_mode::vga_mode(const float rev) : Record(false), 
                                      Record_file(0),
                                      Play(false), 
                                      Play_file(0),
                                      Tmpdir(0)
{ Version = rev;
  _initialise();
  _generate_command_list();
  Screen_device = &vga_screen;
  Pad = Screen_device; 
  Duration = 15;

// place the information from config.h into the environment
  Tmpdir = new char [5 + strlen(TMPDIR)];
  strcpy(Tmpdir, "TMP=");
  strcat(Tmpdir, TMPDIR);
  putenv(Tmpdir);
}

// destructor
vga_mode::~vga_mode(void)
{ if (Record_file) then
    fclose(Record_file);
  if (Play_file) then
    fclose(Play_file);
  destroy(Tmpdir);
}

// generate the list of known commands
void vga_mode::_generate_command_list(void)
{ known_commands += "?";
  dispatch_functions += &vga_mode::_query;
  known_commands += "CD";
  dispatch_functions += &vga_mode::_cd;
  known_commands += "DIR";
  dispatch_functions += &vga_mode::_dir;
  known_commands += "DRIVE";
  dispatch_functions += &vga_mode::_drive;
  known_commands += "EXIT";
  dispatch_functions += &vga_mode::_exit;
  known_commands += "FIRST";
  dispatch_functions += &vga_mode::_first;
  known_commands += "HELP";
  dispatch_functions += &vga_mode::_query;
  known_commands += "LAST";
  dispatch_functions += &vga_mode::_last;
  known_commands += "NEXT";
  dispatch_functions += &vga_mode::_next;
  known_commands += "OPEN";
  dispatch_functions += &vga_mode::_Open;
  known_commands += "PLOT";
  dispatch_functions += &vga_mode::_plot;
  known_commands += "PRINT";
  dispatch_functions += &batch_mode::_print;
  known_commands += "PWD";
  dispatch_functions += &vga_mode::_pwd;
  known_commands += "QUIT";                 // synonym for "EXIT"
  dispatch_functions += &vga_mode::_exit;
  known_commands += "RECORD";
  dispatch_functions += &vga_mode::_record;
  known_commands += "REPLOT";
  dispatch_functions += &vga_mode::_replot;
  known_commands += "SET";
  dispatch_functions += &vga_mode::_set; 
  known_commands += "SHOW";
  dispatch_functions += &vga_mode::_show; 
  known_commands += "VERBOSE";
  dispatch_functions += &vga_mode::_verbose_toggle; 
  known_commands += "VERSION";
  dispatch_functions += &vga_mode::_version; 
}

// ?
int vga_mode::_query(DREstring&, DREstring&)
{ DREstring command_list;
  for (int n = 1; n <= known_commands.length(); n++)
  { command_list += (const char*)(known_commands[n]);
    command_list += "\r\n";
  }
  _display_text(command_list, (DREstring)"Available commands are:-");
  return 1;
}

// cd
int vga_mode::_cd(DREstring& c, DREstring& dir)
{ if (!(dir.length())) then
    return _pwd(c, dir);
  if (chdir(dir)) then
    error((DREstring)"Unable to change to directory " + dir);
  else
    display_status((DREstring)"Directory now : " + dir);
  return 1;
}

// dir
int vga_mode::_dir(DREstring&, DREstring& tplate)
{ if (!(tplate.length())) then
    tplate = "*.*";
  struct ffblk file_info;
  int status = findfirst(tplate, &file_info, FA_DIREC);
  if (status) then
    display_status((DREstring)"No files found");
  else
  { DREstring file_list;
    file_list = right_fill(file_info.ff_name, 15);
    if (file_info.ff_attrib & FA_DIREC) then
      file_list += "D"; 
    file_list += "\r\n";
    while (!(findnext(&file_info)))
    { file_list += right_fill(file_info.ff_name, 15);
      if (file_info.ff_attrib & FA_DIREC) then
        file_list += "D"; 
      file_list += "\r\n";
    }
    _display_text(file_list, (DREstring)"Matching files are:-");
  }
  return 1;
}

// drive
int vga_mode::_drive(DREstring&, DREstring& d)
{ if (d.length()) then
  { d.toupper();
    int drive_nr = (int)(d.chr(1)) - (int)'A' + 1;
    if (_chdrive(drive_nr)) then
      error((DREstring)"Cannot change to drive " + (DREstring)d.chr(1));
  }
  else                           // which drive?
  { display_status((DREstring)"Current drive is : " +
                   (DREstring)(char)((getdisk()) + (int)'A'));
  }
  return 1;
}

// last
int vga_mode::_last(DREstring&, DREstring&)
{ operating_mode::last();
  return 1;
}

// play
int vga_mode::_play(DREstring&, DREstring& p)
{ if (Record) then
  { error((DREstring)"Cannot PLAY when in RECORD mode");
    return 0;
  }
  if (Play) then
    return 0;
  Play_file = fopen(p, "rt");
  if (!Play_file) then
  { error((DREstring)"Unable to open playback file: " + p);
    return 0;
  }
  Play = true;
  return 1;
}

// pwd
int vga_mode::_pwd(DREstring&, DREstring&)
{ char dname[MAXDIR];
  getcurdir(0, dname);
  display_status((DREstring)"Current directory is : " +
                 (DREstring)(char)((getdisk()) + (int)'A') + (DREstring)":\\" +
                 (DREstring)dname);
  return 1;
}

// record
int vga_mode::_record(DREstring&, DREstring& p)
{ if (p == (DREstring)"OFF") then
  { if (Record_file) then
      fclose(Record_file);
    Record = false;
    return 1;
  }
  if (Record) then		// already recording
    return 1;
  Record = false;
  if (!p.length()) then
    p = "MIDAS.LOG";
  if (Record_file) then
    fclose(Record_file);
  Record_file = fopen(p, "wt");
  if (!Record_file) then
    error((DREstring)"Unable to open recording file " + p);
  else
    Record = true; 
  return 1;
}

// replot
int vga_mode::_replot(DREstring&, DREstring&)
{ operating_mode::replot();
  return 1;
}

// set
int vga_mode::_set(DREstring& c, DREstring& p)
{ enum {QUERY = 1, DURATION, SCMODE, PRAMODE, STARTCHANNEL, ENDCHANNEL,
        PLOTMODE, FILETYPE, BRIGHTNESS, SATURATION, START_TOMPLOT_CHANNEL,
        NUMBER_OF_TOMPLOT_CHANNELS, AUTOSCALE, TOMPLOT_DN_RANGE };

  glist valid_parms;

  valid_parms += "?";			// must be first
  valid_parms += "DURATION";
  valid_parms += "SCMODE";
  valid_parms += "PRAMODE";
  valid_parms += "STARTCHANNEL";
  valid_parms += "ENDCHANNEL";
  valid_parms += "PLOTMODE";
  valid_parms += "FILETYPE";
  valid_parms += "BRIGHTNESS";
  valid_parms += "SATURATION";
  valid_parms += "START_TOMPLOT_CHANNEL";
  valid_parms += "NUMBER_OF_TOMPLOT_CHANNELS";
  valid_parms += "AUTOSCALE";
  valid_parms += "TOMPLOT_DN_RANGE";

  int subcommand_number;
  DREstring subcommand;
  const int space_position = find(p, " ");

  if (space_position) then
    subcommand = subDREstring(p, 1, space_position - 1).toupper();
  else
    subcommand = p.toupper();
  if (!(subcommand_number = valid_string(subcommand, valid_parms))) then
    error((DREstring)"in _set(DREstring&, DREstring&) : command " + c +
          (DREstring)" " + p + (DREstring)" unknown");

  switch (subcommand_number)
  { case QUERY :
             { DREstring command_list;
               for (int n = 2; n <= valid_parms.length(); n++)
               { command_list += (DREstring)(char*)valid_parms[n];
                 command_list += "\r\n";
               }
               error((DREstring)"\r\nvalid subcommands are: \r\n" + command_list);
               break;
             }
    case DURATION :
             Duration = atoi(subDREstring(p, space_position + 1));
             verbose((DREstring)"Plot duration now " + DREstring(Duration, 10));
             break;
    case SCMODE :
             _set_scmode(subDREstring(p, space_position + 1));
             break;
    case PRAMODE :
             _set_pramode(subDREstring(p, space_position + 1));
             break;
    case STARTCHANNEL :
             Start_channel = atoi(subDREstring(p, space_position + 1));
             verbose((DREstring)"Start channel now " + DREstring(Start_channel, 10));
             break;
    case PLOTMODE :
             _set_plotmode(subDREstring(p, space_position + 1));
             break;
    case FILETYPE :
             _infiletype((DREstring)"SET FILETYPE",
                         subDREstring(p, space_position + 1));
             break;
    case BRIGHTNESS :
             Brightness = atoi(subDREstring(p, space_position + 1));
             verbose((DREstring)"Brightness now " + DREstring10(Brightness));
             break;
    case SATURATION :
             Saturation = atoi(subDREstring(p, space_position + 1));
             verbose((DREstring)"Saturation now " + DREstring10(Saturation));
             break;
    case START_TOMPLOT_CHANNEL :
             Start_tomplot_channel = atoi(subDREstring(p, space_position + 1));
             verbose((DREstring)"Tomplots now start at channel " +
                     DREstring10(Start_tomplot_channel));
             break;
    case NUMBER_OF_TOMPLOT_CHANNELS :
             N_tomplot_channels = atoi(subDREstring(p, space_position + 1));
             verbose((DREstring)"Tomplots now contain " +
                     DREstring10(N_tomplot_channels) + " channels");
             break;
    case AUTOSCALE :
             { DREstring autoscale = subDREstring(p, space_position + 1);
               Autoscale_tomplot = (autoscale == (DREstring)"ON");
             }
             break;
    case TOMPLOT_DN_RANGE :
             { DREstring params = subDREstring(p, space_position + 1);
               int next_space_position = find(params, " ");
               DREstring channel_nr_string = subDREstring(params, 1, space_position - 1);
               params = subDREstring(params, space_position + 1);
               next_space_position = find(params, " ");
               DREstring low_value_string = subDREstring(params, 1, space_position - 1);
               DREstring high_value_string = subDREstring(params, space_position + 1);
               const int channel_nr = atoi(channel_nr_string);
               Tomplot_dn_range[channel_nr][0] = atoi(low_value_string);
               Tomplot_dn_range[channel_nr][1] = atoi(high_value_string);
             }
             break; 
 
/*
    case DISPLAY :
             _set_display(subDREstring(p, space_position + 1));
             break;
    case PRINTER :
             _set_printer(subDREstring(p, space_position + 1));
             break;
*/

  }
  return 0;
}



// prompt -- private
void vga_mode::_prompt(const char* cp, const int clr)
{ setcolor(clr);
  *Screen_device << text(10, 460, 0, (DREstring)cp, 0);
}

void vga_mode::display_status(DREstring& msg)
{ char blank_line[81];
  blank_line[80] = '\0';
  for (int n = 0; n < 80; n++)
    blank_line[n] = 219;
  setcolor(BLACK);
  *Screen_device << text(10, 440, 0, (DREstring)blank_line, 0);
  setcolor(LIGHTGREEN);
  *Screen_device << text(10, 440, 0, msg, 0);
}

// write text on a text screen
void vga_mode::_display_text(DREstring& msg, DREstring& title, const int clr,
                             const boolean centered)
{ const int gmode = getgraphmode();
  restorecrtmode();
  textcolor(clr);
  int x = (centered ? 39 - title.length() / 2 : 1);
  gotoxy(x, 1);
  cprintf(title);
  x = (centered ? 39 - msg.length() / 2 : 1);
  gotoxy(x, wherey() + 2);
  cprintf(msg);
  DREstring prompt("Press any key to continue");
  x = (centered ? 39 - prompt.length() / 2 : 1);
  gotoxy(x, wherey() + 2);
  cprintf(prompt);
  getch();
  setgraphmode(gmode); 
}

// display an error
void vga_mode::error(DREstring& msg)
{ _display_text(msg, (DREstring)"-- ERROR --", LIGHTRED, CENTERED); 
}

// the main execution loop
void vga_mode::execute(void)
{ DREstring command_line, command;
  int space_position, command_number;
  forever
  { command_number = 0;
    while (!command_number)
    { command_line = _get_command();
      if (Record) then
        fprintf(Record_file, "%s\n", (const char*)command_line);
      space_position = find(command_line, " ");
  
      if (space_position) then
        command = subDREstring(command_line, 1, space_position - 1).toupper();  
      else
        command = command_line.toupper();
  
      if (!(command_number = valid_string(command, known_commands))) then
      { error((DREstring)"Error in vga_mode::execute() : command `" + command +
              (DREstring)"' unknown");
      }
    }

    command_number--;
// execute the command, passing the remainder of the command line
    DREstring parameters;
    if (space_position) then
     parameters = subDREstring(command_line, space_position + 1);

    typedef int (vga_mode::*VPMFSS)(DREstring&, DREstring&);
    VPMFSS _dispatch_function = dispatch_functions[command_number];
// exit is special
    if (_dispatch_function == &vga_mode::_exit) then
      goto end_execute;
    (this->*_dispatch_function)(command, parameters); 
  }
end_execute:
}

// get a command
DREstring vga_mode::_get_command(void)
{ char blank_line[81];
  blank_line[80] = '\0';
  for (int n = 0; n < 80; n++)
    blank_line[n] = 219;
  _prompt(blank_line, BLACK);
  _prompt("Command:_", COMMAND_COLOUR);
  DREstring command_DREstring;
  char filled_square = 219, underbar = 95;
  char t[2];
  const int char_width = textwidth(" ");
  t[1] = '\0';
  char w[2];
  w[1] = '\0';
   while (t[0] = (Play ? getc(Play_file) : getch()), t[0] != 13)
  { if (t[0] == EOF) then
    { fclose(Play_file);
      Play = false;
      t[0] = 8;
    } 
    // backspace is a special case
    if ((t[0] == 8) && (command_DREstring.length())) then
    { command_DREstring = subDREstring(command_DREstring, 1, 
                                 command_DREstring.length() - 1);
      Screen_device->moverel(-2 * char_width, 0);
      w[0] = filled_square;
      setcolor(BLACK);
      *Screen_device << text(-1, -1, 0, (DREstring)w, 0);
      *Screen_device << text(-1, -1, 0, (DREstring)w, 0);
      setcolor(COMMAND_COLOUR);
      Screen_device->moverel(-2 * char_width, 0);
      w[0] = underbar;
      *Screen_device << text(-1, -1, 0, (DREstring)w, 0);
    }
    if (t[0] != 8) then
    { command_DREstring += (DREstring)t;
      Screen_device->moverel(-char_width, 0);
      w[0] = filled_square;
      setcolor(BLACK);
      *Screen_device << text(-1, -1, 0, (DREstring)w, 0);
      setcolor(COMMAND_COLOUR);
      Screen_device->moverel(-char_width, 0);
      *Screen_device << text(-1, -1, 0, (DREstring)t, 0);
      w[0] = underbar;
      *Screen_device << text(-1, -1, 0, (DREstring)w, 0);
    }
  }
  return command_DREstring;
}
