/**************************************************************************/
/*                                                                        */
/*  svgafft                                                               */
/*                                                                        */
/* SVGAlib based spectrum analyzer                                        */
/*                                                                        */ 
/* main entry point                                                       */
/*                                                                        */
/**************************************************************************/

/**************************************************************************

    Copyright (C) 1995 Andrew Veliath

    This program 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.

    This program 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 this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.


***************************************************************************/

// $Id: svgafft.cc,v 0.11 1995/05/02 01:17:55 drewvel Exp $             

/*******************************************************************/
#include <iostream.h>
#include <stdlib.h>
#include <string.h>
#include <vga.h>
#include "analyzer.h"
#include "gconfig.h"
#include "keymap.h"

/*******************************************************************/
extern globalConfig gConfig, gStore;
extern char *gProgInfo;
extern aScreen Screens[];
globalConfig gDefaults;


/*******************************************************************/
void PrintInfo()
{
  cout << endl << gProgInfo << endl;
}


#define BoolValue(a) ((a)?"on":"off")
/*******************************************************************/
void PrintConfig(globalConfig& config)
{
  int i;
  StrOption *p;
  IntOption *q;
  RealOption *r;
  BoolOption *b;

  p=(StrOption *) &config;
  for (i=0; i<NUMSTROPTIONS; i++) {
    cerr.width(15);
    cerr.setf(ios::left);
    cerr << p->name << "\t";
    cerr.width(12);
    cerr << p->val << endl;
    p++;
  }
  
  q=(IntOption *) p;
  for (i=0; i<NUMINTOPTIONS; i++) {
    cerr.width(15);
    cerr << q->name << "\t";
    cerr.width(12);
    cerr.setf(ios::left);
    cerr << q->val << endl;
    q++;
  }

  r=(RealOption *) q;
  for (i=0; i<NUMREALOPTIONS; i++) {
    cerr.width(15);
    cerr << r->name << "\t";
    cerr.width(12);
    cerr.setf(ios::left);
    cerr << r->val << endl;
    r++;
  }

  b=(BoolOption *) r;
  for (i=0; i<NUMBOOLOPTIONS; i++) {
    cerr.width(15);
    cerr << b->name << "\t";
    cerr.width(12);
    cerr.setf(ios::left);
    cerr << BoolValue(b->val) << endl;
    b++;
  }
}


/*******************************************************************/
void PrintNonDefaultConfig(globalConfig& config, ostream& out)
{
  int i;
  StrOption *p, *p1;
  IntOption *q, *q1;
  RealOption *r, *r1;
  BoolOption *b, *b1;

  p=(StrOption *) &config;
  p1=(StrOption *) &gDefaults;
  for (i=0; i<NUMSTROPTIONS; i++) {
    if (strcmp(p1->val, p->val)!=0) {
      out << p->name << " ";
      out << p->val << endl;
    }
    p++; p1++;
  }
  
  q=(IntOption *) p;
  q1=(IntOption *) p1;
  for (i=0; i<NUMINTOPTIONS; i++) {
    if (q->val!=q1->val) {
      out << q->name << " ";
      out << q->val << endl;
    }
    q++; q1++;
  }

  r=(RealOption *) q;
  r1=(RealOption *) q1;
  for (i=0; i<NUMREALOPTIONS; i++) {
    if (r->val!=r1->val) {
      out << r->name << " ";
      out << r->val << endl;
    }
    r++; r1++;
  }

  b=(BoolOption *) r;
  b1=(BoolOption *) r1;
  for (i=0; i<NUMBOOLOPTIONS; i++) {
    if (b->val != b1->val) 
      out << b->name << endl;
    
    b++; b1++;
  }
}


/*******************************************************************/
void NoDevice()
{
  cerr << 
    "Error:  No device specified, " << 
      "use option -device <device> to specify." << endl;
  cerr << "Eg. svgafft -device /dev/dsp" << endl << endl;
}


/*******************************************************************/
void Usage()
{
  cerr << "\nUsage:  svgafft [parameters] -device <device>" << endl <<
    "Type `svgafft --parms' to display available parameters and defaults."
    << endl << "See man page for more info." << endl << endl;
  cerr << endl;
}


/*******************************************************************/
void ProcessBools(globalConfig& config)
{
  // if Full-Screen mode specified then 
  // adjust parameters 
  if (config.FullScreen.val) {
    config.x1.val=1; 
    config.y1.val=1;
    config.x2.val=Screens[config.Screen.val].w-2; 
    config.y2.val=Screens[config.Screen.val].h-2; 
    config.NoText.val=true;
  };

  int w, h;
  w=config.x2.val-config.x1.val; 
  h=config.y2.val-config.y1.val; 

  if (!config.FullScreen.val) {
    int wtst=Screens[config.Screen.val].x2-Screens[config.Screen.val].x1;
    int htst=Screens[config.Screen.val].y2-Screens[config.Screen.val].y1;
    if (w>wtst) {
      config.x1.val=Screens[config.Screen.val].x1;
      config.x2.val=Screens[config.Screen.val].x2;
    }

    if (h>htst) {
      config.y1.val=Screens[config.Screen.val].y1;
      config.y2.val=Screens[config.Screen.val].y2;
    }
  }

  w=config.x2.val-config.x1.val; 
  h=config.y2.val-config.y1.val; 

  if (config.Continuous.val && (config.PlotType.val!=1)) {    
    cerr << w << " >= ~" << (config.SampleSize.val/4+100);
    if (w>=(config.SampleSize.val/4+100)) {
      cerr << "\nThe -continuous option works much better if you set" << endl
	<< "the size to a ^2 value to over approximately 4 times the" << endl
	<< "size of the graph width." << endl << endl;
      exit(0);
    } else cerr << " -- check." << endl;
  }


  if (config.Continuous.val) {
    config.BarWidth.val=1;
    config.SegSpace.val=0;
    config.SegHeight.val=1;
    config.NumBands.val=w;
  }

  if (config.NumBands.val>w) config.NumBands.val=w;
}


/*******************************************************************/
boolean ProcessArgs(int argc, char *args[], 
		    globalConfig& config)
#define CheckRange(a) {if ((a)>=argc) { Usage(); exit(0); }}
{
  int i, j;
  StrOption *p;
  IntOption *q;
  RealOption *r;
  BoolOption *b;
  boolean knownarg;

  if (strcmp(args[1], "--help")==0) 
    return false;  

  i=1; //argc--;
  while (i<argc) {
    knownarg=false;
    
    // check for string options
    p=(StrOption *) &config;
    for (j=0; j<NUMSTROPTIONS && !knownarg; j++) {
      if (strcmp(args[i], p->name)==0) {
	i++;
	CheckRange(i);
	strcpy(p->val, args[i]);
	knownarg=true;
      }
      p++;
    }
    
    // check for int options
    q=(IntOption *) p;
    for (j=0; j<NUMINTOPTIONS && !knownarg; j++) {
      if (strcmp(args[i], q->name)==0) {
	i++;
	CheckRange(i);
	q->val=atoi(args[i]);
	knownarg=true;
      }
      q++;
    }

    // check for real options
    r=(RealOption *) q;
    for (j=0; j<NUMREALOPTIONS && !knownarg; j++) {
      if (strcmp(args[i], r->name)==0) {
	i++;
	CheckRange(i);
	r->val=atof(args[i]);
	knownarg=true;
      }
      r++;
    }

    // check for bool options
    b=(BoolOption *) r;
    for (j=0; j<NUMBOOLOPTIONS && !knownarg; j++) {
      if (strcmp(args[i], b->name)==0) {
	b->val=true;
	knownarg=true;
      }
      b++;
    }

    if (!knownarg) { 
      cerr << "Error: unknown parameter " << args[i] << endl <<
	"use --help for help" << endl;
      return false;
    }

    i++;
  }  

  if (config.Attenuation.val<=0) 
    if (config.Bits8.val) config.Attenuation.val=DEFATTENUATION_8;
    else config.Attenuation.val=DEFATTENUATION_16;  

  if (config.x1.val<0) 
    config.x1.val=Screens[config.Screen.val].x1;

  if (config.y1.val<0) 
    config.y1.val=Screens[config.Screen.val].y1;

  if (config.x2.val<0) 
    config.x2.val=Screens[config.Screen.val].x2;

  if (config.y2.val<0) 
    config.y2.val=Screens[config.Screen.val].y2;

  config.Screen.val%=NUMSCREENS;

  ProcessBools(config);

  return true;
}


/*******************************************************************/
void ShowConfig(globalConfig& config)
{
  cerr << endl << "Parameters and their default values (-1 means set at runtime):" << endl;
  PrintConfig(config);
  cerr << endl << "Attenuation is set to " << DEFATTENUATION_16 << 
    " if using 16-bit mode or " << DEFATTENUATION_8 << " if in " <<
    "8-bit mode." << endl;
  cerr << endl;
}


/*******************************************************************/
int main(int argc, char *args[])
{
  PrintInfo();
  
  vga_init();

  gDefaults=gConfig;
  
  if (argc<=1) {
    NoDevice();
    exit(0);
  }
  
  if (strcmp(args[1], "--parms")==0) {
    ShowConfig(gConfig);
    exit(0);
  }

  if (ProcessArgs(argc, args, gConfig)) {    
    if (strcmp(gConfig.Device.val, NOFILENAME)!=0) {

      // construct objects
      Parms parms(gConfig.SampleSize.val, 
		  gConfig.SampleRate.val, 
		  NUMBITS); 
      
      Signal signal(gConfig.Device.val, parms);

      SpectrumAnalyzer device(gConfig.Attenuation.val, 
			      &signal, gConfig.PlotType.val);
      
      if (signal.SignalOK()) {
	cerr << endl << "Signal verified." << endl << endl;
	cerr << "*** Hit `" << K_EXIT << "' to end and `" << 
	  K_HELP << "' at runtime for help." << endl;
	gStore=gConfig;

	// go for it
	device.Analyze();

      } else {
	cerr << "Could not initiailize " << args[1] << ", aborting." 
	  << endl << "(Is it a dsp device?)" 
	    << endl << endl;
      }
    }
    else 
      NoDevice();    
  } else Usage();

  return 0;	
}
/*******************************************************************/
