#include <stdio.h>
#include <conio.h>
#include <ctype.h>
#include <graphics.h>
#include <math.h>
#include "freq.h"

double amp;
double db;
double phase;

extern int *BitReversed;
extern int x[];
extern int lasty[];
extern int *fftdata;

extern int logamp;
extern int logfreq;
extern int fftlen;
extern long SampleRate;
extern int windfunc;
extern int logs;
extern int logb;
extern int ys;

extern int gain3db;
extern int deriv;
extern int gain;
extern long ref_freq;

void cleanup_vga(void)
{
   /*
    *  Clean up VGA registers so Borland libraries work again.
    */
   outportb(0x3c4,2);
   outportb(0x3c5,0x0f);   // Use all bit planes
}

void setup_vga(void)
{
   /*
    *  Set up VGA registers for graphics display
    */
   outportb(0x3ce,1);
   outportb(0x3cf,0);   // Disable set/reset for all planes
   outportb(0x3ce,3);
   outportb(0x3cf,0);   // No rotate, just write data
   outportb(0x3ce,4);
   outportb(0x3cf,0);   // Read from bit plane 0 (blue)
   outportb(0x3ce,5);
   outportb(0x3cf,inportb(0x3cf)&0x70); // Read and write direct to memory
   outportb(0x3ce,8);
   outportb(0x3cf,0xff);  // Allow modification of all bits
   outportb(0x3c4,2);
   outportb(0x3c5,1);   // Use bit plane 0
}

void update_display(int bin)
{
   char ach[100];

   settextjustify(LEFT_TEXT,TOP_TEXT);
   setcolor(TEXT_COLOR);
   setfillstyle(SOLID_FILL,0);
   bar(0,22,160,70);
   sprintf(ach," Freq: %7.5lg Hz ",(double)bin*SampleRate/fftlen);
   outtextxy(0,22,ach);
   sprintf(ach,"  Amp: %7.5lg   ",amp);
   outtextxy(0,34,ach);
   sprintf(ach,"       %7.5lg db ",db);
   outtextxy(0,46,ach);
   sprintf(ach,"Phase: %7.4lg deg. ",phase);
   outtextxy(0,58,ach);
}


void highlight(int bin)
{
   int i,j;
   int bri=BitReversed[bin];
   double re=fftdata[bri];
   double im=fftdata[bri+1];

   amp=sqrt(re*re+im*im)/32768.0;

   if(gain3db)
      amp*=sqrt((double)bin*(double)SampleRate/fftlen/(double)ref_freq);

   if(deriv==1)
      amp*=(double)SampleRate/(2*M_PI*(double)ref_freq);

   if(deriv==2)
      amp*= (double)SampleRate/(2*M_PI*(double)ref_freq)
           *(double)SampleRate/(2*M_PI*(double)ref_freq);

   if(amp!=0)
   {
      phase=atan2(im,re)*2*M_PI;
      db=20*log10(amp);
   }
   else
   {
      phase=0;
      db=-100;
   }

   for(i=0;(x[i]<bin) && i<(WINDOW_RIGHT-WINDOW_LEFT);i++);
   for(j=i+1;(x[j]==-1) && j<(WINDOW_RIGHT-WINDOW_LEFT);j++);

   if(i==0) j=0;
   i=(i+j)/2;

   setcolor(DARK_HIGHLIGHT);
   moveto(WINDOW_LEFT+i,WINDOW_TOP);
   lineto(WINDOW_LEFT+i,lasty[i]);
   setcolor(LIGHT_HIGHLIGHT);
   lineto(WINDOW_LEFT+i,WINDOW_BOTTOM);
}


void dehighlight(int bin)
{
   int i,j;

   for(i=0;(x[i]<bin) && i<(WINDOW_RIGHT-WINDOW_LEFT);i++);
   for(j=i+1;(x[j]==-1) && j<(WINDOW_RIGHT-WINDOW_LEFT);j++);

   if(i==0) j=0;
   i=(i+j)/2;

   setcolor(BLACK);
   moveto(WINDOW_LEFT+i,WINDOW_TOP);
   lineto(WINDOW_LEFT+i,lasty[i]);
   setcolor(GRAPH_COLOR);
   lineto(WINDOW_LEFT+i,WINDOW_BOTTOM);
}


int process_input(void)
{
   int c;
   int bin;

   cleanup_vga();

   c=toupper(getch());

   if(c==0) c=0x100+getch();

   if(c==' ')
   {
      bin=logfreq;

      highlight(bin);

      setcolor(TEXT_COLOR);
      settextjustify(CENTER_TEXT,TOP_TEXT);
      settextstyle(0,0,0);
      outtextxy(320,86,"Use arrows to move, Space to continue.");

      c=0;
      while(c!=' ' && c!='Q' && c!='X' && c!='E' && c!=0x1B)
      {
         update_display(bin);

         c=toupper(getch());
         if(c==0)
            c=getch()+256;

         if(c==LEFT)
         {
            if(bin>logfreq)
            {
               dehighlight(bin);
               bin--;
               highlight(bin);
            }
         }
         else if(c==RIGHT)
         {
            if(bin<fftlen/2-1)
            {
               dehighlight(bin);
               bin++;
               highlight(bin);
            }
         }
         else if(c==CTL_LEFT)
         {
            dehighlight(bin);
            bin-=10;
            if(bin<logfreq)
               bin=logfreq;
            highlight(bin);
         }
         else if(c==CTL_RIGHT)
         {
            dehighlight(bin);
            bin+=10;
            if(bin>=fftlen/2)
               bin=fftlen/2-1;
            highlight(bin);
         }
      }
      dehighlight(bin);

      setfillstyle(SOLID_FILL,0);
      bar(0,22,160,70);
      bar(160,86,480,96);
   }
   else if(c==UP)
   {
      if(logamp)
      {
         logs++;
         logb++;
         if(logb>10)
         {
            logb--;
            logs--;
         }
         setup_logscales();
      }
      else
      {
         ys--;
         if(ys<1) ys=1;
         setup_linscales();
      }

      amplitude_scale();
   }
   else if(c==DOWN)
   {
      if(logamp)
      {
         logs--;
         logb--;
         if(logs<0)
         {
            logb++;
            logs++;
         }
         setup_logscales();
      }
      else
      {
         ys++;
         if(ys>20) ys=20;
         setup_linscales();
      }

      amplitude_scale();
   }
   else if(c==LEFT)
   {
      if(logamp)
      {
         logb++;
         if(logb>10)
         {
            logb--;
            logs--;
            if(logs<0) logs++;
         }
         setup_logscales();
      }
      else
      {
         ys++;
         if(ys>20) ys=20;
         setup_linscales();
      }

      amplitude_scale();
   }
   else if(c==RIGHT)
   {
      if(logamp)
      {
         logb--;
         if(logb<logs+3) logb++;
         setup_logscales();
      }
      else
      {
         ys--;
         if(ys<1) ys=1;
         setup_linscales();
      }

      amplitude_scale();
   }
   else if(c=='X')
   {
      /*
       *  Toggle between linear and logarithmic frequency scale
       */
      logfreq=!logfreq;

      setup_xscale();
      if(logamp)
         setup_logscales();
      else
         setup_linscales();

      frequency_scale();
   }
   else if(c=='Y')
   {
      /*
       *  Toggle between linear and logarithmic amplitude scale
       */
      logamp=!logamp;

      if(logamp)
         setup_logscales();
      else
         setup_linscales();

      amplitude_scale();
   }
   else if(c=='W')
   {
      /*
       *  Change the windowing function
       */
      windfunc++;
      if(windfunc>6) windfunc=0;
      compute_window_function();

      update_header();
   }
   else if(c=='0')
   {
      /*
       *  0 dB/octave gain
       */
      gain=0;
      gain3db=0;
      deriv=0;

      if(logamp)
         setup_logscales();
      else
         setup_linscales();

      update_header();
   }
   else if(c=='3')
   {
      /*
       *  3 dB/octave gain
       */
      gain=3;
      gain3db=1;
      deriv=0;

      if(logamp)
         setup_logscales();
      else
         setup_linscales();

      update_header();
   }
   else if(c=='6')
   {
      /*
       *  6 dB/octave gain
       */
      gain=6;
      gain3db=0;
      deriv=1;

      if(logamp)
         setup_logscales();
      else
         setup_linscales();

      update_header();
   }
   else if(c=='9')
   {
      /*
       *  9 dB/octave gain
       */
      gain=9;
      gain3db=1;
      deriv=1;

      if(logamp)
         setup_logscales();
      else
         setup_linscales();

      update_header();
   }
   else if(c=='1')
   {
      /*
       *  12 dB/octave gain
       */
      gain=12;
      gain3db=0;
      deriv=2;

      if(logamp)
         setup_logscales();
      else
         setup_linscales();

      update_header();
   }

   setup_vga();

   if(c=='Q' || c=='E' || c==0x1B)
      return(0);
   else
      return(1);
}