/****************************************************************************/
/* Copyright (c) 2008 MBARI                                                 */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Summary  : Command line utility for manipulating XPort GPIO pins         */
/* Filename : GWiz.cc                                                       */
/* Author   : K Headley                                                     */
/* Project  : Benthic Imaging AUV                                           */
/* Created  : 04/01/2008                                                    */
/****************************************************************************/
/* Modification History:                                                    */
/*  $Id: GWiz.cc,v 1.12 2012/04/06 01:13:06 headley Exp $    */
/*  $Name: FastSim $  */
/****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>
#include <unistd.h>
#include <signal.h>

#include "XPort.h"
#include "XPortGPIO.h"
#include "BicamConsts.h"
#include "Utils.h"

#ifdef QNX
using namespace std;
#endif

unsigned int activeLevel=CFG_GPIO_ACTIVE_HI;
unsigned int selectMask=CFG_GPIO_ALL;
unsigned int testUint;
int nCycles=1;
int cycles=0;
long inum=0;
double frac=0.0;
double waitTimeSec=1;
double testWaitTimeSec=0.0;
double onTimeSec=0.0;
double  testOnTimeSec=0.0;
boolean verbose=FALSE;
boolean doActivate=FALSE;
boolean doDeactivate=FALSE;
boolean ignoreErrors=FALSE;
XPortResponse xpResponse;
XPortCommand *setDirection;
XPortCommand *setActiveLevel;
XPortGPIO gpio;
XPortGPIO *pgpio=NULL;
XPort *pxport=NULL;

/* 
   local function definitions 
*/
void terminate (int param)
{
  cerr<<"Interrupted by user...deactivating GPIO"<<endl;
  if(pxport!=NULL){
    if(pgpio!=NULL){
      pgpio->setInactive();
    }
    pxport->disconnectxp(XPCON_ALL);
  }
  cerr<<"done"<<endl;
  exit(1);
}

void printUsage(char *prog){
  char *cp=NULL;
  char *np=prog;

  cp=strtok(prog,"\\/");
  while(cp!=NULL){
    np=cp;
    cp=strtok(NULL,"\\/");
  }

  cout<<endl;
  cout<<np<<" - Utility to manipulate XPort GPIO"<<endl;
  cout<<endl;
  cout<<"usage: "<<np<<" [<options>...] <XPort host addresss>\n"<<endl;
  cout<<endl;
  cout<<"  --help        : print usage message"<<endl;
  cout<<"  -H [host]     : xport host to use (use last argument by default)"<<endl;
  cout<<"  -m [mask]     : set pin selection mask (hex) ["<<hex << "0x"<<selectMask<<"]"<<endl;
  cout<<"  -t [waitTime] : set activation interval      ["<<waitTimeSec<<"]"<<endl;
  cout<<"  -o [onTime]   : set activation duration      ["<<onTimeSec<<"]"<<endl;
  cout<<"  -c [cycles]   : number of cycles to repeat   ["<<nCycles<<"]"<<endl;
  cout<<"  -l,h          : GPIO outputs active LOW,HIGH ["<<(activeLevel==CFG_GPIO_ACTIVE_HI?"HIGH":"LOW")<<"]"<<endl;
  cout<<"  -a            : set GPIO active and exit"<<endl;
  cout<<"  -i            : set GPIO inactive and exit"<<endl;
  cout<<"  -k            : keep going, ignore errors    ["<<(ignoreErrors?"TRUE":"FALSE")<<"]"<<endl;
  cout<<"  -v            : verbose output               ["<<(verbose?"TRUE":"FALSE")<<"]"<<endl;
  
  cout<<endl;
  cout<<"where"<<endl;
  cout<<"  host     - host name or IP address"<<endl;
  cout<<"  waitTime - time between GPIO deactivation and activation (decimal sec)"<<endl;
  cout<<"  onTime   - time between GPIO activation and deactivation (decimal sec)"<<endl;
  cout<<"  cycles   - number of cycles; use -1 to run forever"<<endl;
  cout<<endl;
}

/* Main Entry Point */
int main (int argc, char *argv[]){

  int c;
  int testScan;
  char* xportHost=NULL;

  unsigned int gpioDirection=CFG_GPIO_OUT;
  int test=-1;

  if(argc < 2 || strcmp(argv[1],"--help")==0) {
    printUsage(argv[0]);
    exit(1);
  }


  /* parse command line arguments */
  opterr = 0;
  while((c= getopt(argc, argv, "ac:hH:iklm:o:t:v")) != -1 ){
    switch(c)
      {
       case 'a':
	  doActivate=TRUE;
	break;
       case 'i':
	  doDeactivate=TRUE;
	break;
       case 'k':
	  ignoreErrors=TRUE;
	break;
      case 'c':
	/* set direction */
	testScan=sscanf(optarg,"%d",&nCycles);
	if(testScan<1){
	  cerr<<"Invalid number of cycles: ["<<optarg<<"] - expecting integer value"<<endl;
	  exit(-1);
	}
	break;
       case 'h':
	  activeLevel=CFG_GPIO_ACTIVE_HI;
	break;
       case 'l':
	  activeLevel=CFG_GPIO_ACTIVE_LO;
	break;
      case 'o':
	/* set onTime (time between GPIO 
	   activation and deactivation) 
	*/
	testScan=sscanf(optarg,"%lf",&testOnTimeSec);
	if(testScan<1 || testOnTimeSec<=0.000000){
	  cerr<<"Invalid on time: ["<<optarg<<"] - expecting floating point value > 0"<<endl;
	  exit(-1);
	}
	onTimeSec=testOnTimeSec;
	break;
      case 't':
	/* set waitTime (time between GPIO activations) */
	testScan=sscanf(optarg,"%lf",&testWaitTimeSec);
	if(testScan<1 || testWaitTimeSec<=0.000000){
	  cerr<<"Invalid wait time: ["<<optarg<<"] - expecting floating point value > 0"<<endl;
	  exit(-1);
	}
	waitTimeSec=testWaitTimeSec;
	break;
      case 'm':
	/* set waitTime (time between GPIO activations) */
	testScan=sscanf(optarg,"%x",&testUint);
	if(testScan<1){
	  cerr<<"Invalid wait time: ["<<optarg<<"] - expecting hex mask value"<<endl;
	  exit(-1);
	}
	selectMask=testUint&0xFFFF;
	break;
      case 'H':
	xportHost=optarg;
	if(strlen(xportHost)>MAXHOSTNAMELEN){
	  cerr<<"host name exceeds MAXHOSTNAMELEN ("<<MAXHOSTNAMELEN<<")"<<endl;
	  exit(1);
	}
	cout<<"host set to "<<xportHost<<endl;
	break;
      case 'v':
	verbose=TRUE;
	break;
      default:
	cerr<<"Unknown option: ["<<optopt<<"]"<<endl;
	exit(-1);
	break;
      }

  }

  if((argc-1)>=optind){
    xportHost=argv[argc-1];
  }else
  if(xportHost==NULL){
    cout<<"argc="<<argc<<" optind="<<optind<<" host=NULL"<<endl;
    cerr<<endl<<"No XPort host specified"<<endl;
    printUsage(argv[0]);
    exit(1);
  }

  cout<<endl;

  /* Validate settings */
  if(doActivate==TRUE && doDeactivate==TRUE){
    cerr<<endl;
    cerr<<"#"<<endl;
    cerr<<"# Invalid Arguments: Can not use both -i and -a arguments"<<endl;
    cerr<<"#"<<endl;
    cerr<<endl;
    exit(-1);
  }

  /* Show settings */
  if(verbose==TRUE){
    cout<<endl;
    cout<<"host="<<xportHost<<endl;
    cout<<"mask="<<hex<<"0x"<<selectMask<<endl;
    cout<<"nCycles="<<nCycles<<endl;
    cout<<"waitTimeSec="<<waitTimeSec<<" s"<<endl;
    cout<<"onTimeSec="<<onTimeSec<<" s"<<endl;
    cout<<"gpioDirection="<<(gpioDirection==CFG_GPIO_OUT?"OUTPUT":"INPUT")<<endl;
    cout<<"doActivate="<<(doActivate==TRUE?"TRUE":"FALSE")<<endl;
    cout<<"doDeactivate="<<(doDeactivate==TRUE?"TRUE":"FALSE")<<endl;
    cout<<"ignoreErrors="<<(ignoreErrors==TRUE?"TRUE":"FALSE")<<endl;
    cout<<endl;
  }

  /* trap SIGINT to deactivate outputs on CTRL-C */
  signal(SIGINT,terminate);

  /* Create XPort instance*/
  XPort xport(xportHost,
	      GPIO_IP_PORT,SERIAL_IP_PORT1,
	      selectMask,gpioDirection,
	      selectMask,activeLevel);
   
  /* Set global reference to XPortGPIO for use by signal trap */
  pxport=&xport;

   /* connect to XPort, don't set GPIO*/
  test=xport.connectxp(XPCON_ALL,FALSE);
  if(test!=0){
    cout<<"Could not connect to XPort; return code ("<<test<<")"<<endl;
    if(ignoreErrors==FALSE)
      exit(-1);
  }

  //  Configure a XPortGPIO to control GPIO pin(s)
  //  Constructor arguments:
  //  an XPort reference
  //  GPIO pin select mask (which pins to activate when triggering camera)
  //  GPIO pin direction mask (which pins to set as outputs(1)/inputs(0))
  //  GPIO pin active level mask (which pins to set active HI(0)/active LO(1))
  //  GPIO pin state mask (which pins to set active(1)/inactive(0)))
   gpio.configure(&xport,
		  selectMask,
		  gpioDirection,
		  activeLevel,
		  selectMask);

  /* Set global reference to XPortGPIO for use by signal trap */
   pgpio=&gpio;

  /* Configure GPIO as OUTPUT */   
  if(verbose){
    cout<<"setting GPIO direction "<<(gpioDirection==CFG_GPIO_OUT?"OUTPUT":"INPUT")<<endl;
  }
  gpio.setDirection(selectMask,gpioDirection,&xpResponse);

  /* Configure GPIO active levels */
  if(verbose){
    cout<<"setting active level "<<(activeLevel==CFG_GPIO_ACTIVE_HI?"active HI":"active LOW")<<endl;
  }
  gpio.setActiveLevel(selectMask,activeLevel,&xpResponse);


  /* If doDeactivate is TRUE, switch off and exit 
     (do this first; it shouldn't ever have both set,
     but if it does, it may be better to turn off
     than on)
  */
  if(doDeactivate==TRUE){
    cout<<"Deactivating GPIO outputs and exiting"<<endl;
    gpio.setInactive();
    xport.disconnectxp(XPCON_ALL);
    exit(0);
  }

  /* If doActivate is TRUE, switch on and exit */
  if(doActivate==TRUE){
    cout<<"Activating GPIO outputs and exiting"<<endl;
    gpio.setActive();
    xport.disconnectxp(XPCON_ALL);
    exit(0);
  }

  /* Initializes GPIO inactive (OFF) */
  if(verbose){
    cout<<"initializing GPIO outputs INACTIVE"<<endl;
  }
  gpio.setInactive();

  /* Start turning on and off at the specified rate.
     Note that this loop is endless when nCycles<0.
     Otherwise, the loop terminates after completing
     nCycles cycles.
  */
  for(cycles=0;(cycles<nCycles)||(nCycles<0);cycles++){
    // wait for specifed delay
    delay(waitTimeSec);

    if(verbose){
      cout<<" activating GPIO"<<endl;
    }
    gpio.setActive();

    // wait for specified on-time
    delay(onTimeSec);

    if(verbose){
      cout<<" deactivating GPIO"<<endl;
    }

    // deactivate outputs
    gpio.setInactive();

  }

  // deactivate outputs before exiting
  gpio.setInactive();

  cout<<endl;

  // disconnect the XPort
  xport.disconnectxp(XPCON_ALL);

  return 0;
  
}
