
#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 100

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

cCpuMonOsd::cCpuMonOsd(void): cThread("CPU Utilisation OSD"), cOsdObject(true)
{
  arrowcolor           = clrWhite;
  bgcolor              = 0xAA000000;
  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++;
  }
};

void cCpuMonOsd::show_functions()
{
  unsigned int ind = 0;
  tMeasured* val_head = Fetcher->get_act_val();
  tMeasured* act_val = val_head;
  
  while(act_val->next != val_head )
  {
      for(unsigned int prio = 0; prio < 4; prio++)
      {

        if( parms->functions() & (1 << act_val->order[prio].func) )
	{	
          switch( parms->d_mode() )
	  {
	    case DMODE_POINTS: // display mode points (draw just a point)
	      if(ind != 0)
              osd->DrawRectangle((VAL_COUNT - ind) * 2 + XBASE - POINT_WIDTH,
                                 YBASE - POINT_WIDTH - 2 * act_val->val[act_val->order[prio].func],
  	    	                 (VAL_COUNT - ind) * 2 + XBASE + POINT_WIDTH,
                                 YBASE + POINT_WIDTH - 2 * act_val->val[act_val->order[prio].func],
	  	                 functioncolor[act_val->order[prio].func]);
	      break;
	      
	    case DMODE_LINES: // display mode lines (combine 2 points with a line)
	      if(ind != 0)
	        DrawLine((VAL_COUNT - ind) * 2 + XBASE - 1,
                         YBASE - 2 * act_val->val[act_val->order[prio].func],
  	                 (VAL_COUNT - ind) * 2 + XBASE,
	                 YBASE - 2 * act_val->next->val[act_val->order[prio].func],
	                 POINT_WIDTH,
	                 functioncolor[act_val->order[prio].func]);
	      break;
	      
	    case DMODE_SOLID: // display mode solid (draw a vertical line beginning at the x axis)
	      if(ind != 0)
              osd->DrawRectangle((VAL_COUNT - ind) * 2 + XBASE - POINT_WIDTH,
                                 YBASE - POINT_WIDTH - 2 * act_val->val[act_val->order[prio].func],
  	    	                 (VAL_COUNT - ind) * 2 + XBASE + POINT_WIDTH,
                                 YBASE,
	  	                 functioncolor[act_val->order[prio].func]);
	      break;       
	      
	    default:
	      dsyslog("CPUmon: d_mode wrong value\n");
	      break;
	  }; // SWITCH
	}; // IF_FUNCTIONS&PRIO^2
      }; // FOR_PRIO
      
      act_val = act_val->next;
      ind++;
  }; // WHILE
};


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];
  tMeasured* act_val = Fetcher->get_act_val();
      
  sprintf(val,"%i", act_val->val[USER]);
  osd->DrawText(XBASE + 120, YBASE + 10,   val,   functioncolor[USER], clrTransparent, font, 50, 50, 6);
  sprintf(val,"%i", act_val->val[NICE]);
  osd->DrawText(XBASE + 120, YBASE + 40,   val,   functioncolor[NICE], clrTransparent, font, 50, 50, 6);
  sprintf(val,"%i", act_val->val[SYSTEM]);
  osd->DrawText(XBASE + 120, YBASE + 70,   val,   functioncolor[SYSTEM], clrTransparent, font, 50, 50, 6);
  sprintf(val,"%i", act_val->val[IDLE]);
  osd->DrawText(XBASE + 120, YBASE + 100,  val,   functioncolor[IDLE], clrTransparent, font, 50, 50, 6);
  
  sprintf(val,"SUM: %3i", act_val->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 kUp:
        Fetcher->reset_values();
	break;	
	
      case kDown:
        if( !parms->grid() )
	  parms->set_grid( true );
	else
	  parms->set_grid( false );
	break;
			
#if CPUMON_SCREENSHOT
      case kLeft:
        cDevice::PrimaryDevice()->GrabImageFile(s_fn, "jpg", 95, 768, 576);
	return osContinue;					    
        break;
#endif

      case kNone:
        return osContinue;
	break;

//      case kBack:
      default:
	Cancel(3);
	return osEnd;
	break;
//        return state;
//	break;
    }
    state = osContinue;
  }
  return state;
};
