/**************************************************************************/
/* svgafft --  Spectrum Analyzer                                          */
/*                                                                        */ 
/* svga 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: svga_display.cc,v 0.2 1995/05/01 22:04:55 drewvel Exp $             

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


/*******************************************************************/
void doBox(int x1, int y1, int x2, int y2, int col)
{
  gl_line(x1, y1, x2, y1, col);
  gl_line(x1, y1, x1, y2, col);
  gl_line(x2, y1, x2, y2, col);
  gl_line(x1, y2, x2, y2, col);
}


/*******************************************************************/
void FadeBox(int x1, int y1, int x2, int y2,
	     Color& from_col, Color& to_col, 
	     int width, boolean fill, int bgcol)
{
  int i;
  unsigned char *cols=new unsigned char[width];
  Color *Colors=new Color[2];
    
  Colors[0]=from_col;
  Colors[1]=to_col;

  FadeColorRange(Colors, 2, cols, width);
  
  for (i=0; i<width; i++) 
    doBox(x1-i, y1-i, 
	  x2+i, y2+i, 
	  cols[i]);
  
  delete [] Colors;
  delete [] cols;
  
  if (fill) 
    gl_fillbox(x1+1, y1+1, 
	       x2-x1+1, y2-y1+1, 
	       bgcol);
  
}


/*******************************************************************/
void ShowColorsV(unsigned char *cols, int num, 
		 int x, int y, 
		 int w, int h,
		 boolean reverse)
{
  int divh=h/num;
  int i;
  for (i=0; i<num; i++) {
    if (reverse) 
      gl_fillbox(x, y+i*divh, w, divh-1, cols[num-i]);
    else 
      gl_fillbox(x, y+i*divh, w, divh-1, cols[i]);
  }
}




// SVGA_Display Derived Class (from Display)
/*******************************************************************/
SVGA_Display::SVGA_Display(int n, Signal& S, 
			   aScreen &screen, int t,
			   boolean st):Display(n, S, screen, t)
{	     
  display_on=false;
  SetExtents(gConfig.x1.val,
	     gConfig.y1.val,
	     gConfig.x2.val,
	     gConfig.y2.val);
  fp=NULL;
  clipping_enabled=true;
}

/*******************************************************************/
SVGA_Display::~SVGA_Display()
{
  if (display_on)
    UnShow();
}


/*******************************************************************/
void SVGA_Display::ProcessControl(char control)
{
  extern char *gSVGADisplayHelpText;

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

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

  case K_SD_SCREEN0:
    ChangeScreen(0);
    break;
    
  case K_SD_SCREEN1:
    ChangeScreen(1);
    break;
    
  case K_SD_SCREEN2:
    ChangeScreen(2);
    break;
    
  case K_SD_SCREEN3:
    ChangeScreen(3);
    break;    

  case K_SAVEOPTIONS: // prepare save-configuration var
    SetScrConfig(gPassConfig);
    break;
  }
}


/*******************************************************************/
void SVGA_Display::GetScrConfig(globalConfig& config)
{
  theScreen.x1=config.x1.val;
  theScreen.y1=config.y1.val;
  theScreen.x2=config.x2.val;
  theScreen.y2=config.y2.val;
}


/*******************************************************************/
void SVGA_Display::SetScrConfig(globalConfig& config)
{
  config.x1.val=theScreen.x1;
  config.y1.val=theScreen.y1;
  config.x2.val=theScreen.x2;
  config.y2.val=theScreen.y2;
}


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


/*******************************************************************/
void SVGA_Display::Reset()
{
  DisableClipping();
  SetupScreen();   
  DisplayChange();
  EnableClipping();
  WriteInfo();
}


/*******************************************************************/
void SVGA_Display::UnShow()
{
  vga_setmode(TEXT);
  display_on=false;
  if (fp) {
    delete [] fp;
    fp=NULL;
  }
}

/*******************************************************************/
void SVGA_Display::ClearGraph()
{
  if (display_on) {
    
    FadeBox(theScreen.x1+1, theScreen.y1+2,
	    theScreen.x2-1, theScreen.y2-2,
	    Color(0, 0, 0), Color(255,255,255), 
	    5, true, gConfig.GraphBG.val);
  }
}


/*******************************************************************/
void SVGA_Display::ChangeScreen(int num)
{
  theScreen=Screens[num % NUMSCREENS];
  gConfig.Screen.val=num % NUMSCREENS;
  SetScrConfig(gConfig);
  InitScreen();
  Reset();
  DrawInit();
  RefreshFreqs();
}


/*******************************************************************/
void SVGA_Display::InitScreen()
{
  if (display_on) UnShow();
  if (vga_hasmode(theScreen.modenum)) {
    vga_setmode(theScreen.modenum);
    gl_setcontextvga(theScreen.modenum);
    if (theScreen.rgbcols) {
      gl_setrgbpalette();
      DOUT("palette set to rgb");
    } else
      DOUT("normal palette used");

    DOUT("initialized screen mode " << theScreen.modenum);
    
    DOUT("allocating font");
    
    if (!fp) {
      fp=new char[FWIDTH*FHEIGHT*256*BYTESPERPIXEL];
      
      if (fp) {
	gl_expandfont(FWIDTH, FHEIGHT, FCOLOR, gl_font8x8, fp);
	gl_setfont(FWIDTH, FHEIGHT, fp);  
	DOUT("font allocated");
      }
      else {
	cerr << "couldn't allocate SVGAlib font!" << endl;
	exit(0);
      }
    }
    
    display_on=true;
  } else {
    cerr << 
      "\n\nSVGAlib says your video card doesn't support this mode.\n\n";
    exit(0);
  }

  DOUT("init screen done");
}


/*******************************************************************/
void SVGA_Display::DrawYAxis()
{
  if (!gConfig.NoText.val) {
    int h=theScreen.y2-theScreen.y1;
    int interval=h/10;
    char db[8];
    int dBRange=gConfig.Bits8.val ? DBRANGE8 : DBRANGE16;
    
    for (int i=0; i<=h; i+=interval) {
      gl_line(theScreen.x1-4, theScreen.y1+i+1, 
	      theScreen.x1-1, theScreen.y1+i+1, DEFTICKMARKCOLOR);
      sprintf(db, "+%0.0f", (float) (h-i)/h*dBRange);
      if (strlen(db)<=3)
	WriteStr(theScreen.x1-30, theScreen.y1+i-3, 
		 gConfig.LabelColor.val, db);
      else
	WriteStr(theScreen.x1-36, theScreen.y1+i-3, 
		 gConfig.LabelColor.val, db);
    }
    
    WriteStr(theScreen.x1-25, theScreen.y1-15, DEFKEYCOLOR, "dB");
  }
}


/*******************************************************************/
void SVGA_Display::DrawXAxis()
{
  if (!gConfig.NoText.val) {
    int i;
    int lInt=Width()/gConfig.NumFreqLabels.val;
    int offset=lInt/2;
    float freq;
    int x, y, center;
    char label[8];
    int high=controls.FreqRange.high;
    int low=controls.FreqRange.low;
    int diff=high-low;

    float finterval=(float) diff/(float) Width();

    // rid old freqs
    gl_fillbox(theScreen.x1-30, theScreen.y2+3,
	       theScreen.w+1, FHEIGHT+3, 
	       gConfig.BackGround.val);   

    y=theScreen.y2+6;

    int w=Width();
    int c=0;
    if (lInt<1) lInt=1;
    for (i=offset; i<w; i+=lInt, c++) {
      int adj=i+controls.FreqLabOffset.val;

      x=theScreen.x1+adj;

      freq=(float) adj*finterval;

      if (controls.LogXScale.isOn) {
	freq=LogNum(freq, (float) diff)+low;
      } else 
	freq+=(float) low;

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

      center=strlen(label)*FWIDTH/2;
      WriteStr(x-center, y, gConfig.LabelColor.val, label);

      gl_line(x, theScreen.y2+1, 
	      x, theScreen.y2+4, 
	      DEFTICKMARKCOLOR);
    }

    WriteStr(theScreen.x1-30, theScreen.y2+8, DEFKEYCOLOR, "Hz");
  }
}


/*******************************************************************/
void SVGA_Display::SetupScreen()
{
  DOUT("setup screen");
  gl_clearscreen(gConfig.BackGround.val);  
  gl_setwritemode(WRITEMODE_MASKED);
  
  if (!gConfig.NoText.val) 
    WriteStr(1, 1, 245,
	     VERSION);

  DOUT("setup screen done");
}

/*******************************************************************/
#define SwitchOn(a,b) ((a) ? (b) : ' ')
#define WordOn(a,b,c) ((a) ? (b) : (c))
void SVGA_Display::WriteInfo()
{
  if (!gConfig.NoText.val && display_on) {
    int n;
    char label[5][128];

    DisableClipping();
    
    n=0;
    sprintf(label[n++], "%s %d-bit mode", 
	    gConfig.Device.val, 
	    NUMBITS);   
    sprintf(label[n++], 
	    "size:%d rate:%d, %d bands", 
	    s->GetParms().sampbufsize, s->GetParms().Sample_Rate, num);    
    sprintf(label[n++], "attn:%d vpos:%d bt:%d [%c] l[%c %c]", 
	    controls.Attenuation.val,
	    controls.VPosition.val,
	    type,
	    SwitchOn(gConfig.HWindow.val, 'w'),
	    SwitchOn(controls.LogXScale.isOn, 'x'), 
	    SwitchOn(controls.LogYScale.isOn, 'y') 
	    );
    
    n=0; // reset string counter
    
    // write strings (depending on screen size)

    // clear info area with background color
    gl_fillbox(33, 10, theScreen.w-33, 18, gConfig.BackGround.val);
    
    if (theScreen.w <= 320) {
      WriteStr(120, 1, gConfig.ForeGround.val, label[n++]);
      WriteStr(33, 10, gConfig.ForeGround.val, label[n++]);
      WriteStr(33, 19, gConfig.ForeGround.val, label[n++]);

    } else { // screen is larger so add more info
      
      gl_fillbox(105, 1, theScreen.w-105, FHEIGHT, 
		 gConfig.BackGround.val);
      sprintf(label[3], "%d-bit cal. offset: %d  "
	      "freq. range: %d..%d",
	      NUMBITS, gConfig.Bits8.val 
	      ? gConfig.Offset8.val
	      : gConfig.Offset16.val,
	      controls.FreqRange.low,
	      controls.FreqRange.high);
      strcpy(label[4], "? - help");

      if (theScreen.w <= 640) {
	int m;
	m=FWIDTH*(strlen(label[n])+1);
	WriteStr(105, 1, gConfig.ForeGround.val, label[n++]);
	WriteStr(105+m, 1, gConfig.ForeGround.val, label[n++]);
	WriteStr(105, 10, gConfig.ForeGround.val, label[n++]);
	WriteStr(105, 19, gConfig.ForeGround.val, label[n++]);
	m=FWIDTH*(strlen(label[n])+1);
	WriteStr(theScreen.w-m, 10, gConfig.ForeGround.val, label[n++]);
      }
      else if (theScreen.w <= 800) {
	int m;
	m=FWIDTH*(strlen(label[n])+1);
	WriteStr(105, 1, gConfig.ForeGround.val, label[n++]);
	WriteStr(105+m, 1, gConfig.ForeGround.val, label[n++]);
	WriteStr(105, 10, gConfig.ForeGround.val, label[n++]);
	WriteStr(105, 19, gConfig.ForeGround.val, label[n++]);
	m=FWIDTH*(strlen(label[n])+2);
	WriteStr(theScreen.w-m, 10, gConfig.ForeGround.val, label[n++]);
      }
      else if (theScreen.w <= 1024) {
	int m;
	m=FWIDTH*(strlen(label[n])+1);
	WriteStr(105, 1, gConfig.ForeGround.val, label[n++]);
	WriteStr(105+m, 1, gConfig.ForeGround.val, label[n++]);
	WriteStr(105, 10, gConfig.ForeGround.val, label[n++]);
	WriteStr(105, 19, gConfig.ForeGround.val, label[n++]);
	m=FWIDTH*(strlen(label[n])+2);
	WriteStr(theScreen.w-m, 10, gConfig.ForeGround.val, label[n++]);
      } // end elseif
    } // first screen if
    EnableClipping();
  } // endif
}
#undef SwitchOn
#undef WordOn



/*******************************************************************/
void SVGA_Display::EnableClipping() 
{ 
  if (clipping_enabled) {
    int frame=2;
    gl_enableclipping();
    gl_setclippingwindow(theScreen.x1+frame, theScreen.y1+frame,
			 theScreen.x2-frame, theScreen.y2-frame);
  }
}


/*******************************************************************/
void SVGA_Display::DisableClipping() 
{
  if (clipping_enabled)
    gl_disableclipping();
}


/*******************************************************************/
void SVGA_Display::WriteStr(int x, int y, int color, char *str)
{
  if (fp) {
    FontColor(color);
    gl_write(x, y, str);
  }
}


/*******************************************************************/
void SVGA_Display::FontColor(int color)
{    
  gl_colorfont(FWIDTH, FHEIGHT, color, fp);
}

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