
#include "cpumonosd.h"

#define XBASE 10
// XBASE MINIMUM 10!
#define YBASE 250

#define XLENGTH (VAL_COUNT * 2)
#define YLENGTH 220 + 10

#define XGRID    50
#define YGRID    20
#define GRIDDIST 10

#define POINT_WIDTH 1

#define FUNCTION_EN_USER 0x1
#define FUNCTION_EN_NICE 0x2
#define FUNCTION_EN_SYST 0x4
#define FUNCTION_EN_IDLE 0x8

#define OSD_UPD_INTERVAL 200

#define USER   0
#define NICE   1
#define SYSTEM 2
#define IDLE   3

cCpuMonOsd::cCpuMonOsd(void): cThread("CPU Utilisation OSD"), cOsdObject(true)
{
  arrowcolor           = clrWhite;
  bgcolor              = 0xDD000000;
  gridcolor            = 0xFF777777;
  legendcolor_en       = clrWhite;
  legendcolor	       = 0xFF666666;
  rulercolor	       = 0xFFAAAA22;
  functioncolor[USER]  = clrRed;
  functioncolor[NICE]  = clrGreen;
  functioncolor[SYSTEM]= clrYellow;
  functioncolor[IDLE]  = 0xFF7777FF;
  osd 		       = NULL;
};

cCpuMonOsd *cCpuMonOsd::pInstance = NULL;

cCpuMonOsd *cCpuMonOsd::Instance(cCpuMonFetcher *pFetcher,
                                 cParameters *pparms)
{
  if( pInstance == NULL )
  {
    pInstance = new cCpuMonOsd();
    pInstance->Fetcher = pFetcher;
    pInstance->parms = pparms;
    return (pInstance);
  }
    return NULL;
    esyslog("cCpuMonOsd::Instance(): pInstance not NULL!\n");
};

cCpuMonOsd::~cCpuMonOsd(void)
{
  if( Running() )
    Cancel(3);

  if( osd )    
    delete osd;
  
  parms     = NULL;
  pInstance = NULL;
  Fetcher   = NULL;
};

void cCpuMonOsd::Action()
{
   const cFont *font = cFont::GetFont(fontOsd);
   // reset the osd area
   osd->DrawRectangle(0, 0, 399, 399, clrTransparent);
   // draw a plugin title
   osd->DrawText(32, 2, tr("CPU Utilisation"), clrBlack,  clrTransparent, font, 300, 50);
   osd->DrawText(30, 0, tr("CPU Utilisation"), clrYellow, clrTransparent, font, 300, 50);
   
  // perform a display loop
  while( Running() )
  {
    Reset();
    drawBoard();
    show_functions();

    if( parms->legend() )
    {
      deleteLegend();
      drawLegend();
    }
    else
      deleteLegend();
      
    osd->Flush();
    timer.Wait(OSD_UPD_INTERVAL);
  }
  Cancel(0);
};
 

void cCpuMonOsd::DrawLine(unsigned int x1,
                      unsigned int y1,
		      unsigned int x2,
		      unsigned int y2,
		      unsigned int lw,
		      tColor lcolor)
{
  unsigned int x = x1;
  double y_acc = 0;

  if (y1 > y2)
  {
    unsigned int h = y2;
    y2 = y1;
    y1 = h;

    h = x2;
    x2 = x1;
    x1 = h;
  }
  
  for (unsigned int y = y1; y <= y2; y++)
  {
    osd->DrawRectangle(x,
                       y,
  	  	       x + lw,
                       y + lw,
		       lcolor);
    y_acc = (double) y;
    if ((y_acc > (((double)y2 - (double) y1) / ((double)x2 - (double)x1))) &&
        (x < x2))
      x++;
  }
};


unsigned int cCpuMonOsd::get_next_value(unsigned int index,
                                        unsigned int rank)
{
  fs a[4];
  for( unsigned int x_ = 0; x_ < 4; x_++)
  {
    a[x_].val =  - Fetcher->values[index].val[x_];
    a[x_].func = x_;
  };
  
  qsort(a, 4, sizeof(fs), vergleich);  
  return a[rank].func;
};

void cCpuMonOsd::show_functions()
{
  unsigned int funcn = 0;
  
  for(unsigned int ind = 1; ind <= Fetcher->get_index(); ind++)
  {        
    if(Fetcher->values[ind].used)
    {
      for(unsigned int prio = 0; prio <= 3; prio++)
      {
        funcn = get_next_value(ind, prio);

        if( parms->functions() & (1 << funcn) )
	{	
          switch( parms->d_mode() )
	  {
	    case 0: // display mode points (draw just a point)
              osd->DrawRectangle(ind * 2 + XBASE - POINT_WIDTH,
                                 YBASE - POINT_WIDTH - 2 * Fetcher->values[ind].val[funcn],
  	    	                 ind * 2 + XBASE + POINT_WIDTH,
                                 YBASE + POINT_WIDTH - 2 * Fetcher->values[ind].val[funcn],
	  	                 functioncolor[funcn]);
	      break;
	      
	    case 1: // display mode lines (combine 2 points with a line)
	      DrawLine(ind * 2 + XBASE - 1,
                       YBASE - 2 * Fetcher->values[ind - 1].val[funcn],
  	               ind * 2 + XBASE,
	               YBASE - 2 * Fetcher->values[ind].val[funcn],
	               POINT_WIDTH,
	               functioncolor[funcn]);
	      break;
	      
	    case 2: // display mode solid (draw a vertical line from point to x axis)
              osd->DrawRectangle(ind * 2 + XBASE - POINT_WIDTH,
                                 YBASE - POINT_WIDTH - 2 * Fetcher->values[ind].val[funcn],
  	    	                 ind * 2 + XBASE + POINT_WIDTH,
                                 YBASE,
	  	                 functioncolor[funcn]);
	  	     
	      break;       
	      
	    default:
	      dsyslog("CPUmon: d_mode wrong value\n");
	      break;
	  }; // SWITCH
	}; // IF_FUNCTIONS&PRIO^2
      }; // FOR_PRIO
    }; // IF_VALUE_USED
  }; // FOR_IND

  // mark the actual position
  osd->DrawRectangle(XBASE + Fetcher->get_index() * 2,
                     YBASE - YLENGTH,
		     XBASE + Fetcher->get_index() * 2 + 2,
		     YBASE - 1,
		     rulercolor);
};

void cCpuMonOsd::Reset()
{
  osd->DrawRectangle(0, YBASE - YLENGTH - 10, XBASE + XLENGTH + 10, YBASE + 4, clrTransparent);
  osd->DrawRectangle(XBASE - 5, YBASE - YLENGTH - 5, XBASE + XLENGTH + 5, YBASE + 5, bgcolor);
};

void cCpuMonOsd::drawBoard(void)
{
  // draw x axis and x grid
  osd->DrawRectangle(XBASE, YBASE, XBASE + XLENGTH, YBASE + 1, arrowcolor);
  if( parms->grid() )
  {
    for(unsigned int a = XBASE + 2; a <= XLENGTH + XBASE; a++)
    {
      if( (a - XBASE) % XGRID == 0)
      {
        for(unsigned int b = 10; b < YLENGTH; b += GRIDDIST)
        osd->DrawRectangle(a, YBASE - b, a + 1, YBASE - b + 1, gridcolor);
      }
    }
  }
    
  // draw y axis and y grid
  osd->DrawRectangle(XBASE - 1, YBASE - YLENGTH, XBASE + 1, YBASE, arrowcolor);
  if( parms->grid() )
  {
    for(unsigned int a = YBASE - 2; a >= (YBASE - YLENGTH + 10); a--)
    {
      if( (YBASE - a) % YGRID == 0)
      {
        for(unsigned int b = XBASE; b < XLENGTH; b += GRIDDIST)
	  osd->DrawRectangle(XBASE + b, a, XBASE + b + 1, a, gridcolor);
      }
    }
  }

};

void cCpuMonOsd::drawLegend()
{
  const cFont *font = cFont::GetFont(fontOsd);
  // draw a small legend
  osd->DrawRectangle(XBASE - 5, YBASE + 5, XBASE + XLENGTH + 5, YBASE + 125, bgcolor);
  
  if(parms->functions() & FUNCTION_EN_USER)
    osd->DrawText(XBASE + 30, YBASE + 10,  "User", legendcolor_en, clrTransparent, font, 100, 50);
  else    
    osd->DrawText(XBASE + 30, YBASE + 10,  "User", legendcolor, clrTransparent, font, 100, 50);

  if(parms->functions() & FUNCTION_EN_NICE)
    osd->DrawText(XBASE + 30, YBASE + 40,  "Nice", legendcolor_en, clrTransparent, font, 100, 50);
  else    
    osd->DrawText(XBASE + 30, YBASE + 40,  "Nice", legendcolor, clrTransparent, font, 100, 50);

  if(parms->functions() & FUNCTION_EN_SYST)
    osd->DrawText(XBASE + 30, YBASE + 70,  "System", legendcolor_en, clrTransparent, font, 100, 50);
  else    
    osd->DrawText(XBASE + 30, YBASE + 70,  "System", legendcolor, clrTransparent, font, 100, 50);

  if(parms->functions() & FUNCTION_EN_IDLE)
    osd->DrawText(XBASE + 30, YBASE + 100,  "Idle", legendcolor_en, clrTransparent, font, 100, 50);
  else    
    osd->DrawText(XBASE + 30, YBASE + 100,  "Idle", legendcolor, clrTransparent, font, 100, 50);

  osd->DrawRectangle(XBASE, YBASE + 10,  XBASE + 20, YBASE + 30,  functioncolor[USER]);
  osd->DrawRectangle(XBASE, YBASE + 40,  XBASE + 20, YBASE + 60,  functioncolor[NICE]);
  osd->DrawRectangle(XBASE, YBASE + 70,  XBASE + 20, YBASE + 90,  functioncolor[SYSTEM]);
  osd->DrawRectangle(XBASE, YBASE + 100, XBASE + 20, YBASE + 120, functioncolor[IDLE]);
  
  
  char val[9];
  unsigned int a_ind = Fetcher->get_index();
      
  sprintf(val,"%i", Fetcher->values[a_ind].val[USER]);
  osd->DrawText(XBASE + 120, YBASE + 10,   val,   functioncolor[USER], clrTransparent, font, 50, 50, 6);
  sprintf(val,"%i", Fetcher->values[a_ind].val[NICE]);
  osd->DrawText(XBASE + 120, YBASE + 40,   val,   functioncolor[NICE], clrTransparent, font, 50, 50, 6);
  sprintf(val,"%i", Fetcher->values[a_ind].val[SYSTEM]);
  osd->DrawText(XBASE + 120, YBASE + 70,   val,   functioncolor[SYSTEM], clrTransparent, font, 50, 50, 6);
  sprintf(val,"%i", Fetcher->values[a_ind].val[IDLE]);
  osd->DrawText(XBASE + 120, YBASE + 100,  val,   functioncolor[IDLE], clrTransparent, font, 50, 50, 6);
  
  sprintf(val,"SUM: %3i", Fetcher->values[a_ind].sum);
  osd->DrawText(XBASE + 190, YBASE + 10,  val,   clrWhite, clrTransparent, font, 120, 50);
  
};

void cCpuMonOsd::deleteLegend()
{
  osd->DrawRectangle(XBASE - 5, YBASE  + 5, XBASE + XLENGTH + 5, YBASE + 125, clrTransparent);
};

void cCpuMonOsd::Show(void)
{
  osd = cOsdProvider::NewOsd(50,50);
  if( osd )
  {
    tArea Areas[] = {
                     {0,   0,   199, 199, 4},
                     {0,   200, 199, 399, 4},
		     {200, 0,   399, 199, 4},
		     {200, 200, 399, 399, 4}
		    };
    osd->CanHandleAreas(Areas,sizeof(Areas) / sizeof(tArea));
    osd->SetAreas(Areas,sizeof(Areas) / sizeof(tArea));
    
    Start();
  }
  else
    esyslog("cCpuMonOsd::Show(): something is wrong, OSD not opened -> check cpuosd.c\n");
};

eOSState cCpuMonOsd::ProcessKey(eKeys Key)
{
  eOSState state;
  state = cOsdObject::ProcessKey(Key);

#if CPUMON_SCREENSHOT
  char s_fn[] = "/tmp/cpumon.jpg";
#endif

  if (state == osUnknown)
  {
    switch (Key)
    {
      case kRed:
        if(parms->functions() & FUNCTION_EN_USER)
	  parms->set_functions( parms->functions() & ~FUNCTION_EN_USER);
	else
	  parms->set_functions( parms->functions() | FUNCTION_EN_USER);
        break;
	
      case kBlue:
        if(parms->functions() & FUNCTION_EN_IDLE)
	  parms->set_functions( parms->functions() & ~FUNCTION_EN_IDLE);
	else
	  parms->set_functions( parms->functions() | FUNCTION_EN_IDLE);
        break;
	
      case kGreen:
        if(parms->functions() & FUNCTION_EN_NICE)
	  parms->set_functions( parms->functions() & ~FUNCTION_EN_NICE);
	else
	  parms->set_functions( parms->functions() | FUNCTION_EN_NICE);
        break;
	
      case kYellow:
        if(parms->functions() & FUNCTION_EN_SYST)
	  parms->set_functions( parms->functions() & ~FUNCTION_EN_SYST);
	else
	  parms->set_functions( parms->functions() | FUNCTION_EN_SYST);
        break;
	
      case kOk:
        if( !parms->legend() )
          parms->set_legend( true );
	else
          parms->set_legend( false );
	break;
	
      case kDown:
        if( !parms->grid() )
	  parms->set_grid( true );
	else
	  parms->set_grid( false );
	break;
	
      case kBack:
	Cancel(3);
	return osEnd;
	break;
		
#if CPUMON_SCREENSHOT
      case kLeft:
        cDevice::PrimaryDevice()->GrabImageFile(s_fn, "jpg", 95, 768, 576);
	return osContinue;					    
        break;
#endif

      case kNone:
        return osContinue;
	break;
      default:
        return state;
	break;
    }
    state = osContinue;
  }
  return state;
};
