// -*- C++ -*- generated by wxGlade 0.6.3 on Mon Mar 16 15:10:36 2009
/*
  Copyright 2009 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:	yaACA2.cpp
  Purpose:	Apollo LM rotational hand-controller emulation module for 
  		yaAGC.
  Reference:	http://www.ibiblio.org/apollo/index.html
  Mode:		2009-03-19 RSB	Began.
  		2009-03-22 RSB	Removed strict dependence on wxJoystick, so 
				that Allegro or SDL joystick code could be 
				used, according to the command-line switches.
		2009-03-23 RSB	Correctly implemented END_OF_MAIN() so that
				Allegro and wxWidgets can play together on 
				the Mac.  (I hope!)  Began trying to add
				SDL support.
		2009-04-09 RSB	Backed out all Allegro and SDL stuff.
				yaACA and yaACA3 are perfectly fine 
				Allegro and SDL programs, and I don't need
				this stuff cluttering up yaACA2.
*/

#define VER(x) #x

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

#include "yaACA2.h"
#include "../yaAGC/yaAGC.h"
#include "../yaAGC/agc_engine.h"

#include <wx/file.h>
#include <wx/textfile.h>

// Interval (ms.) at which the timer fires for servicing the socket.
#define PULSE_INTERVAL 100

static yaAcaFrameClass* yaAcaFrame;
static char DefaultHostname[] = "localhost";
static char *Hostname = DefaultHostname;
static char NonDefaultHostname[129];
#ifdef WIN32
static int StartupDelay = 500;
#else
static int StartupDelay = 0;
#endif
extern int Portnum;
static int ServerSocket = -1, LastServerSocket = -1;
static int ControllerNumber = 0;
static int JoystickConnectionTimeout = 0;


// begin wxGlade: ::extracode
// end wxGlade


// Sets parameters to all defaults.  These parameters are
// for my Logitech 3D Extreme.
void
yaAcaFrameClass::DefaultParameters (void)
{
    Roll.Axis = AXIS_X;
    Roll.PositiveSense = true;
    Pitch.Axis = AXIS_Y;
    Pitch.PositiveSense = true;
#if defined (WIN32)
    Yaw.Axis = AXIS_RUDDER;
#elif defined (__APPLE__)
    Yaw.Axis = AXIS_U;
#else // Linux
    Yaw.Axis = AXIS_Z;
#endif
    Yaw.PositiveSense = false;
}


// Gets parameters from a cfg file if available, from defaults otherwise.
void
yaAcaFrameClass::GetParameters (int ControllerNumber)
{

  DefaultParameters ();
  
  wxTextFile File;
  wxString Filename;
  Filename = wxString::Format (wxT ("yaACA2-%d.cfg"), ControllerNumber);
  if (wxFileExists (Filename))
    {
      wxString Dummy, Name, Value;
      int LineCount, i;
      long iValue;
      
      File.Open (Filename);
      LineCount = File.GetLineCount ();
      for (i = 0; i < LineCount; i++)
        {
	  Dummy = File.GetLine (i);
	  Name = Dummy.BeforeLast ('=');
	  Value = Dummy.AfterLast ('=');
	  if (!Value.ToLong (&iValue))
	    iValue = 0;
	  if (Name.IsSameAs (wxT ("Roll.Axis")))
	    Roll.Axis = iValue;
	  else if (Name.IsSameAs (wxT ("Roll.PositiveSense")))
	    Roll.PositiveSense = (iValue == 0);
	  else if (Name.IsSameAs (wxT ("Pitch.Axis")))
	    Pitch.Axis = iValue;
	  else if (Name.IsSameAs (wxT ("Pitch.PositiveSense")))
	    Pitch.PositiveSense = (iValue == 0);
	  else if (Name.IsSameAs (wxT ("Yaw.Axis")))
	    Yaw.Axis = iValue;
	  else if (Name.IsSameAs (wxT ("Yaw.PositiveSense")))
	    Yaw.PositiveSense = (iValue == 0);
	}
      
      File.Close ();
    }
}


// Writes parameters to a cfg file.
void
yaAcaFrameClass:: WriteParameters (int ControllerNumber)
{
  wxFile File;
  wxString Filename;
  Filename = wxString::Format (wxT ("yaACA2-%d.cfg"), ControllerNumber);
  if (File.Create (Filename, true))
    {
      wxString Dummy;
      Dummy = wxString::Format (wxT ("Roll.Axis=%d\n"), Roll.Axis);
      File.Write (Dummy);
      Dummy = wxString::Format (wxT ("Roll.PositiveSense=%d\n"), Roll.PositiveSense ? 0 : 1);
      File.Write (Dummy);
      Dummy = wxString::Format (wxT ("Pitch.Axis=%d\n"), Pitch.Axis);
      File.Write (Dummy);
      Dummy = wxString::Format (wxT ("Pitch.PositiveSense=%d\n"), Pitch.PositiveSense ? 0 : 1);
      File.Write (Dummy);
      Dummy = wxString::Format (wxT ("Yaw.Axis=%d\n"), Yaw.Axis);
      File.Write (Dummy);
      Dummy = wxString::Format (wxT ("Yaw.PositiveSense=%d\n"), Yaw.PositiveSense ? 0 : 1);
      File.Write (Dummy);
      File.Close ();
    }
  else
    {
      Filename = wxT ("Cannot create file ") + Filename + wxT (".\nConfiguration not saved.");
      wxMessageBox (Filename, wxT ("Error"), wxICON_ERROR);
    }
}


yaAcaFrameClass::yaAcaFrameClass(wxWindow* parent, int id, const wxString& title, const wxPoint& pos, const wxSize& size, long style):
    wxFrame(parent, id, title, pos, size, wxDEFAULT_FRAME_STYLE|wxCLOSE_BOX)
{
    GetParameters (ControllerNumber);
    Roll.CurrentAdjustedReading = 0;
    Roll.LastRawReading = 0;
    Pitch.CurrentAdjustedReading = 0;
    Pitch.LastRawReading = 0;
    Yaw.CurrentAdjustedReading = 0;
    Yaw.LastRawReading = 0;

    // begin wxGlade: yaAcaFrameClass::yaAcaFrameClass
    StatusPanel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER|wxTAB_TRAVERSAL);
    ControllerPositionPanel_copy_1 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER|wxTAB_TRAVERSAL);
    ControllerPositionPanel_copy = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER|wxTAB_TRAVERSAL);
    ControllerPitchPanel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER|wxTAB_TRAVERSAL);
    panel_1 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER|wxTAB_TRAVERSAL);
    ModelPanel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER|wxTAB_TRAVERSAL);
    ControllerNumberPanel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER|wxTAB_TRAVERSAL);
    ControllerSizer_staticbox = new wxStaticBox(this, -1, wxT("Joystick Controller"));
    RollBox_staticbox = new wxStaticBox(this, -1, wxT("Roll Axis Configuration"));
    PitchBox_staticbox = new wxStaticBox(this, -1, wxT("Pitch Axis Configuration"));
    YawBox_staticbox = new wxStaticBox(this, -1, wxT("Yaw Axis Configuration"));
    panel_2 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER|wxTAB_TRAVERSAL);
    label_4 = new wxStaticText(this, wxID_ANY, wxT("Drivers: "));
    ToolkitLabel = new wxStaticText(panel_2, wxID_ANY, wxT("0 (wxWidgets)"));
    label_1 = new wxStaticText(this, wxID_ANY, wxT("Controller: "));
    ControllerNumberLabel = new wxStaticText(ControllerNumberPanel, wxID_ANY, wxT("0"));
    label_1_copy_3 = new wxStaticText(this, wxID_ANY, wxT("Model: "));
    ControllerModelLabel = new wxStaticText(ModelPanel, wxID_ANY, wxT("(no joystick controller attached)"));
    label_3 = new wxStaticText(this, wxID_ANY, wxT("Axes: "));
    BoxAxis0 = new wxCheckBox(panel_1, wxID_ANY, wxT("0"));
    BoxAxis1 = new wxCheckBox(panel_1, wxID_ANY, wxT("1"));
    BoxAxis2 = new wxCheckBox(panel_1, wxID_ANY, wxT("2"));
    BoxAxis3 = new wxCheckBox(panel_1, wxID_ANY, wxT("3"));
    BoxAxis4 = new wxCheckBox(panel_1, wxID_ANY, wxT("4"));
    BoxAxis5 = new wxCheckBox(panel_1, wxID_ANY, wxT("5"));
    BoxAxis6 = new wxCheckBox(panel_1, wxID_ANY, wxT("6"));
    label_1_copy_1 = new wxStaticText(this, wxID_ANY, wxT("Pitch: "));
    ControllerPitchLabel = new wxStaticText(ControllerPitchPanel, wxID_ANY, wxT("0"));
    label_1_copy_4 = new wxStaticText(this, wxID_ANY, wxT("Roll: "));
    ControllerRollLabel = new wxStaticText(ControllerPositionPanel_copy, wxID_ANY, wxT("0"));
    label_1_copy = new wxStaticText(this, wxID_ANY, wxT("Yaw: "));
    ControllerYawLabel = new wxStaticText(ControllerPositionPanel_copy_1, wxID_ANY, wxT("0"));
    label_2 = new wxStaticText(this, wxID_ANY, wxT("Axis number: "));
    RollAxisCtrl = new wxSpinCtrl(this, wxID_ANY, wxT("0"), wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 6);
    const wxString RollPolarityBox_choices[] = {
        wxT("Positive"),
        wxT("Negative")
    };
    RollPolarityBox = new wxRadioBox(this, wxID_ANY, wxT("Polarity"), wxDefaultPosition, wxDefaultSize, 2, RollPolarityBox_choices, 0, wxRA_SPECIFY_ROWS);
    label_2_copy = new wxStaticText(this, wxID_ANY, wxT("Axis number: "));
    PitchAxisCtrl = new wxSpinCtrl(this, wxID_ANY, wxT("0"), wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 6);
    const wxString PitchPolarityBox_choices[] = {
        wxT("Positive"),
        wxT("Negative")
    };
    PitchPolarityBox = new wxRadioBox(this, wxID_ANY, wxT("Polarity"), wxDefaultPosition, wxDefaultSize, 2, PitchPolarityBox_choices, 0, wxRA_SPECIFY_ROWS);
    label_2_copy_1 = new wxStaticText(this, wxID_ANY, wxT("Axis number: "));
    YawAxisCtrl = new wxSpinCtrl(this, wxID_ANY, wxT("0"), wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 6);
    const wxString YawPolarityBox_choices[] = {
        wxT("Positive"),
        wxT("Negative")
    };
    YawPolarityBox = new wxRadioBox(this, wxID_ANY, wxT("Polarity"), wxDefaultPosition, wxDefaultSize, 2, YawPolarityBox_choices, 0, wxRA_SPECIFY_ROWS);
    DefaultButton = new wxButton(this, ID_DEFAULT, wxT("Default"));
    SetButton = new wxButton(this, ID_SET, wxT("Set"));
    StatusLabel = new wxStaticText(StatusPanel, wxID_ANY, wxT("Waiting for connection to yaAGC ..."));

    set_properties();
    do_layout();
    // end wxGlade
    
    RollAxisCtrl->SetValue (Roll.Axis);
    RollPolarityBox->SetSelection (Roll.PositiveSense ? 0 : 1);
    PitchAxisCtrl->SetValue (Pitch.Axis);
    PitchPolarityBox->SetSelection (Pitch.PositiveSense ? 0 : 1);
    YawAxisCtrl->SetValue (Yaw.Axis);
    YawPolarityBox->SetSelection (Yaw.PositiveSense ? 0 : 1);
    ToolkitLabel->SetLabel (wxT ("wxWidgets"));
    wxString Dummy = wxString::Format (wxT ("%d"), ControllerNumber);
    ControllerNumberLabel->SetLabel (Dummy);
    ControllerModelLabel->SetLabel (wxT ("(no joystick controller attached)"));
    ControllerRollLabel->SetLabel (wxT ("(none)"));
    ControllerPitchLabel->SetLabel (wxT ("(none)"));
    ControllerYawLabel->SetLabel (wxT ("(none)"));
    BoxAxis0->SetValue (false);
    BoxAxis1->SetValue (false);
    BoxAxis2->SetValue (false);
    BoxAxis3->SetValue (false);
    BoxAxis4->SetValue (false);
    BoxAxis5->SetValue (false);
    BoxAxis6->SetValue (false);
}


BEGIN_EVENT_TABLE(yaAcaFrameClass, wxFrame)
    // begin wxGlade: yaAcaFrameClass::event_table
    EVT_BUTTON(ID_DEFAULT, yaAcaFrameClass::OnDefaultPressed)
    EVT_BUTTON(ID_SET, yaAcaFrameClass::OnSetPressed)
    // end wxGlade
    EVT_CLOSE(yaAcaFrameClass::TimerStop)
END_EVENT_TABLE();


// wxGlade: add yaAcaFrameClass event handlers


void yaAcaFrameClass::OnDefaultPressed (wxCommandEvent &event)
{
  DefaultParameters ();
  RollAxisCtrl->SetValue (Roll.Axis);
  RollPolarityBox->SetSelection (Roll.PositiveSense ? 0 : 1);
  PitchAxisCtrl->SetValue (Pitch.Axis);
  PitchPolarityBox->SetSelection (Pitch.PositiveSense ? 0 : 1);
  YawAxisCtrl->SetValue (Yaw.Axis);
  YawPolarityBox->SetSelection (Yaw.PositiveSense ? 0 : 1);
  WriteParameters (ControllerNumber);
}


void yaAcaFrameClass::OnSetPressed (wxCommandEvent &event)
{
  Roll.Axis = RollAxisCtrl->GetValue ();
  Roll.PositiveSense = (RollPolarityBox->GetSelection () == 0);
  Pitch.Axis = PitchAxisCtrl->GetValue ();
  Pitch.PositiveSense = (PitchPolarityBox->GetSelection () == 0);
  Yaw.Axis = YawAxisCtrl->GetValue ();
  Yaw.PositiveSense = (YawPolarityBox->GetSelection () == 0);
  WriteParameters (ControllerNumber);
}


void yaAcaFrameClass::set_properties()
{
    // begin wxGlade: yaAcaFrameClass::set_properties
    SetTitle(wxT("yaACA2 by Ron Burkey"));
    wxIcon _icon;
    _icon.CopyFromBitmap(wxBitmap(wxT("ApolloPatch2.png"), wxBITMAP_TYPE_ANY));
    SetIcon(_icon);
    SetBackgroundColour(wxColour(255, 255, 255));
    BoxAxis0->Enable(false);
    BoxAxis0->SetValue(1);
    BoxAxis1->Enable(false);
    BoxAxis1->SetValue(1);
    BoxAxis2->Enable(false);
    BoxAxis3->Enable(false);
    BoxAxis4->Enable(false);
    BoxAxis5->Enable(false);
    BoxAxis6->Enable(false);
    RollAxisCtrl->SetToolTip(wxT("For a joystick controller that has multiple degrees of freedom, it's necessary to select which such dimension is associate with pitch, with roll, and with yaw.  A different degree of freedom is associated wtih each.  This setting does not take effect until the \"Set\" button is clicked."));
    RollPolarityBox->SetToolTip(wxT("Determine whether which direction is \"positive\" and which is \"negative\"."));
    RollPolarityBox->SetSelection(0);
    PitchAxisCtrl->SetToolTip(wxT("For a joystick controller that has multiple degrees of freedom, it's necessary to select which such dimension is associate with pitch, with roll, and with yaw.  A different degree of freedom is associated wtih each.  This setting does not take effect until the \"Set\" button is clicked."));
    PitchPolarityBox->SetToolTip(wxT("Determine whether which direction is \"positive\" and which is \"negative\"."));
    PitchPolarityBox->SetSelection(0);
    YawAxisCtrl->SetToolTip(wxT("For a joystick controller that has multiple degrees of freedom, it's necessary to select which such dimension is associate with pitch, with roll, and with yaw.  A different degree of freedom is associated wtih each.  This setting does not take effect until the \"Set\" button is clicked."));
    YawPolarityBox->SetToolTip(wxT("Determine whether which direction is \"positive\" and which is \"negative\"."));
    YawPolarityBox->SetSelection(0);
    DefaultButton->SetToolTip(wxT("Click this to restore the defaults.  The new settings go into effect as soon as this button is clicked."));
    SetButton->SetToolTip(wxT("Click this to accept the settings above.  The settings do not take effect until this button is clicked."));
    // end wxGlade
}


void yaAcaFrameClass::do_layout()
{
    // begin wxGlade: yaAcaFrameClass::do_layout
    wxFlexGridSizer* TopSizer = new wxFlexGridSizer(6, 3, 0, 0);
    wxBoxSizer* StatusSizer = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer* sizer_6 = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer* ControllerBox = new wxBoxSizer(wxVERTICAL);
    wxStaticBoxSizer* YawBox = new wxStaticBoxSizer(YawBox_staticbox, wxVERTICAL);
    wxBoxSizer* sizer_1_copy_1 = new wxBoxSizer(wxHORIZONTAL);
    wxStaticBoxSizer* PitchBox = new wxStaticBoxSizer(PitchBox_staticbox, wxVERTICAL);
    wxBoxSizer* sizer_1_copy = new wxBoxSizer(wxHORIZONTAL);
    wxStaticBoxSizer* RollBox = new wxStaticBoxSizer(RollBox_staticbox, wxVERTICAL);
    wxBoxSizer* sizer_1 = new wxBoxSizer(wxHORIZONTAL);
    wxStaticBoxSizer* ControllerSizer = new wxStaticBoxSizer(ControllerSizer_staticbox, wxVERTICAL);
    wxFlexGridSizer* grid_sizer_1 = new wxFlexGridSizer(6, 2, 0, 0);
    wxBoxSizer* ControllerPositionSizer_copy_1 = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer* ControllerPositionSizer_copy = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer* ControllerPositionSizer = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer* sizer_2 = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer* sizer_2_copy = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer* ModelSizer = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer* ModelSizer_copy = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer* sizer_3 = new wxBoxSizer(wxHORIZONTAL);
    TopSizer->Add(20, 10, 0, 0, 0);
    TopSizer->Add(20, 10, 0, 0, 0);
    TopSizer->Add(20, 10, 0, 0, 0);
    TopSizer->Add(20, 20, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 0);
    grid_sizer_1->Add(label_4, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 0);
    sizer_3->Add(ToolkitLabel, 1, wxEXPAND, 0);
    panel_2->SetSizer(sizer_3);
    grid_sizer_1->Add(panel_2, 1, wxEXPAND, 0);
    grid_sizer_1->Add(label_1, 0, wxALIGN_RIGHT, 0);
    ModelSizer_copy->Add(ControllerNumberLabel, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 0);
    ControllerNumberPanel->SetSizer(ModelSizer_copy);
    grid_sizer_1->Add(ControllerNumberPanel, 1, wxEXPAND, 0);
    grid_sizer_1->Add(label_1_copy_3, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 0);
    ModelSizer->Add(ControllerModelLabel, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 0);
    ModelPanel->SetSizer(ModelSizer);
    grid_sizer_1->Add(ModelPanel, 1, wxEXPAND, 0);
    grid_sizer_1->Add(label_3, 0, wxALIGN_RIGHT, 0);
    sizer_2_copy->Add(BoxAxis0, 0, 0, 0);
    sizer_2_copy->Add(BoxAxis1, 0, 0, 0);
    sizer_2_copy->Add(BoxAxis2, 0, 0, 0);
    sizer_2_copy->Add(BoxAxis3, 0, 0, 0);
    sizer_2_copy->Add(BoxAxis4, 0, 0, 0);
    sizer_2_copy->Add(BoxAxis5, 0, 0, 0);
    sizer_2_copy->Add(BoxAxis6, 0, 0, 0);
    sizer_2->Add(sizer_2_copy, 1, wxEXPAND, 0);
    panel_1->SetSizer(sizer_2);
    grid_sizer_1->Add(panel_1, 1, wxEXPAND, 0);
    grid_sizer_1->Add(label_1_copy_1, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 0);
    ControllerPositionSizer->Add(ControllerPitchLabel, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 0);
    ControllerPitchPanel->SetSizer(ControllerPositionSizer);
    grid_sizer_1->Add(ControllerPitchPanel, 1, wxEXPAND, 0);
    grid_sizer_1->Add(label_1_copy_4, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 0);
    ControllerPositionSizer_copy->Add(ControllerRollLabel, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 0);
    ControllerPositionPanel_copy->SetSizer(ControllerPositionSizer_copy);
    grid_sizer_1->Add(ControllerPositionPanel_copy, 1, wxEXPAND, 0);
    grid_sizer_1->Add(label_1_copy, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 0);
    ControllerPositionSizer_copy_1->Add(ControllerYawLabel, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 0);
    ControllerPositionPanel_copy_1->SetSizer(ControllerPositionSizer_copy_1);
    grid_sizer_1->Add(ControllerPositionPanel_copy_1, 1, wxEXPAND, 0);
    ControllerSizer->Add(grid_sizer_1, 1, wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 0);
    ControllerBox->Add(ControllerSizer, 1, wxEXPAND, 0);
    ControllerBox->Add(20, 20, 0, 0, 0);
    sizer_1->Add(label_2, 0, wxALIGN_CENTER_VERTICAL, 0);
    sizer_1->Add(RollAxisCtrl, 0, 0, 0);
    RollBox->Add(sizer_1, 0, wxALIGN_CENTER_HORIZONTAL, 0);
    RollBox->Add(RollPolarityBox, 0, wxALIGN_CENTER_HORIZONTAL, 0);
    ControllerBox->Add(RollBox, 0, wxEXPAND, 0);
    ControllerBox->Add(20, 20, 0, 0, 0);
    sizer_1_copy->Add(label_2_copy, 0, wxALIGN_CENTER_VERTICAL, 0);
    sizer_1_copy->Add(PitchAxisCtrl, 0, 0, 0);
    PitchBox->Add(sizer_1_copy, 0, wxALIGN_CENTER_HORIZONTAL, 0);
    PitchBox->Add(PitchPolarityBox, 0, wxALIGN_CENTER_HORIZONTAL, 0);
    ControllerBox->Add(PitchBox, 0, wxEXPAND, 0);
    ControllerBox->Add(20, 20, 0, 0, 0);
    sizer_1_copy_1->Add(label_2_copy_1, 0, wxALIGN_CENTER_VERTICAL, 0);
    sizer_1_copy_1->Add(YawAxisCtrl, 0, 0, 0);
    YawBox->Add(sizer_1_copy_1, 0, wxALIGN_CENTER_HORIZONTAL, 0);
    YawBox->Add(YawPolarityBox, 0, wxALIGN_CENTER_HORIZONTAL, 0);
    ControllerBox->Add(YawBox, 0, wxEXPAND, 0);
    TopSizer->Add(ControllerBox, 1, wxEXPAND, 0);
    TopSizer->Add(20, 20, 0, 0, 0);
    TopSizer->Add(20, 20, 0, 0, 0);
    TopSizer->Add(20, 20, 0, 0, 0);
    TopSizer->Add(20, 20, 0, 0, 0);
    TopSizer->Add(20, 20, 0, 0, 0);
    sizer_6->Add(20, 20, 1, 0, 0);
    sizer_6->Add(DefaultButton, 0, 0, 0);
    sizer_6->Add(20, 20, 0, 0, 0);
    sizer_6->Add(SetButton, 0, 0, 0);
    sizer_6->Add(20, 20, 1, 0, 0);
    TopSizer->Add(sizer_6, 1, wxEXPAND, 0);
    TopSizer->Add(20, 20, 0, 0, 0);
    TopSizer->Add(20, 20, 0, 0, 0);
    TopSizer->Add(20, 20, 0, 0, 0);
    TopSizer->Add(20, 20, 0, 0, 0);
    TopSizer->Add(20, 20, 0, 0, 0);
    StatusSizer->Add(StatusLabel, 0, 0, 0);
    StatusPanel->SetSizer(StatusSizer);
    TopSizer->Add(StatusPanel, 1, wxEXPAND, 0);
    TopSizer->Add(20, 20, 0, 0, 0);
    SetSizer(TopSizer);
    TopSizer->Fit(this);
    Layout();
    // end wxGlade
}



class yaAcaAppClass: public wxApp {
public:
    bool OnInit();
};

IMPLEMENT_APP(yaAcaAppClass)

static wxApp *App;

bool yaAcaAppClass::OnInit()
{
    int i;

    App = this;

    printf ("yaACA2 Apollo ACA simulation, ver " VER(NVER) ", built " __DATE__ " " __TIME__ "\n");
    printf ("Copyright 2009 by Ronald S. Burkey\n");
    printf ("Refer to http://www.ibiblio.org/apollo/index.html for more information.\n");
	  
    Portnum = 19803;  
    for (i = 1; i < argc; i++)
    {
      wxString Arg = argv[i];
      wxString ArgStart = Arg.BeforeFirst ('=');
      wxString ArgEnd = Arg.AfterFirst ('=');
      
      if (ArgStart.IsSameAs (wxT ("--ip")))
	{
	  strcpy (NonDefaultHostname, ArgEnd.char_str ());
	  Hostname = NonDefaultHostname;
	}
      else if (ArgStart.IsSameAs (wxT ("--port")))
        {
	  long lPortnum;
	  ArgEnd.ToLong (&lPortnum);
	  Portnum = lPortnum;
	  if (Portnum <= 0 || Portnum >= 0x10000)
	    {
	      printf ("The --port switch is out of range.  Must be 1-64K.\n");
	      goto Help;
	    }
	}
      else if (ArgStart.IsSameAs (wxT ("--delay")))
        {
	  long lj;
	  ArgEnd.ToLong (&lj);
          StartupDelay = lj;
	}
      else if (ArgStart.IsSameAs (wxT ("--controller")))
        {
	  long lj;
	  ArgEnd.ToLong (&lj);
	  if (lj < 0 || lj > 1)
	    {
	      wxMessageBox (wxT ("Only --controller=0 and --controller=1 are allowed."),
	      		    wxT ("Error"), wxICON_ERROR);
	      goto Help;
	    }
	  else
            ControllerNumber = lj;
	}
      else
        {
	Help:
	  printf ("USAGE:\n");
	  printf ("\tyaACA2 [OPTIONS]\n");
	  printf ("The available options are:\n");
	  printf ("--ip=Hostname\n");
	  printf ("\tThe yaACA2 program and the yaAGC Apollo Guidance Computer simulation\n");
	  printf ("\texist in a \"client/server\" relationship, in which the yaACA2 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 yaACA2 and yaAGC are running on the same computer.\n");
	  printf ("--port=Portnumber\n");
	  printf ("\tBy default, yaACA2 attempts to connect to the yaAGC program using port\n");
	  printf ("\tnumber %d.  However, if more than one instance of yaACA2 is being\n",
	          Portnum);
	  printf ("\trun, or if yaAGC has been configured to listen on different ports, then\n");
	  printf ("\tdifferent port settings for yaACA2 are needed.  Note that by default,\n");
	  printf ("\tyaAGC listens for new connections on ports %d-%d.\n",
	          19697, 19697 + 10 - 1);
	  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 yaACA2 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 at\n");
	  printf ("\tstart-up.\n");
	  printf ("--roll=...\n");
	  printf ("--pitch=...\n");
	  printf ("--yaw=...\n");
	  printf ("\tThese options are accepted for backward-compatibility with yaACA, the\n");
	  printf ("\tpredecessor to yaACA2, but are ignored.\n");
	  printf ("--controller=N\n");
	  printf ("\tIn case there are two joystick controllers attached, this allows\n");
	  printf ("\tselection of just one of them.  The default is N=0, but N=1 is also\n");
	  printf ("\tallowed.  If there are more than two attached, only the first two can\n");
	  printf ("\tbe accessed.\n");
	  exit (1);
	}	
    }
    
    wxInitAllImageHandlers();
    yaAcaFrame = new yaAcaFrameClass(NULL, wxID_ANY, wxEmptyString);
    SetTopWindow(yaAcaFrame);
    
    yaAcaFrame->Timer = new TimerClass ();
    yaAcaFrame->Timer->Initialization = 0;
    yaAcaFrame->Timer->Start (PULSE_INTERVAL);

    yaAcaFrame->Show();
    return true;
}


// This function translates a raw joystick reading to the -57..+57 range expected by AGC.
void
TimerClass::Translate (Axis_t *AxisPtr)
{	  
  int i, Axis;
  bool PositiveSense;
  if (Initialization < 2)
    i = 0;
  else
    {
      i = AxisPtr->LastRawReading;
      Axis = AxisPtr->Axis;
      PositiveSense = AxisPtr->PositiveSense;
      // Center around midpoint.
      i -= Axes[Axis].Midpoint;
      // Polarity.
      if (!PositiveSense)
	i = -i;
      // Cut out the deadzone around the midpoint.
      if (i > 0)
	{
	  i -= Axes[Axis].Deadzone;
	  if (i < 0)
	    i = 0;
	}
      else if (i < 0)
	{
	  i += Axes[Axis].Deadzone;
	  if (i > 0)
	    i = 0;
	}
      // Scale.
      i /= Axes[Axis].Divisor;
      // Sanity check.
      if (i > 57)
	i = 57;
      else if (i < -57)
	i = -57;
    }
  // Output!
  AxisPtr->CurrentAdjustedReading = i;
}


void yaAcaFrameClass::TimerStop(wxCloseEvent &event)
{
  Timer->Stop ();
  delete Timer;
  //event.Skip ();
  Close ();
}


//-------------------------------------------------------------------------
// This function is called every PULSE_INTERVAL milliseconds.  It manages
// the server connection, and causes display-updates based on input from
// yaAGC.

void
TimerClass::Notify ()
{
  int i;

  // Service the joystick.  In all cases, the idea here is that yaAcaFrame's
  // Roll, Pitch, and Yaw structures are updated with current information
  // with respect to the joystick hardware and/or joystick drivers.
  ServiceJoystick_wxWidgets ();
  
  // Convert the readings to the form expected by yaAcaFrame, update screen,
  // output to yaAGC.
  yaAcaFrame->UpdateJoystick ();
  
  // Service the socket connection.
  
  if (StartupDelay > 0)
    {
      StartupDelay -= PULSE_INTERVAL;
      return;
    }
  // Try to connect to the server (yaAGC) if not already connected.
  if (ServerSocket == -1)
    {
      ServerSocket = CallSocket (Hostname, Portnum);
      if (ServerSocket != -1)
        printf ("yaACA2 is connected, socket=%d.\n", ServerSocket);
    }
    
  if (ServerSocket != LastServerSocket)
    {
      if (ServerSocket == -1)
        yaAcaFrame->StatusLabel->SetLabel (wxT ("Waiting for connection to yaAGC ..."));
      else
        yaAcaFrame->StatusLabel->SetLabel (wxT ("Connected to yaAGC"));
      LastServerSocket = ServerSocket;
    }
    
}


// Service the joystick using the wxWidgets toolkit.
void
TimerClass::ServiceJoystick_wxWidgets (void)
{
  static wxJoystick *Joystick = NULL;
  int i;
  
  // If no joystick has been attached yet, then try to attach one.  There is,
  // unfortunately, no corresponding method of determining that the joystick
  // has become *disconnected*, since Joystick->IsOk() seems to stubbornly keep
  // returning whatever value it had when the wxJoystick object was created.
  // We only check for connections about once per second.
  if (Joystick == NULL)	// No joystick found so far?
    {
      if (JoystickConnectionTimeout <= 0)
        {
	  JoystickConnectionTimeout = 1000;
	  if (ControllerNumber == 0)
	    Joystick = new wxJoystick (wxJOYSTICK1);
	  else
	    Joystick = new wxJoystick (wxJOYSTICK2);
	  if (!Joystick->IsOk ())
	    {
	      delete Joystick;
	      Joystick = NULL;
	    }
	  else
	    {
	      // Get the parameters of the joystick controller.
	      yaAcaFrame->ControllerModelLabel->SetLabel (Joystick->GetProductName ());
	      i = 0; 
	      Axes[i].Has = true;
	      yaAcaFrame->BoxAxis0->SetValue (true);
	      Axes[i].Minimum = Joystick->GetXMin ();
	      Axes[i].Maximum = Joystick->GetXMax ();
	      Axes[i].CurrentRawReading = 0;
	      i++;
	      yaAcaFrame->BoxAxis1->SetValue (true);
	      Axes[i].Has = true;
	      Axes[i].Minimum = Joystick->GetYMin ();
	      Axes[i].Maximum = Joystick->GetYMax ();
	      Axes[i].CurrentRawReading = 0;
	      i++;
	      Axes[i].Has = Joystick->HasZ ();
	      if (Axes[i].Has)
	        {
		  yaAcaFrame->BoxAxis2->SetValue (true);
		  Axes[i].Minimum = Joystick->GetZMin ();
		  Axes[i].Maximum = Joystick->GetZMax ();
	        }
	      Axes[i].CurrentRawReading = 0;
	      i++;
	      Axes[i].Has = Joystick->HasU ();
	      if (Axes[i].Has)
	        {
		  yaAcaFrame->BoxAxis3->SetValue (true);
		  Axes[i].Minimum = Joystick->GetUMin ();
		  Axes[i].Maximum = Joystick->GetUMax ();
	        }
	      Axes[i].CurrentRawReading = 0;
	      i++;
	      Axes[i].Has = Joystick->HasV ();
	      if (Axes[i].Has)
	        {
		  yaAcaFrame->BoxAxis4->SetValue (true);
		  Axes[i].Minimum = Joystick->GetVMin ();
		  Axes[i].Maximum = Joystick->GetVMax ();
	        }
	      Axes[i].CurrentRawReading = 0;
	      i++;
	      Axes[i].Has = Joystick->HasRudder ();
	      if (Axes[i].Has)
	        {
		  yaAcaFrame->BoxAxis5->SetValue (true);
		  Axes[i].Minimum = Joystick->GetRudderMin ();
		  Axes[i].Maximum = Joystick->GetRudderMax ();
	        }
	      Axes[i].CurrentRawReading = 0;
	      i++;
	      Axes[i].Has = Joystick->HasPOVCTS ();
	      if (Axes[i].Has)
	        {
		  yaAcaFrame->BoxAxis6->SetValue (true);
		  Axes[i].Minimum = 0;
		  Axes[i].Maximum = 35999;
	        }
	      Axes[i].CurrentRawReading = 0;
	      
	      // Compute parameters that derive from Maximum and Minimum.
	      for (i = 0; i < NUM_AXES; i++)
	        if (Axes[i].Has)
		  {
		    Axes[i].Midpoint = (Axes[i].Minimum + Axes[i].Maximum) / 2;
		    Axes[i].Deadzone = (Axes[i].Maximum - Axes[i].Minimum) / 20;
		    Axes[i].Divisor = ((Axes[i].Maximum - Axes[i].Minimum) * 9 + 10 * 57) / (20 * 57);
		  }
		else
		  {
		    Axes[i].Midpoint = 0;
		    Axes[i].Deadzone = 13;
		    Axes[i].Divisor = 2;
		  }
	      
	      // Make the current position will update ASAP.
	      yaAcaFrame->Roll.LastRawReading = 100000;
	      yaAcaFrame->Pitch.LastRawReading = 100000;
	      yaAcaFrame->Yaw.LastRawReading = 100000;
	      
#ifndef __APPLE__	  
	      // For reasons I can't fathom, this causes the entire frame to 
	      // blank out on Mac OS X.    
	      yaAcaFrame->Hide ();
	      yaAcaFrame->Fit ();
	      yaAcaFrame->Show ();
#endif	      
	      
	      Initialization = 2;
	    }
	}
      else
        {
	  JoystickConnectionTimeout -= PULSE_INTERVAL;
	}
    }
    
  // Find the current position.
  if (Joystick != NULL)
    {
      // Read all of the axes. 
      wxPoint Position;
      Position = Joystick->GetPosition ();
      Axes[AXIS_X].CurrentRawReading = Position.x;
      Axes[AXIS_Y].CurrentRawReading = Position.y;
      if (Axes[AXIS_Z].Has)
        Axes[AXIS_Z].CurrentRawReading = Joystick->GetZPosition ();
      if (Axes[AXIS_U].Has)
        Axes[AXIS_U].CurrentRawReading = Joystick->GetUPosition ();
      if (Axes[AXIS_V].Has)
        Axes[AXIS_V].CurrentRawReading = Joystick->GetVPosition ();
      if (Axes[AXIS_RUDDER].Has)
        Axes[AXIS_RUDDER].CurrentRawReading = Joystick->GetRudderPosition ();
	
    }
}


// Output joystick positions to yaAGC.
static void
OutputTranslated (int Channel, int Value)
{
  unsigned char Packet[4];
  int j;
  if (ServerSocket == -1)
    return;
  if (Value < 0)			// Make negative values 1's-complement.
    Value = (077777 & (~(-Value)));
  FormIoPacket (Channel, Value, Packet);
  j = send (ServerSocket, (const char *) Packet, 4, MSG_NOSIGNAL);
  if (j == SOCKET_ERROR && SOCKET_BROKEN)
    {
      close (ServerSocket);
      ServerSocket = -1;
      fprintf (stdout, "\nServer connection lost.\n");
    }
}


// Update screen and/or output changed joystick data to yaAGC.
void
yaAcaFrameClass::UpdateJoystick (void)
{
  static int LastAdjustedRoll = 1000, LastAdjustedPitch = 1000, LastAdjustedYaw = 1000;
  static int Last31 = -1;
  int New31, j;
  
  if (Roll.LastRawReading != Timer->Axes[Roll.Axis].CurrentRawReading)
    {	
      Roll.LastRawReading = Timer->Axes[Roll.Axis].CurrentRawReading;
      Timer->Translate (&Roll);
      ControllerRollLabel->SetLabel (wxString::Format (wxT ("%d"), Roll.CurrentAdjustedReading));
    }
  if (Pitch.LastRawReading != Timer->Axes[Pitch.Axis].CurrentRawReading)
    {	
      Pitch.LastRawReading = Timer->Axes[Pitch.Axis].CurrentRawReading;
      Timer->Translate (&Pitch);
      ControllerPitchLabel->SetLabel (wxString::Format (wxT ("%d"), Pitch.CurrentAdjustedReading));
    }
  if (Yaw.LastRawReading != Timer->Axes[Yaw.Axis].CurrentRawReading)
    {	
      Yaw.LastRawReading = Timer->Axes[Yaw.Axis].CurrentRawReading;
      Timer->Translate (&Yaw);
      ControllerYawLabel->SetLabel (wxString::Format (wxT ("%d"), Yaw.CurrentAdjustedReading));
    }
    
  // Report to yaAGC.
  New31 = 077777;
  if (Roll.CurrentAdjustedReading < 0)
    New31 &= ~040040;
  else if (Roll.CurrentAdjustedReading > 0)
    New31 &= ~040020;
  if (Yaw.CurrentAdjustedReading < 0)
    New31 &= ~040010;
  else if (Yaw.CurrentAdjustedReading > 0)
    New31 &= ~040004;
  if (Pitch.CurrentAdjustedReading < 0)
    New31 &= ~040002;
  else if (Pitch.CurrentAdjustedReading > 0)
    New31 &= ~040001;
  if (ServerSocket != -1 && New31 != Last31)
    {
      unsigned char Packet[8];
      Last31 = New31;
      // First, create the mask which will tell the CPU to only pay attention to
      // relevant bits of channel (031).
      FormIoPacket (0431, 040077, Packet);
      FormIoPacket (031, New31, &Packet[4]);
      // And, send it all.
      j = send (ServerSocket, (const char *) Packet, 8, MSG_NOSIGNAL);
      if (j == SOCKET_ERROR && SOCKET_BROKEN)
        {
	  close (ServerSocket);
	  ServerSocket = -1;
          fprintf (stdout, "\nServer connection lost.\n");
	  return;
	}
    }
    
  if (Roll.CurrentAdjustedReading != LastAdjustedRoll)
    {
      OutputTranslated (0170, LastAdjustedRoll = Roll.CurrentAdjustedReading);
    }
  if (Pitch.CurrentAdjustedReading != LastAdjustedPitch)
    {
      OutputTranslated (0166, LastAdjustedPitch = Pitch.CurrentAdjustedReading);
    }
  if (Yaw.CurrentAdjustedReading != LastAdjustedYaw)
    {
      OutputTranslated (0167, LastAdjustedYaw = Yaw.CurrentAdjustedReading);
    }
    
}







