/*
  Copyright 2003-2005 Ronald S. Burkey <info@sandroid.org>
  
  This file is part of yaAGC.

  yaAGC is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  yaAGC is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with yaAGC; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  
  Filename:	main.c
  Contact:	Ron Burkey <info@sandroid.org>
  Reference:	http://www.ibiblio.org/apollo/index.html
  Mods:		08/20/03 RSB.	First fully-working version.
  		07/11/04 RSB.	Flash-rate change from 1 cycle per second
				to 1.5 cps. per AC's "APOLLO GUIDANCE AND 
				NAVIGATION SYSTEM LUNAR MODULE STUDENT STUDY 
				GUIDE".  Also, instead of flashing the NOUN 
				or VERB labels, we now flash the NOUN or VERB 
				digits.  Furthermore, OPR ERR and KEY REL flash.
		07/19/04 RSB.	--help messages adjusted for crippled Win32
				command-line length.
		08/11/04 RSB	Added the --half-size option.
		08/21/04 RSB	Added StartupDelay et al.
		05/14/05 RSB	Added and corrected website references.
		05/15/05 RSB	Added CounterDebugMode, for debugging
				yaAGC counter increments.
		05/29/05 RSB	In the Pulse() function, rewrote the
				conditions for buffering incoming 
				socket data to take into account the new
				idea of using packet signatures rather
				than the strict sequence 00 01 10 11.
		06/26/05 RSB	Added the --test-uplink switch.
		06/28/05 RSB	Added --test-downlink, to separate that
				functionality from --test-uplink.
*/

/*
 * Initial main.c file generated by Glade. Edit as required.
 * Glade will not overwrite this file.
 */

//#define VER(x) #x

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <gtk/gtk.h>

#include "interface.h"
#include "support.h"

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

#include "yaAGC.h"
#include "agc_engine.h"

int HalfSize = 0;
#define PULSE_INTERVAL 100
gint Pulse (gpointer data);
char *Hostname = "localhost";
//int Portnum = 19697;
GtkWidget *window1 = NULL;
int ParseCfg (GtkWidget *, char *Filename);
#ifdef WIN32
static int StartupDelay = 500;
#else
static int StartupDelay = 0;
#endif
int DebugCounterMode = 0;
// TestUplink is set when we want to test the digital uplink by emitting
// keycodes on the digital uplink rather than on the DSKY channels.
int TestUplink = 0, TestDownlink = 0;

//------------------------------------------------------------------------

#define DEBUG(x) 

int
main (int argc, char *argv[])
{
  GtkWidget *hCreateMainWindow (void);
  int i, j;

#ifdef ENABLE_NLS
  bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
  textdomain (GETTEXT_PACKAGE);
#endif

  gtk_set_locale ();
  gtk_init (&argc, &argv);

  printf ("yaDSKY Apollo DSKY simulation, ver " NVER ", built " __DATE__ " " __TIME__ "\n");
  printf ("Copyright 2003-2005 by Ronald S. Burkey\n");
  printf ("Refer to http://www.ibiblio.org/apollo/index.html for more information.\n");
	  
  add_pixmap_directory (PACKAGE_DATA_DIR "/" PACKAGE "/pixmaps");
  DEBUG (-1);

  for (i = 1; i < argc; i++)
    {
      if (!strcmp (argv[i], "--test-uplink"))
        TestUplink = 1;
      else if (!strcmp (argv[i], "--test-downlink"))
        TestDownlink = 1;
      else if (!strncmp (argv[i], "--ip=", 5))
        Hostname = &argv[i][5];
      else if (!strncmp (argv[i], "--port=", 7))
        {
          Portnum = atoi (&argv[i][7]);
	  if (Portnum <= 0 || Portnum >= 0x10000)
	    {
	      printf ("The --port switch is out of range.  Must be 1-64K.\n");
	      return (1);
	    }
	}
      else if (!strncmp (argv[i], "--cfg=", 6))
        {
	  if (window1 == NULL)
	    window1 = create_MainWindow ();
	  if (ParseCfg (window1, &argv[i][6]))
	    {
	      printf ("Aborting due to --cfg errors.\n");
	      return (1);
	    }
	}
      else if (!strcmp (argv[i], "--half-size"))
        {
	  if (window1 == NULL)
	    window1 = hCreateMainWindow ();
	  else
	    {
	      printf ("The --half-size switch must precede the --cfg switch.\n");
	      return (1);
	    }
          HalfSize = 1;
	}
      else if (1 == sscanf (argv[i], "--delay=%d", &j))
        StartupDelay = j;
      else if (!strcmp (argv[i], "--debug-counter-mode"))
        DebugCounterMode = 1;
      else
        {
	  printf ("USAGE:\n");
	  printf ("\tyadsky [OPTIONS]\n");
	  printf ("The available options are:\n");
	  printf ("--ip=Hostname\n");
	  printf ("\tThe yaDSKY program and the yaAGC Apollo Guidance Computer simulation\n");
	  printf ("\texist in a \"client/server\" relationship, in which the yaDSKY program\n");
	  printf ("\tneeds to be aware of the IP address or symbolic name of the host \n");
	  printf ("\tcomputer running the yaAGC program.  By default, this is \"localhost\",\n");
	  printf ("\tmeaning that both yaDSKY and yaAGC are running on the same computer.\n");
	  printf ("--port=Portnumber\n");
	  printf ("\tBy default, yaDSKY attempts to connect to the yaAGC program using port\n");
	  printf ("\tnumber %d.  However, if more than one instance of yaDSKY is being\n",
	          Portnum);
	  printf ("\trun, or if yaAGC has been configured to listen on different ports, then\n");
	  printf ("\tdifferent port settings for yaDSKY are needed.  Note that by default,\n");
	  printf ("\tyaAGC listens for new connections on ports %d-%d.\n",
	          Portnum, Portnum + 10 - 1);
	  printf ("--cfg=ConfigFilename\n");
	  printf ("\tSelects a configuration file to be used, to allow different yaDSKY\n");
	  printf ("\tsettings for LM vs. CM, or for different Apollo missions.  The \n");
	  printf ("\tconfiguration files presently known are LM.ini, CM.ini, and CM0.ini. \n");
	  printf ("\tBy default (no --cfg switch) LM settings are used, but not the LM.ini\n");
	  printf ("\tfile itself.\n");
	  printf ("--half-size\n");
	  printf ("\tUses a half-size version of yaDSKY, suitable for smaller graphical\n");
	  printf ("\tdisplays.  If present, must precede --cfg.\n");
	  printf ("--delay=N\n");
	  printf ("\t\"Start-up delay\", in ms.  Defaults to %d.  What the start-up\n", StartupDelay);
	  printf ("\tdelay does is to prevent yaDSKY from attempting to communicate with\n");
	  printf ("\tyaAGC for a brief time after power-up.  This option is really only\n");
	  printf ("\tuseful in Win32, to work around a problem with race-conditions in\n");
	  printf ("\tthe start-up scripts like SimLuminary131.  When the race problem is\n");
	  printf ("\tfixed correctly, this option will probably no longer be useful.\n");
	  printf ("--debug-counter-mode\n");
	  printf ("\tThis is present only for debugging yaAGC\'s \"unprogrammed\"\n");
	  printf ("\tcounter-increments, and has no useful purpose otherwise.  In this\n");
	  printf ("\tthe simulated DSKY\'s do not have their normal interpretations and\n");
	  printf ("\tcannot be used to affect the AGC CPU in any normal way.  Instead,\n");
	  printf ("\tthe combination NOUN-digit-digit is used to specify an (octal)\n");
	  printf ("\tCPU register number, and the combination VERB-digit is used\n");
	  printf ("\tto select a counter-increment type as defined on the Virtual AGC\n");
	  printf ("\twebsite\'s developer page.  The PRO key is used to send the selected\n");
	  printf ("\tcounter-increment command to the AGC.  Note that there are no\n");
	  printf ("\tvisual displays associated with this, since the AGC is still\n");
	  printf ("\tcommanding the DSKY visual display in its usual way.  This mode\n");
	  printf ("\tis terminated only by restarting yaDSKY.  Only counter registers\n");
	  printf ("\tin the range 32-60 (octal, default 32) are accepted, and only \n");
	  printf ("\tincrement-types in the range 0-6 (default 1) are accepted.\n");
	  printf ("--test-uplink\n");
	  printf ("\tIf this switch is used, keypresses are communicated to yaAGC\n");
	  printf ("\tas digital-uplink date instead of regular DSKY data.\n");
	  printf ("--test-downlink\n");
	  printf ("\tIn this mode, messages will also be printed whenever a downlink list is\n");
	  printf ("\tdetected. (Highly recommended.  Probably should be the default.)\n");
	  return (0);
	}	
    }
  DEBUG (0);
  printf ("Hostname=%s, port=%d\n", Hostname, Portnum);	
  	
  /*
   * The following code was added by Glade to create one of each component
   * (except popup menus), just so that you see something after building
   * the project. Delete any components that you don't want shown initially.
   */
  DEBUG (1);
  if (window1 == NULL)
    window1 = create_MainWindow ();
  DEBUG (2);
  gtk_widget_show (window1);
  DEBUG (3);

  // Sets up the Pulse function to be called every PULSE_INTERVAL ms.
  g_timeout_add (PULSE_INTERVAL, Pulse, NULL);
  DEBUG (4);

  gtk_main ();
  return 0;
}

//-------------------------------------------------------------------------
// Manage server connection.

int ServerSocket = -1;

gint
Pulse (gpointer data)
{
#define gtk_image_set_from_file my_gtk_image_set_from_file
  extern void my_gtk_image_set_from_file (GtkImage *Image, const char *Filename);
  extern int VerbNounFlashing;
  static unsigned char Packet[4];
  static int PacketSize = 0;
  static int FlashCounter = 1, FlashStatus = 0;
  int i;
  unsigned char c;
  if (StartupDelay > 0)
    {
      StartupDelay -= PULSE_INTERVAL;
      return (TRUE);
    }
  // If the noun/verb-flash flag is set, then flash them.
  if (!--FlashCounter)
    {
#if 1
      extern GtkImage *VD1Digit, *VD2Digit, *ND1Digit, *ND2Digit,
		      *OprErrAnnunciator, *KeyRelAnnunciator;
      extern const char *CurrentVD1, *CurrentVD2, *CurrentND1, 
			*CurrentND2, *CurrentBlank,
			*CurrentOprErr, *CurrentKeyRel,
			*BlankOprErr, *BlankKeyRel;
      if (CurrentVD1 == CurrentBlank && CurrentVD2 == CurrentBlank &&
          CurrentND1 == CurrentBlank && CurrentND2 == CurrentBlank &&
	  CurrentKeyRel == BlankKeyRel && CurrentOprErr == BlankOprErr)
        FlashCounter = 1;
      else
        FlashCounter = 10;
      if (FlashStatus)
	{
	  if (VerbNounFlashing)
	    {
	      gtk_image_set_from_file (VD1Digit, CurrentVD1);
	      gtk_image_set_from_file (VD2Digit, CurrentVD2);
	      gtk_image_set_from_file (ND1Digit, CurrentND1);
	      gtk_image_set_from_file (ND2Digit, CurrentND2);
	    }
	  gtk_image_set_from_file (KeyRelAnnunciator, CurrentKeyRel);
	  gtk_image_set_from_file (OprErrAnnunciator, CurrentOprErr);
	}
      else
	{
	  if (VerbNounFlashing)
	    {
	      gtk_image_set_from_file (VD1Digit, CurrentBlank);
	      gtk_image_set_from_file (VD2Digit, CurrentBlank);
	      gtk_image_set_from_file (ND1Digit, CurrentBlank);
	      gtk_image_set_from_file (ND2Digit, CurrentBlank);
	    }
	  gtk_image_set_from_file (KeyRelAnnunciator, BlankKeyRel);
	  gtk_image_set_from_file (OprErrAnnunciator, BlankOprErr);
	}
#else // 1
      extern GtkImage *VerbAnnunciator, *NounAnnunciator;
      FlashCounter = 10;
      if (FlashStatus)
	{
	  gtk_image_set_from_file (VerbAnnunciator,
				   PACKAGE_DATA_DIR "/" PACKAGE 
				   "/pixmaps/VerbOn.xpm");
	  gtk_image_set_from_file (NounAnnunciator,
				   PACKAGE_DATA_DIR "/" PACKAGE 
				   "/pixmaps/NounOn.xpm");
	}
      else
	{
	  gtk_image_set_from_file (VerbAnnunciator,
				   PACKAGE_DATA_DIR "/" PACKAGE 
				   "/pixmaps/VerbOff.xpm");
	  gtk_image_set_from_file (NounAnnunciator,
				   PACKAGE_DATA_DIR "/" PACKAGE 
				   "/pixmaps/NounOff.xpm");
	}
#endif // 1
      FlashStatus = !FlashStatus;
    }
  // Try to connect to the server (yaAGC) if not already connected.
  if (ServerSocket == -1)
    {
      ServerSocket = CallSocket (Hostname, Portnum);
      if (ServerSocket != -1)
        printf ("yaDSKY is connected.\n");
    }
  if (ServerSocket != -1)
    {
      for (;;)
        {
	  i = recv (ServerSocket, &c, 1, MSG_NOSIGNAL);
	  if (i == -1)
	    {
	      // The condition i==-1,errno==0 occurs only on Win32, and 
	      // I'm not sure exactly what it corresponds to---I assume 
	      // means "no data" rather than error.
	      if (errno == EAGAIN || errno == 0)
	        i = 0;
	      else
	        {	
		  printf ("yaDSKY reports server error %d\n", errno);
		  close (ServerSocket);
		  ServerSocket = -1;
		  break;
	        }
	    }
	  if (i == 0)
	    break;
#if 0
	  // This (older) code is based on the notion that the packet signature
	  // is always 00 01 10 11.
	  if ((PacketSize << 6) == (0xc0 & c))
	    { 
	      Packet[PacketSize++] = c;
	      if (PacketSize >= 4)
		{
		  void ActOnIncomingIO (GtkWidget *widget, unsigned char *Packet);
		  ActOnIncomingIO (window1, Packet);
		  PacketSize = 0;   
		}  
	    }
	  else
	    {
	      PacketSize = 0;  
	      if (0 == (0xc0 & c))
	        Packet[PacketSize++] = c;
	    }
#else // 0
	  // This (newer) code will accept any packet signature of the form
	  // 00 XX XX XX.
	  if (0 == (0xc0 & c))
	    PacketSize = 0;
	  if (PacketSize != 0 || (0xc0 & c) == 0)	      
	    { 
	      Packet[PacketSize++] = c;
	      if (PacketSize >= 4)
		{
		  void ActOnIncomingIO (GtkWidget *widget, unsigned char *Packet);
		  ActOnIncomingIO (window1, Packet);
		  PacketSize = 0;   
		}  
	    }
#endif // 0
	}
    }
  return (TRUE);    
}

