/*
* xradiotrack.c V.1.1  F.W.Brinkman 1995.
*/

/* #define DEBUG1 1 */

#include "xradiotrack.h"
#include <stdio.h>
#include <math.h>

/*
 * Include files required for all Toolkit programs
*/

#include <X11/Intrinsic.h>  	/* Intrinsics Definitions */
#include <X11/StringDefs.h>	/* Standard Name-String definitions */
#include <X11/Shell.h>		/* Shell definitions */


/*
 include files for widgets we actually use in this file.
*/

#include <X11/Xaw/Command.h>    
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Box.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/Repeater.h>

/* defines */

#define MMW XtVaCreateManagedWidget

/* global declarations */

int n;
int muted = 0; /* if muted : sound is off */
int tuned = 0; /* if tuned : we've got adequate signal
                  and have indicator ON */
float lastfreq;
XtAppContext app_context;

Widget toplevel, form, frame, display, dtext;
Widget quit, onbutton, offbutton, mutebutton;
Widget volume, volplus, volminus;
Widget tuning, tunplus, tunminus, scanplus, scanminus;
Widget tunedind, mutedind;
Widget st1, st2, st3, st4, st5;
Widget st6, st7, st8, st9, st10;

/* arglists */

Arg dtextargs[9];       /* display text */
Arg stsargs[9];         /* stations */

/* ************** various aux routines ************** */

/* Routine to show if tuned, green 'T' if tuned, else black */

void ShowTuned(void)
{
if ( (tuned = IsTuned()) )
  {
    XtVaSetValues(tunedind,
                  XtVaTypedArg,XtNforeground, XtRString, "green", 6,
                  NULL);
  }
else
  {
    XtVaSetValues(tunedind,
                  XtVaTypedArg,XtNforeground, XtRString, "black", 6,
                  NULL);
  }
}

/* Routine to clear tuned indicator */

void ClearTuned(void)
{
XtVaSetValues(tunedind,
              XtVaTypedArg,XtNforeground, XtRString, "black",  6,
              NULL );
tuned = 0;
}

/* Routine to clear mute indicator */

void ClearMute(void)
{
XtVaSetValues(mutedind,
              XtVaTypedArg,XtNforeground, XtRString, "black",  6,
              NULL );
muted = 0;     /* needed for calls from various unmute causes */
}

/* ******* Mute button callback function  *********** */
void Mute(w, client_data, call_data)
Widget w;
XtPointer client_data, call_data;
{ 
   if ( (muted = CardMute()) ) 
     {
     XtVaSetValues(mutedind,
                  XtVaTypedArg,XtNforeground, XtRString, "red",  4,
                  NULL);
     }
   else ClearMute();
}

/* ******* Volume up callback function  *********** */
void VolUp(w)
Widget w;
{
   if (muted) ;          /* ignore if muted! */
   else VolumeUp();
}

/* ******* Volume  down callback function  *********** */
void VolDown(w)
Widget w;
{
   if (muted) ;          /* ignore if muted!  */
   else VolumeDown();
}

/* ******* Quit button callback function *********** */

void Quit(w, client_data, call_data)
Widget w;
XtPointer client_data, call_data;
{ 
    exit(0); 
}

/* ******* OFF callback function  *********** */
void Switchoff(w, client_data, call_data)
Widget w; XtPointer client_data, call_data;
{
   XtVaSetValues(dtext,
                 XtNlabel, "--.-- MHz",
                 NULL);
   if (muted) ClearMute();          /* switch mute indication off if req'd */
   if (tuned) ClearTuned();
   CardOff();
}

/* ******* station tuning callback ********** */
void StationTune(w, freq, call_data)
Widget w; float *freq; XtPointer call_data;
{
  lastfreq=*freq;	/* save frequency in use ! */
  Tune(*freq);
  if (muted) ClearMute();          /* switch mute indication off if req'd */
  ShowTuned();
}  

/* ******* tuning by frequency  ********** */
void TuneDown(w, client_data, call_data)
Widget w; XtPointer client_data, call_data;
{
  char buffer[10];
  if (muted) ClearMute();          /* switch mute indication off */
  if (tuned) ClearTuned();
  lastfreq=lastfreq-0.05;
  if (lastfreq < MinFreq) lastfreq=MaxFreq;
  if (lastfreq > MaxFreq) lastfreq=MaxFreq;

  Tune(lastfreq);
  sprintf(buffer,"%2.2f Mhz",lastfreq);
  n=0;
  XtSetArg(dtextargs[n], XtNlabel, buffer);n++;
  XtSetValues(dtext,dtextargs,n);
}  

void TuneUp(w, client_data, call_data)
Widget w; XtPointer client_data, call_data;
{
  char buffer[10];
  if (muted) ClearMute();          /* switch mute indication off */
  if (tuned) ClearTuned();
  lastfreq=lastfreq+0.05;
  if (lastfreq > MaxFreq) lastfreq=MinFreq;
  if (lastfreq < MinFreq) lastfreq=MinFreq;

  Tune(lastfreq);
  sprintf(buffer,"%2.2f Mhz",lastfreq);
  n=0;
  XtSetArg(dtextargs[n], XtNlabel, buffer);n++;
  XtSetValues(dtext,dtextargs,n);
}  

/* when tuning ends, show if tuned. Doing it while tuning
   would make it too slow, < 1 MHz/sec */

void TuneTuned(w, client_data, call_data)
Widget w; XtPointer client_data, call_data;
{
ShowTuned();
}

/* ****** put station name in display ******* */
void DisplayName(w, txt, call_data)
Widget w; char *txt; XtPointer call_data;
{
  n=0;
  XtSetArg(dtextargs[n], XtNlabel, txt);n++;
  XtSetValues(dtext,dtextargs,n);
}

/* ********* scanning the range untill tuned **************** */
/* Note : this is fairly unreliable due to hardware limitations ! */

void ScanUp(w)
Widget w; 
{
  char scanbuffer[10];
  int rollover = 0;
  if (tuned) ClearTuned(); 
  if (muted) ClearMute(); 
  if ( lastfreq < MinFreq ) lastfreq = MinFreq;
  while ( tuned == 0)
  {
    lastfreq = lastfreq + 0.1 ;
    if (lastfreq > MaxFreq)
    {
      lastfreq=MinFreq;
      rollover++;
      if ( rollover > 1) break; 
    }
    Tune(lastfreq);
    sprintf(scanbuffer,"%2.2f Mhz",lastfreq);
    n=0;
    XtSetArg(dtextargs[n], XtNlabel, scanbuffer);n++;
    XtSetValues(dtext,dtextargs,n);
    ShowTuned();
  }
}  

/* same for going down */

void ScanDown(w)
Widget w; 
{
  char scanbuffer[10];
  int rollover = 0;
  if (tuned) ClearTuned(); 
  if (muted) ClearMute(); 
  if ( lastfreq < MinFreq ) lastfreq = MinFreq;
  while ( tuned == 0)
  {
    lastfreq = lastfreq - 0.1 ;
    if (lastfreq < MinFreq)
    {
      lastfreq=MaxFreq;
      rollover++;
      if ( rollover > 1) break; 
    }
    Tune(lastfreq);
    sprintf(scanbuffer,"%2.2f Mhz",lastfreq);
    n=0;
    XtSetArg(dtextargs[n], XtNlabel, scanbuffer);n++;
    XtSetValues(dtext,dtextargs,n);
    ShowTuned();
  }
}

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

void main(argc, argv)
int argc;
char **argv;
{
    /* Init application context */

    toplevel = XtVaAppInitialize(
            &app_context,           /* Application context */
            "XRadiotrack",          /* Application class */
            NULL, 0,                /* command line option list */
            &argc, argv,            /* command line args */
            NULL,                   /* for missing app-defaults file */
            NULL);                  /* terminate varargs list */


    /* first see if we can init the station array and card i/o */

    n=do_init();

    /*
       In general there'll be a bunch of XSetArg ()'s after
       creating a widget. Or there may be some args
       at create time, or both. Reason is:

       - I want finegrained control, hardcoded 
       - never liked applications with a resourcefile
         too big to read
       - I'm using this program to get some idea
         of what it means to code for X, so I don't
         care if the code seems brute and unelegant
       - this is for Linux, if you don't like or understand
         it, change the code and recompile ...

    In V1.1 changed lots of setargs to sets at creationtime, and
    used SetVaArg's a lot more.

    */

    /* create form to contain all */

    form = MMW ( "form", formWidgetClass, toplevel,
                  XtVaTypedArg,XtNbackground, XtRString, "grey60", 7,
                  NULL);

    /* create set of buttons on,off,quit, mute */

    onbutton = MMW ( "ON", commandWidgetClass, form,
                     XtNhorizDistance, 4,
                     XtNvertDistance, 4,
                     XtNwidth, 57,
                     XtNheight, 15,
                     NULL);

    offbutton = MMW ( "OFF", commandWidgetClass, form,
                      XtNhorizDistance, 4,
                      XtNvertDistance, 4,
                      XtNwidth, 57,
                      XtNheight, 15,
                      XtNfromHoriz, onbutton,
                      NULL);

    quit  = MMW ( "QUIT", commandWidgetClass, form,
                   XtNhorizDistance, 4,
                   XtNvertDistance, 4,
                   XtNwidth, 57,
                   XtNheight, 15,
                   XtNfromVert, onbutton,
                   NULL);

    mutebutton = MMW ( "MUTE", commandWidgetClass, form,
                       XtNhorizDistance, 4,
                       XtNvertDistance, 4,
                       XtNwidth, 57,
                       XtNheight, 15,
                       XtNfromVert, onbutton,
                       XtNfromHoriz, quit,
                       NULL);

    /* put up the display frame */

    frame = MMW ( "frame", formWidgetClass, form,
                  XtVaTypedArg,XtNbackground, XtRString, "grey50", 7,
                  XtNhorizDistance, 8,
                  XtNvertDistance, 4,
                  XtNfromHoriz, offbutton,
                  NULL);

    /* indicators for tuning and mute in frame*/

    tunedind = MMW ( "T", labelWidgetClass, frame,
                 XtVaTypedArg,XtNbackground, XtRString, "grey50", 7,
                 XtNhorizDistance, 0,
                 XtNvertDistance, 0,
                 XtNborderWidth, 0,
                 XtNwidth, 14,
                 NULL);

    mutedind = MMW ( "M", labelWidgetClass, frame,
                 XtVaTypedArg,XtNbackground, XtRString, "grey50", 7,
                 XtNhorizDistance, 0,
                 XtNvertDistance, 0,
                 XtNborderWidth, 0,
                 XtNfromVert, tunedind,
                 XtNwidth, 14,
                 NULL);

    /* put the display in the frame */

    display = MMW ( "display", formWidgetClass, frame,
                 XtVaTypedArg,XtNbackground, XtRString, "grey40", 7,
                 XtNfromHoriz, tunedind,
                 XtNhorizDistance, 1,
                 NULL);

    /* label in the display */
    /* set initial text to --.-- MHz, including the trailing
       blank which will set the size of display  */

    dtext = MMW ( "stations", labelWidgetClass, display,
                 XtNlabel, "--.-- MHz ",
                 XtVaTypedArg,XtNbackground, XtRString, "black", 6,
                 XtVaTypedArg,XtNforeground, XtRString, "green", 6,
                 NULL);

    /* create LOTS OF BUTTONS */

    volminus = MMW ( "-", commandWidgetClass, form, 
                     XtNhorizDistance, 8,
                     XtNvertDistance, 4,
                     XtNwidth, 39,
                     XtNheight, 15,
                     XtNfromHoriz, frame,
                     NULL);

    volume   = MMW ( "VOLUME", labelWidgetClass, form,
                     XtNhorizDistance, 0,
                     XtNvertDistance, 4,
                     XtNwidth, 53,
                     XtNheight, 15,
                     XtNfromHoriz, volminus,
                     NULL);

    volplus  = MMW ( "+", commandWidgetClass, form,
                     XtNhorizDistance, 0,
                     XtNvertDistance, 4,
                     XtNwidth, 39,
                     XtNheight, 15,
                     XtNfromHoriz, volume,
                     NULL);

    scanminus = MMW ( "<<",commandWidgetClass, form,
                     XtNhorizDistance, 8,
                     XtNvertDistance, 4,
                     XtNwidth, 20,
                     XtNheight, 15,
                     XtNfromVert, volminus,
                     XtNfromHoriz, frame,
                     NULL);
 
    tunminus = MMW ( "<", repeaterWidgetClass, form,
                     XtNhorizDistance, 0,
                     XtNvertDistance, 4,
                     XtNwidth, 17,
                     XtNheight, 15,
                     XtNfromVert, volminus,
                     XtNfromHoriz, scanminus,
                     XtNinitialDelay, 500,
                     XtNrepeatDelay, 250,
                     XtNminimumDelay, 10,
                     NULL);

    tuning   = MMW ( "TUNING", labelWidgetClass, form,
                     XtNhorizDistance, 0,
                     XtNvertDistance, 4,
                     XtNwidth, 53,
                     XtNheight, 15,
                     XtNfromVert, volminus,
                     XtNfromHoriz, tunminus,
                     NULL);

    tunplus  = MMW ( ">", repeaterWidgetClass, form,
                     XtNhorizDistance, 0,
                     XtNvertDistance, 4,
                     XtNwidth, 17,
                     XtNheight, 15,
                     XtNfromVert, volminus,
                     XtNfromHoriz, tuning,
                     XtNinitialDelay, 500,
                     XtNrepeatDelay, 250,
                     XtNminimumDelay, 10,
                     NULL);

    scanplus = MMW ( ">>", commandWidgetClass, form,
                     XtNhorizDistance, 0,
                     XtNvertDistance, 4,
                     XtNwidth, 20,
                     XtNheight, 15,
                     XtNfromVert, volminus,
                     XtNfromHoriz, tunplus,
                     NULL);
 
    /* the station buttons, the boring and repetitive
       way so I can play with the args if I like */

    st1 = MMW ( "station1", commandWidgetClass, form, NULL, NULL);

    n=0;
    XtSetArg(stsargs[n], XtNwidth, 72);n++;
    XtSetArg(stsargs[n], XtNfromVert, quit);n++;
    XtSetArg(stsargs[n], XtNshapeStyle, XmuShapeOval);n++;

    /* get and set station name from array. if station is not
       defined (freq==0), desensitize button and leave default
       text. Same for other stations */

    if (stations[0].freq != 0)
       {XtSetArg(stsargs[n], XtNlabel, stations[0].name);n++;}
    else
       {XtSetArg(stsargs[n], XtNsensitive, FALSE);n++;}

    XtSetValues(st1,stsargs,n);


    st2 = MMW ( "station2", commandWidgetClass, form, NULL, NULL);

    n=0;
    XtSetArg(stsargs[n], XtNwidth, 72);n++;
    XtSetArg(stsargs[n], XtNfromVert, quit);n++;
    XtSetArg(stsargs[n], XtNshapeStyle, XmuShapeOval);n++;
    XtSetArg(stsargs[n], XtNfromHoriz, st1);n++;
    if (stations[1].freq != 0)
       {XtSetArg(stsargs[n], XtNlabel, stations[1].name);n++;}
    else
       {XtSetArg(stsargs[n], XtNsensitive, FALSE);n++;}
    XtSetValues(st2,stsargs,n);

    st3 = MMW ( "station3", commandWidgetClass, form, NULL, NULL);

    n=0;
    XtSetArg(stsargs[n], XtNwidth, 72);n++;
    XtSetArg(stsargs[n], XtNfromVert, quit);n++;
    XtSetArg(stsargs[n], XtNshapeStyle, XmuShapeOval);n++;
    XtSetArg(stsargs[n], XtNfromHoriz, st2);n++;
    if (stations[2].freq != 0)
       {XtSetArg(stsargs[n], XtNlabel, stations[2].name);n++;}
    else
       {XtSetArg(stsargs[n], XtNsensitive, FALSE);n++;}
    XtSetValues(st3,stsargs,n);

    st4 = MMW ( "station4", commandWidgetClass, form, NULL, NULL);

    n=0;
    XtSetArg(stsargs[n], XtNwidth, 72);n++;
    XtSetArg(stsargs[n], XtNfromVert, quit);n++;
    XtSetArg(stsargs[n], XtNshapeStyle, XmuShapeOval);n++;
    XtSetArg(stsargs[n], XtNfromHoriz, st3);n++;
    if (stations[3].freq != 0)
       {XtSetArg(stsargs[n], XtNlabel, stations[3].name);n++;}
    else
       {XtSetArg(stsargs[n], XtNsensitive, FALSE);n++;}
    XtSetValues(st4,stsargs,n);

    st5 = MMW ( "station5", commandWidgetClass, form, NULL, NULL);

    n=0;
    XtSetArg(stsargs[n], XtNwidth, 72);n++;
    XtSetArg(stsargs[n], XtNfromVert, quit);n++;
    XtSetArg(stsargs[n], XtNshapeStyle, XmuShapeOval);n++;
    XtSetArg(stsargs[n], XtNfromHoriz, st4);n++;
    if (stations[4].freq != 0)
       {XtSetArg(stsargs[n], XtNlabel, stations[4].name);n++;}
    else
       {XtSetArg(stsargs[n], XtNsensitive, FALSE);n++;}
    XtSetValues(st5,stsargs,n);

    st6 = MMW ( "station6", commandWidgetClass, form, NULL, NULL);

    n=0;
    XtSetArg(stsargs[n], XtNwidth, 72);n++;
    XtSetArg(stsargs[n], XtNfromVert, st1);n++;
    XtSetArg(stsargs[n], XtNshapeStyle, XmuShapeOval);n++;
    if (stations[5].freq != 0)
       {XtSetArg(stsargs[n], XtNlabel, stations[5].name);n++;}
    else
       {XtSetArg(stsargs[n], XtNsensitive, FALSE);n++;}
    XtSetValues(st6,stsargs,n);

    st7 = MMW ( "station7", commandWidgetClass, form, NULL, NULL);

    n=0;
    XtSetArg(stsargs[n], XtNwidth, 72);n++;
    XtSetArg(stsargs[n], XtNfromVert, st1);n++;
    XtSetArg(stsargs[n], XtNfromHoriz, st6);n++;
    XtSetArg(stsargs[n], XtNshapeStyle, XmuShapeOval);n++;
    if (stations[6].freq != 0)
       {XtSetArg(stsargs[n], XtNlabel, stations[6].name);n++;}
    else
       {XtSetArg(stsargs[n], XtNsensitive, FALSE);n++;}
    XtSetValues(st7,stsargs,n);

    st8 = MMW ( "station8", commandWidgetClass, form, NULL, NULL);

    n=0;
    XtSetArg(stsargs[n], XtNwidth, 72);n++;
    XtSetArg(stsargs[n], XtNfromVert, st1);n++;
    XtSetArg(stsargs[n], XtNfromHoriz, st7);n++;
    XtSetArg(stsargs[n], XtNshapeStyle, XmuShapeOval);n++;
    if (stations[7].freq != 0)
       {XtSetArg(stsargs[n], XtNlabel, stations[7].name);n++;}
    else
       {XtSetArg(stsargs[n], XtNsensitive, FALSE);n++;}
    XtSetValues(st8,stsargs,n);

    st9 = MMW ( "station9", commandWidgetClass, form, NULL, NULL);

    n=0;
    XtSetArg(stsargs[n], XtNwidth, 72);n++;
    XtSetArg(stsargs[n], XtNfromVert, st1);n++;
    XtSetArg(stsargs[n], XtNfromHoriz, st8);n++;
    XtSetArg(stsargs[n], XtNshapeStyle, XmuShapeOval);n++;
    if (stations[8].freq != 0)
       {XtSetArg(stsargs[n], XtNlabel, stations[8].name);n++;}
    else
       {XtSetArg(stsargs[n], XtNsensitive, FALSE);n++;}
    XtSetValues(st9,stsargs,n);

    st10 = MMW ( "station10", commandWidgetClass, form, NULL, NULL);

    n=0;
    XtSetArg(stsargs[n], XtNwidth, 72);n++;
    XtSetArg(stsargs[n], XtNfromVert, st1);n++;
    XtSetArg(stsargs[n], XtNfromHoriz, st9);n++;
    XtSetArg(stsargs[n], XtNshapeStyle, XmuShapeOval);n++;
    if (stations[9].freq != 0)
       {XtSetArg(stsargs[n], XtNlabel, stations[9].name);n++;}
    else
       {XtSetArg(stsargs[n], XtNsensitive, FALSE);n++;}
    XtSetValues(st10,stsargs,n);

    /* ******* ADD CALLBACKS ************ */

    /* Volume UP */

    XtAddCallback(volplus, XtNcallback, VolUp, NULL);

    /* Volume Down */

    XtAddCallback(volminus, XtNcallback, VolDown, NULL);

    /* ON button = tune to default */

    XtAddCallback(onbutton, XtNcallback, StationTune, (XtPointer) &defaultstation.freq);
    XtAddCallback(onbutton, XtNcallback, DisplayName, (XtPointer) defaultstation.name);

    /* OFF */

    XtAddCallback(offbutton, XtNcallback, Switchoff, NULL); 

    /* QUIT button */

    XtAddCallback(quit, XtNcallback, Quit, NULL);

    /* MUTE button */

    XtAddCallback(mutebutton, XtNcallback, Mute, NULL);

    /* tune down ( "<" ) */

    XtAddCallback(tunminus, XtNcallback, TuneDown, NULL);
    XtAddCallback(tunminus, XtNstopCallback, TuneTuned, NULL);

    /* scan down ( "<<" ) */

    XtAddCallback(scanminus, XtNcallback, ScanDown, NULL);

    /* tune up ( ">" ) */

    XtAddCallback(tunplus, XtNcallback, TuneUp, NULL);
    XtAddCallback(tunplus, XtNstopCallback, TuneTuned, NULL);
 
    /* scan up ( ">>" ) */

    XtAddCallback(scanplus, XtNcallback, ScanUp, NULL);

    /* stations */

    XtAddCallback(st1, XtNcallback, StationTune, (XtPointer) &stations[0].freq);
    XtAddCallback(st1, XtNcallback, DisplayName, (XtPointer) stations[0].name);
    XtAddCallback(st2, XtNcallback, StationTune, (XtPointer) &stations[1].freq);
    XtAddCallback(st2, XtNcallback, DisplayName, (XtPointer) stations[1].name);
    XtAddCallback(st3, XtNcallback, StationTune, (XtPointer) &stations[2].freq);
    XtAddCallback(st3, XtNcallback, DisplayName, (XtPointer) stations[2].name);
    XtAddCallback(st4, XtNcallback, StationTune, (XtPointer) &stations[3].freq);
    XtAddCallback(st4, XtNcallback, DisplayName, (XtPointer) stations[3].name);
    XtAddCallback(st5, XtNcallback, StationTune, (XtPointer) &stations[4].freq);
    XtAddCallback(st5, XtNcallback, DisplayName, (XtPointer) stations[4].name);
    XtAddCallback(st6, XtNcallback, StationTune, (XtPointer) &stations[5].freq);
    XtAddCallback(st6, XtNcallback, DisplayName, (XtPointer) stations[5].name);
    XtAddCallback(st7, XtNcallback, StationTune, (XtPointer) &stations[6].freq);
    XtAddCallback(st7, XtNcallback, DisplayName, (XtPointer) stations[6].name);
    XtAddCallback(st8, XtNcallback, StationTune, (XtPointer) &stations[7].freq);
    XtAddCallback(st8, XtNcallback, DisplayName, (XtPointer) stations[7].name);
    XtAddCallback(st9, XtNcallback, StationTune, (XtPointer) &stations[8].freq);
    XtAddCallback(st9, XtNcallback, DisplayName, (XtPointer) stations[8].name);
    XtAddCallback(st10, XtNcallback, StationTune, (XtPointer) &stations[9].freq);
    XtAddCallback(st10, XtNcallback, DisplayName, (XtPointer) stations[9].name);

    /* ********* END of CALLBACKS ************ */

    /* Create windows for widgets and map them */

    XtRealizeWidget(toplevel);

    /* ****  Loop for events **** */

    XtAppMainLoop(app_context);    /* will sleep if no work */
}
