/**************************************************************************/
/* svgafft --  Spectrum Analyzer                                          */
/*                                                                        */ 
/* abstract display class implementation                                  */
/**************************************************************************/

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

    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: display.cc,v 0.11 1995/05/01 22:04:51 drewvel Exp $             

/*******************************************************************/
#include <iostream.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "display.h"
#include "analyzer.h"
#include "gconfig.h"
#include "signal.h"
#include "barconfig.h"
#include "colors.h"
#include "keymap.h"


/*******************************************************************/
void ClrScr()
{
  for (int i=0; i<24; i++) 
    cerr << endl;
}


/*******************************************************************/
void KeyCont()
{
  cerr << "Press enter to continue..." << endl;
  getchar();
}


// abstract display class initializer
/*******************************************************************/
Display::Display(int n, Signal& S, 
		 aScreen& screen, int t, 
		 boolean st)
{
  num=n;
  s=&S;
  theScreen=screen;
  logscale=st;
  type=t;
  coefficients=NULL;

  controls.Set(gConfig);
  controls.Sample_Rate=s->GetParms().Sample_Rate;
  controls.Normalize();

  sizescale=gConfig.SizeScale.val;

  granularity=false;
  
  index=NULL;
  i_length=NULL;  

  AllocIndex(&index, &i_length, s->GetParms().sampbufsize);
}


/*******************************************************************/
Display::~Display()
{
  FreeIndex(&index, &i_length);
}


/*******************************************************************/
virtual void Display::ProcessControl(char control)
{
  extern char *gDisplayHelpText;

  switch (control) {

  case K_LOWFREQ_INC:
    controls.FreqRange.LowInc();
    InitRange();
    WriteInfo();
    RefreshFreqs();
    break;
    
  case K_LOWFREQ_DEC:
    controls.FreqRange.LowDec();
    InitRange();
    WriteInfo();
    RefreshFreqs();
    break;
    
  case K_HIGHFREQ_INC:
    controls.FreqRange.HighInc();
    InitRange();
    WriteInfo();
    RefreshFreqs();
    break;
    
  case K_HIGHFREQ_DEC:
    controls.FreqRange.HighDec();
    InitRange();
    WriteInfo();
    RefreshFreqs();
    break;
    
  case K_ATTEN_INC:
    controls.Attenuation++;
    WriteInfo();
    break;
    
  case K_ATTEN_DEC:
    controls.Attenuation--;
    WriteInfo();
    break;
    
  case K_VPOS_INC:
    controls.VPosition++;
    WriteInfo();
    break;
    
  case K_VPOS_DEC:
    controls.VPosition--;
    WriteInfo();
    break;

  case K_FREQOFS_INC:
    controls.FreqLabOffset++;
    RefreshFreqs();
    break;

  case K_FREQOFS_DEC:
    controls.FreqLabOffset--;
    RefreshFreqs();
    break;

  case K_SAMPLERATE_INC:
  case K_SAMPLERATE_DEC:
    {
      Parms t=s->p; 
      if (control==K_SAMPLERATE_INC) 
	t.Sample_Rate+=
	  controls.Sample_Rate.step;
      else
	t.Sample_Rate-=
	  controls.Sample_Rate.step;
      
      int newtopfreq=(int) 
	((float) controls.FreqRange.high / 
	 (float) (s->p.Sample_Rate/2) * t.Sample_Rate);
      
      s->UpdateParms(t);
      controls.Sample_Rate.val=s->p.Sample_Rate;
      controls.FreqRange.SetHigh(newtopfreq);
      InitRange();
      WriteInfo();
      DisplayChange();
      RefreshFreqs();
    }
    break; 
    
  case K_PAUSE:
    getchar();
    s->Flush();
    break;
    
  case K_SIGFLUSH:
    s->Flush();
    break;      
    
  case K_MORELABELS:
    gConfig.NumFreqLabels.val--;
    if (gConfig.NumFreqLabels.val<=0)
      gConfig.NumFreqLabels.val=1;
    RefreshFreqs();
    break;
    
  case K_LESSLABELS:
    gConfig.NumFreqLabels.val++;
    RefreshFreqs();
    break;
    
  case K_CLEARGRAPH:
    ClearGraph();
    break;
    
  case K_ATTENVPOSZERO:
    controls.VPosition.val=DEFVPOS;
    
    if (gConfig.Bits8.val) 
      controls.Attenuation=DEFATTENUATION_8;
    else 
      controls.Attenuation=DEFATTENUATION_16;
    WriteInfo();
    break;
    
  case K_HANNINGWINDOWTOGGLE:
    gConfig.HWindow.val=!gConfig.HWindow.val;
    WriteInfo();
    break;
    
  case K_LOGXSCALE_TOGGLE:
    controls.LogXScale.Toggle();
    RefreshFreqs();
    WriteInfo();
    s->Flush();
    break;      
    
  case K_LOGYSCALE_TOGGLE:
    controls.LogYScale.Toggle();
    SetLogAtten(LOGATTEN);
    ClearGraph();
    WriteInfo();
    s->Flush();
    break;      
    
    
  case K_HELP:
    UnShow();
    ClrScr();
    cerr << gProgInfo << endl;
    cerr << gDisplayHelpText << endl;
    KeyCont();
    break;

  case K_SAVEOPTIONS:
    gPassConfig=gConfig; // request for save configuraion
    break;
  }
}


/*******************************************************************/
void Display::SetExtents(int X1, int Y1, 
			 int X2, int Y2)
{ 
  if ((Y2-Y1) < theScreen.h && (X2-X1) < theScreen.w) {    
    theScreen.x1=X1; 
    theScreen.y1=Y1; 
    theScreen.x2=X2; 
    theScreen.y2=Y2; 
  }
}


/*******************************************************************/
int Display::CalculateFreqNum(int freq)
{
  int num;

  num=(int) ((float) freq*2/(float) (s->GetParms().Sample_Rate)*
	     (float) s->GetParms().sampbufsize /
	     (float) sizescale);  
  
  return num;
}


/*******************************************************************/
int Display::LogNum(int num, int high)
{
  float lhigh=log((float) high);
  if (num>=high) num=high-1;
  float lcurrent=log((float) (high-num));
  float mult=lcurrent/lhigh;
  return (int) ((1-mult)*(float) high);
}


/*******************************************************************/
float Display::LogNum(float num, float high)
{
  float lhigh=log(high);
  if (num>=high) num=high-1;
  float lcurrent=log(high-num);
  float mult=lcurrent/lhigh;
  return (1-mult)*high;
}


/*******************************************************************/
virtual float Display::GetFreqFromBufferIndex(int a)
{
  float freq;

  if (!controls.LogXScale.isOn) {
    freq=((float) a / (float) s->GetParms().sampbufsize)
      * (float) sizescale * (float) s->GetParms().Sample_Rate * 2;
  } 
  else {
    
    // calculate log frequency

    int size=s->GetParms().sampbufsize; 
    int sizef=(int) ((float) size / (float) sizescale);

    float high=log((float) sizef);
    a=sizef-a;

    float current=log((float) a);

    float mult=current/high;

    freq=(1-mult)
      * ((float) s->GetParms().Sample_Rate / 2.0);
  }
  
  return freq;
}


/*******************************************************************/
virtual void Display::UpdateFreqIndex()
{
  int size=s->GetParms().sampbufsize;

  if (!(index && i_length)) 
    AllocIndex(&index, &i_length, s->GetParms().sampbufsize);      
  
  if (index && i_length) {
    if (granularity)
      CreateFreqIndex(controls.FreqRange.low, controls.FreqRange.high,
		      s->GetParms().Sample_Rate,
		      controls.LogXScale.isOn,
		      index, i_length,
		      size/sizescale, num);
    else
      CreateFreqIndex(controls.FreqRange.low, controls.FreqRange.high,
		      s->GetParms().Sample_Rate,
		      controls.LogXScale.isOn,
		      index, i_length,
		      size/sizescale);  
  }
}


/*******************************************************************/
virtual void Display::RefreshFreqs()
{
  UpdateFreqIndex();
}


/*******************************************************************/
void Display::AllocIndex(short *a[], short *b[], int num)
{
  FreeIndex(a, b);  

  *a=new short[num];
  *b=new short[num];
}


/*******************************************************************/
void Display::FreeIndex(short *a[], short *b[])
{
  if (*a) 
    delete [] *a;
  if (*b) 
    delete [] *b;

  *a=NULL;
  *b=NULL;
}


/*******************************************************************/
#define LinearBarBase(num) ((int) ((float) (num)*mult)+offset)
#define LogBarBase(num) (LogNum((int) ((float) (num)*mult)+offset, size))
void Display::CreateFreqIndex(int low, int high, int srate, boolean logscale,
			      short index[], short i_length[],
			      int size, int num)
{  
  float mult;
  int offset, length;
  boolean num_set=false;

  if (num==0) {
    num=size;
    num_set=true;
  }
  
  mult=((float) (high-low) / (float) srate) * 2.0 * (float) size;
  mult=mult / (float) num; 
  offset=(int) ((float) ((float) low/ (float) srate * 2 )
		* (float) size );
  length=(int) (mult); 
  length=length < 1 ? 1 : length; 

  int i;
  if (controls.LogXScale.isOn) {
    int lenhigh;
    int len, llen;
    
    lenhigh=LogNum((int) ((num-length)*mult), size) - 
      LogNum((int) ((num-length*2)*mult), size);
    
    size-=lenhigh; // adjust for length

    mult=((float) (high-low) / (float) srate) * 2.0 * (float) size;
    mult=mult / (float) num; 
    length=(int) (mult); 
    length=length < 1 ? 1 : length; 

    for (i=0; i<num; i++) {
      index[i]=LogBarBase(i);
      len=(int) ((float) i/(float) num*(float) lenhigh);
      llen=LogNum(len, lenhigh);  //length;
      if (llen>lenhigh) llen=lenhigh;
      if (llen<1) llen=1;
      i_length[i]=llen;
    }        
  } 
  else {
    length--;
    if (length<1) length=1;
    for (i=0; i<num; i++) {      
      index[i]=LinearBarBase(i);      
      i_length[i]=length;
    }    
  }
}
#undef LinearBarBase
#undef LogBarBase


/*******************************************************************/
void Display::InitRange(int SizeScale)
{
  controls.Sample_Rate=s->GetParms().Sample_Rate;
  controls.Normalize();

  if (SizeScale>0) 
    sizescale=SizeScale;
}


/*******************************************************************/
void Display::SetBuffer(real *abuffer)
{
  coefficients=abuffer;
}


/*******************************************************************/
void Display::SetSignal(Signal& sig)
{
  s=&sig;
}


/*******************************************************************/
void Display::SetScreen(aScreen& screen)
{
  theScreen=screen;
}


/*******************************************************************/
void Display::SetParent(SpectrumAnalyzer& a)
{ 
  analyzer=&a;
}


/*******************************************************************/
void Display::SetAtten(int Atten)
{
  controls.Attenuation=Atten;
}


/*******************************************************************/
void Display::SetLogAtten(int LogAtten)
{
  logatten=(int) ((float) LogAtten*((float) Height()/(float) 2));
}


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