/**************************************************************************/
/* svgafft --  Spectrum Analyzer                                          */
/*                                                                        */ 
/* svga bar 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: s_bar_display.cc,v 0.1 1995/04/15 18:36:13 drewvel Exp $             

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

/*******************************************************************/
// SVGA_Bar_Display Derived Class (from SVGA_Display)
/*******************************************************************/

/*******************************************************************/
SVGA_Bar_Display::SVGA_Bar_Display(int n, Signal& S, 
				   aScreen& screen, int t,
				   boolean st)
: SVGA_Display(n, S, screen, t, st)
{
  DOUT("svga bar display ctor");
  granularity=true;
  analyzer=NULL; // no parent yet
  allocated=false;
  AllocIndex(&index, &i_length, n);
  DOUT("svga bar display ctor (out)");
}


/*******************************************************************/
SVGA_Bar_Display::~SVGA_Bar_Display()
{
  DeallocBars();
}


/*******************************************************************/
void SVGA_Bar_Display::ProcessControl(char control)
{
  extern char *gSVGABarDisplayHelpText;

  // inherit parent controls
  SVGA_Display::ProcessControl(control);  

  int num=1;

  switch(control) {
  case K_HELP:
    ClrScr();
    cerr << gProgInfo << endl;
    cerr << gSVGABarDisplayHelpText << endl;
    KeyCont();
    break;

  case K_SBD_BARTYPE6: // this is weird but works
    num++;
  case K_SBD_BARTYPE5:
    num++;
  case K_SBD_BARTYPE4:
    num++;
  case K_SBD_BARTYPE3:
    num++;
  case K_SBD_BARTYPE2:
    num++;
  case K_SBD_BARTYPE1:
    RenewDisplayMethod(num);
    ClearGraph();
    WriteInfo();
    s->Flush();
    break;
  }
}


/*******************************************************************/
void SVGA_Bar_Display::DisplayChange()
{
  DOUT("changing bar display to number " << type);
  AllocBars(type);
  ClearGraph();
}


/*******************************************************************/
void SVGA_Bar_Display::Reset()
{
  SVGA_Display::Reset();
  DrawInit();
}


/*******************************************************************/
void SVGA_Bar_Display::DrawInit()
{
  DisableClipping();  
  SetBarFreqs();
  DrawXAxis();
  DrawYAxis();   
  EnableClipping();  
}


/*******************************************************************/
void SVGA_Bar_Display::AllocBars(int t)
{
  DOUT("allocating bars");
  if (!allocated) AllocBarsType(&Bars, t, num);  
  if (Bars) {
    SetBarExtents();
    allocated=true;
  }
  else {
    cerr << "Couldn't allocate bars!" << endl;
    exit(0);
  }
  DOUT("bars allocated");
}


/*******************************************************************/
void SVGA_Bar_Display::RenewDisplayMethod(int t)
{
  DeallocBars();
  type=t;
  AllocBars(type);
}


/*******************************************************************/
void SVGA_Bar_Display::AllocBarsType(BaseType** bars, int type, int num)
{
  // note: found that objects must have same
  // data space size to allocate an array,
  // so put all data in base class.

#ifdef RUNTIME_BARCHANGE  
  switch (type) {
  case 1:
    *bars=new BarGraph[num];
    break;

  case 2:
    *bars=new SegmentedBarGraph[num];
    break;

  case 3:
    *bars=new SegmentBarGraph[num];
    break;

  case 4:
    *bars=new PeakDropSegmentedBarGraph[num];
    break;

  case 5:
    *bars=new PeakDropSegmentBarGraph[num];
    break;

  case 6:
    *bars=new FadeSegmentBarGraph[num];
    break;

  default:
    *bars=new BarGraph[num];
    break;
  }
#else
  *bars=new BaseType[num];
#endif
}


/*******************************************************************/
float SVGA_Bar_Display::AvgFreq(int low, int high)
{
  float n=(float) s->GetParms().sampbufsize / (float) sizescale;
  float period=1 / (float) s->GetParms().Sample_Rate;
  float sum=0, f;
  int count=abs(high-low+1);
  int i;

  count=count>=1?count:1;
  
  for (i=low; i<=high; i++) {
    if (i==0) f=0;
    //else if (i==1) f=1/((float) period);
    else f=((float) i/2)/((float) n * period);
    
    sum+=f;
  }
  
  return sum/count;
}


/*******************************************************************/
float SVGA_Bar_Display::BarFreq(int barnum)
{
  int lownum=CalculateFreqNum(controls.FreqRange.low);
  int highnum=CalculateFreqNum(controls.FreqRange.high);

  float interval=(float) (highnum-lownum+1) / (float) num;
  int middle=(int) (interval/2);
  if (middle<1) middle=1;
  
  int low=(int) ((float) barnum*interval)-middle+lownum;
  low=((low >= 0) ? low : 0);
  int high=(int) (((float) barnum*interval+interval-1))-middle+lownum;
  high=((high >= 1) ? high : 1);
  if (high<=low) high=low+1;

  return AvgFreq(low, high);
}


/*******************************************************************/
void SVGA_Bar_Display::Show()
{
  InitScreen();
  Reset();
  DrawInit();
}


/*******************************************************************/
void SVGA_Bar_Display::InitRange(int SizeScale)
{
  Display::InitRange(SizeScale);
  UpdateIntervals();
}


void Show(short a[], short b[], int num)
{
  int i;
  for (i=0; i<num; i++) {
    cout << "i: " << i << endl;
    cout << "index: " << a[i] << " length: " << b[i] << endl;
  }
}


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

  if (!(index && i_length)) 
    AllocIndex(&index, &i_length, num);      

  if (index && i_length) {
    CreateFreqIndex(controls.FreqRange.low, controls.FreqRange.high,
		    s->GetParms().Sample_Rate,
		    controls.LogXScale.isOn,
		    index, i_length,
		    size/sizescale, num);
  }
    
#if 0
  //::Show(index, i_length, num);
  cout << "lowfreq: " << lowfreq << endl;
  cout << "highfreq: " << highfreq << endl;
  cout << "rate: " << s->GetParms().Sample_Rate << endl;
  cout << "size: " << size/sizescale << endl;
  cout << "num: " << num << endl; 
#endif

}
*/

/*******************************************************************/
void SVGA_Bar_Display::UpdateIntervals()
{
  UpdateFreqIndex();
}



/*******************************************************************/
void SVGA_Bar_Display::UpdateCoefficients()
{
  register int i;
  if (controls.LogYScale.isOn) 
    for (i=0; i<num; i++) {      
      rrms_val=logatten*
	log10(rrms(&coefficients[index[i]], i_length[i])+1)
	  /controls.Attenuation.val+controls.VPosition.val;
      Bars[i].SetValue((int) rrms_val);
      Bars[i].Update();
    }
  else
    for (i=0; i<num; i++) {
      irms_val=irms(&coefficients[index[i]], i_length[i])
	/controls.Attenuation.val+controls.VPosition.val;
      Bars[i].SetValue(irms_val);
      Bars[i].Update();
    }
}
#undef LinearBarBase


/*******************************************************************/
void SVGA_Bar_Display::SetBarFreqs()
{
  float freq;
  char label[16];

  for (int i=0; i<num; i++) {
    if (controls.LogXScale.isOn) 
      freq=BarFreq(LogNum(i, num));
    else
      freq=BarFreq(i);

    if (freq>1000) {
      sprintf(label, "%*.*fk", 
	      0, // field width 
	      2, // field precision
	      freq/1000);
    } else {
      sprintf(label, "%*.*f", 
	      3, // field width 
	      0, // field precision
	      freq);
    }
    
    Bars[i].Label(label);    
  }
}


/*******************************************************************/
void SVGA_Bar_Display::RefreshFreqs()
{
  SVGA_Display::RefreshFreqs();

  DisableClipping();

  SetBarFreqs();   
  DrawXAxis();
  DrawYAxis();

  EnableClipping();
}


/*******************************************************************/
void SVGA_Bar_Display::DrawFreqs()
{
  int intv=gConfig.NumFreqLabels.val*(1024/theScreen.h);

  FontColor(gConfig.LabelColor.val);

  for (int i=0; i<num; i++) 
    if (!(i % intv) &&
	!gConfig.NoText.val) {
      //Bars[i].DrawLabel();    
      //Bars[i].DrawTick(3, DEFTICKMARKCOLOR);
    } 
  
  if (!gConfig.NoText.val) 
    WriteStr(theScreen.x1-34, theScreen.y2+7, DEFKEYCOLOR, "Hz");
}

 
/*******************************************************************/
void SVGA_Bar_Display::SetBarExtents()
{

  int w=theScreen.x2-theScreen.x1+1;
  int h=theScreen.y2-theScreen.y1;

  FontColor(gConfig.LabelColor.val);

  for (int i=0; i<num; i++) {
    Bars[i].SetProx(i*w/num+w/4/num+theScreen.x1+1, theScreen.y2);
    Bars[i].SetClipValue(h);
    Bars[i].SetWidth(gConfig.BarWidth.val);
    Bars[i].AdjustVals();    
  }
}


/*******************************************************************/
void SVGA_Bar_Display::DeallocBars()
{
  if (allocated) {
    if (Bars) 
      delete [] Bars;
    Bars=NULL;
    allocated=false;
  }
}


/*******************************************************************/
void SVGA_Bar_Display::ClearGraph()
{
  for (int i=0; i<num; i++) Bars[i].Reset();
  Update();
  SVGA_Display::ClearGraph();
}


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