/*
 * mosaicosd.c: A plugin for the Video Disk Recorder
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id$
 */
 
#include "mosaicosd.h"
#include "config.h"
#include <vdr/config.h>
#include <vdr/device.h>
#include <vdr/plugin.h>
#include <vdr/channels.h>
#include <vdr/remote.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "symbols/up.xpm"
#include "symbols/down.xpm"

extern int MosaicChannelNumber;

cMosaicOsd::cMosaicOsd(void)
{
  osd = NULL;
  group = -1;
  number = 0;
  lastTime = cTimeMs::Now();
  extraInfo = false;
  offset = 0;
  lines = 0;
  Present = NULL;
  Following = NULL;
  pArray = NULL;
  currentEvent = 0;
  row = 0;
  col = 0;
  mos = 0;

  numTracks = 0;
 
  track[0][0][0] = 0;
  track[0][0][1] = 1;
  track[0][0][2] = 2;
  track[0][0][3] = 3;
  track[0][0][4] = 4;

  track[0][1][0] = 5;
  track[0][1][1] = 6;
  track[0][1][2] = 7;
  track[0][1][3] = 8;
  track[0][1][4] = 9;

  track[0][2][0] = 10;
  track[0][2][1] = 11;
  track[0][2][2] = 12;
  track[0][2][3] = 13;
  track[0][2][4] = 14;

  track[0][3][0] = 15;
  track[0][3][1] = 16;
  track[0][3][2] = 17;
  track[0][3][3] = 18;
  track[0][3][4] = 19;

  track[1][0][0] = 0;
  track[1][0][1] = 1;
  track[1][0][2] = 2;
  track[1][0][3] = 3;
  track[1][0][4] = 4;

  track[1][1][0] = 5;
  track[1][1][1] = 6;
  track[1][1][2] = 7;
  track[1][1][3] = 8;
  track[1][1][4] = 9;

  track[1][2][0] = 10;
  track[1][2][1] = 11;
  track[1][2][2] = 12;
  track[1][2][3] = 13;
  track[1][2][4] = 14;

  track[1][3][0] = 15;
  track[1][3][1] = 16;
  track[1][3][2] = 17;
  track[1][3][3] = 18;
  track[1][3][4] = 19;


}
 
cMosaicOsd::~cMosaicOsd(void)
{
  delete osd;
}
void cMosaicOsd::DrawSymbol(int x, int y, char *SymbolName[], tColor ColorFg)
{
  cBitmap bm(SymbolName);
  osd->DrawBitmap(x, y, bm, ColorFg, pal[0]);
}



void cMosaicOsd::DisplayChannel(const cChannel *Channel)
{
  int BufSize = 255;
  if (Channel) {
     if (Channel->GroupSep())
        snprintf(ChanName, BufSize, "* %s *", Channel->Name());
     else
        snprintf(ChanName, BufSize, "%d%s %s", Channel->Number(), number ? "-" : "", Channel->Name());
     }
  else if (number)
     snprintf(ChanName, BufSize, "%d-", number);
  else
     snprintf(ChanName, BufSize, "%s", " *** Invalid Channel *** ");
/*  // Draw background for channel name
  osd->DrawRectangle(2, 2, config.width-2, LINEHEIGHT-2, pal[3]);
  // Draw channel number and name
  osd->DrawText(10, 3, ChanName,pal[2],pal[3],font);  */
  DisplayBitmap();
}

static int CompareEventTime(const void *p1, const void *p2)
{
  return (int)((*(cEvent **)p1)->StartTime() - (*(cEvent **)p2)->StartTime());
}

void cMosaicOsd::UpdateEPGInfo(int NowNextPrev)
{
    // Start to reset all previous info
    textPresentInfo = NULL;
    textFollowingInfo = NULL;
    titlePresentInfo = NULL;
    titleFollowingInfo = NULL;
    subtitlePresentInfo = NULL;
    subtitleFollowingInfo = NULL;
    cSchedulesLock SchedulesLock;
    const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock);
    if (Schedules) {
      const cSchedule *Schedule = Schedules->GetSchedule(Channels.GetByNumber(MosaicChannelNumber)->GetChannelID());
      if (Schedule) {
        // Get Present event
        time_t now = time(NULL);
        int a=0;
        int num = ( Schedule->Events() )->Count();
	switch (NowNextPrev) {
	  case 1: // Now
            // Free previous Array
            Present = NULL;
            Following = NULL;
            if (pArray) {
              free(pArray);
              pArray = NULL;
            }
            pArray = MALLOC(const cEvent *, num);
            if (pArray) {
              int numreal = 0;
              for (int a = 0; a < num; a++) {
                const cEvent *EventInfo = ( Schedule->Events() )->Get(a);
                // if (EventInfo->GetTime() + EventInfo->GetDuration() > now)
                    pArray[numreal++] = EventInfo;
              }
              qsort(pArray, numreal, sizeof(cEvent *), CompareEventTime);
            }
            // Find the current event
            while ( (a < num) && ( (pArray[a])->StartTime() + (pArray[a])->Duration() < now ) ) {
              a++;
            }
            currentEvent = a;
            if (a<num) {
              Present = pArray[currentEvent];
              if (currentEvent + 1 < num)
                Following = pArray[currentEvent + 1];
            }
            break;
	  case 2: // Next
            if (currentEvent + 1 < num) {
              Present = NULL;
              Following = NULL;
              currentEvent++;
              Present = pArray[currentEvent];
              if (currentEvent + 1 < num)
                Following = pArray[currentEvent + 1];
            }
            break;
	  case 3: // Prev
            if (currentEvent != 0) {
              Following = Present;
              if (currentEvent  > 0) {
                currentEvent--;
                Present = pArray[currentEvent];
              }
            }
            break;
	}
        // Get Event details
        if (Present) {
          // printf("[%d] PRESENT:", currentEvent);
          titlePresentInfo = Present->Title();
          subtitlePresentInfo = Present->ShortText();
          textPresentInfo = Present->Description();
        }
        // Get Following event
        if (Following) {
          // printf("[%d] FOLLOWING:", currentEvent+1);
          titleFollowingInfo = Following->Title();
          subtitleFollowingInfo = Following->ShortText();
          textFollowingInfo = Following->Description();
	  // printf("  --->%s\n", Following->GetTitle());
        }
      }
    }
}

void cMosaicOsd::Show()
{
  
  if (config.mosaic1 > 0)
      mosaic[0] = config.mosaic1;
  if (config.mosaic2 > 0)
      mosaic[1] = config.mosaic2;
  if (config.mosaic3 > 0)
      mosaic[2] = config.mosaic3;
  if (config.mosaic4 > 0)
      mosaic[3] = config.mosaic4;

  parse_file(AddDirectory(cPlugin::ConfigDirectory(), "mosaic.conf"));

  // find the actual current
  cChannel *Channel = Channels.GetByNumber(cDevice::PrimaryDevice()->CurrentChannel());
  if (Channel) {
    ChannelOrigNumber = Channel->Number();
  }
  Channel =  Channels.GetByNumber(mosaic[0]);
  if (Channel){
         cDevice::PrimaryDevice()->SwitchChannel(Channel, true);
   }
   for (int i = ttAudioFirst; i <= ttDolbyLast; i++) {
      const tTrackId *TrackId = cDevice::PrimaryDevice()->GetTrack(eTrackType(i));
      if (TrackId && TrackId->id) {
         types[numTracks] = eTrackType(i);
         numTracks++;
      }
    }
    MosaicChannelNumber = mosa[mos][row][col];
    
    osd=cOsdProvider::NewOsd(config.originx, config.originy);
    if (osd) {
     int bottom = config.height - (3+2*LINEHEIGHT);
     int x0 = 70;

     //tArea Areas[] = { { 0, 0, config.width-1, bottom - 1, 2 }, { x0, bottom, config.width-1, config.height-1,4 } };

     tArea Areas[] = { { 0, 0, config.width-1, bottom - 1, 2 }, { 0, bottom, x0-1, config.height-1,4 }, { x0, bottom, config.width-1, config.height-1,4 } };

     osd->SetAreas(Areas,sizeof(Areas)/sizeof(tArea));

 //   tArea Area = { 0, 0, config.width-1, config.height-1, 4};
 //   osd->SetAreas(&Area, 1);
      SendPalette();
      UpdateEPGInfo(1);
      DrawMenu(0,0);
      DisplayBitmap();
    }

}

int cMosaicOsd::parse_file(const char *Filename)
{
 esyslog("MOSAIC: config file '%s'", Filename);
FILE *f = fopen(Filename, "r");
if (f)
{
  int idxch;
  int cnt = 0;
  int mosch = -1;
  int nbch = -2;
  char *s;
  cReadLine *FileOps= new cReadLine();
  cReadLine ReadLine;
  while ( (s = ReadLine.Read(f) ) != NULL )
   {
	//s = skipspace(s);
	if ( nbch == -2 )
        {
           if (strcmp(s, "MOSAIQUE") == 0)
	    {
	       mosch++;
	       nbch = -1;
	    }
	}
	else if (*s++ == '#')
	{
	   cnt++;
           esyslog("MOSAIC: Line '%s'", s);
	}
	else
	{
	  esyslog("MOSAIC: Channel : '%s'", s);
          if (  nbch < 19) {
	   idxch = GetChannelNumberByName(s);
           esyslog("MOSAIC: Channel number : '%d'", idxch);
           if ( idxch == 0 )
                 idxch = mosaic[mosch];
              nbch++;
              if ( nbch < 5) {
            	   mosa[mosch][0][nbch] = idxch;
              } else if ( nbch < 10) {
            	   mosa[mosch][1][nbch-5] = idxch;
              } else if ( nbch < 15) {
                 mosa[mosch][2][nbch-10] = idxch;
              } else {
              	 mosa[mosch][3][nbch-15] = idxch;
              }
              if ( nbch == 19)
                  nbch = -2;
          }
        }
    }
    return 1;
 }
 return 0;
}

int cMosaicOsd::GetChannelNumberByName(char *name)
{
    for (cChannel *channelname = Channels.First(); channelname; channelname = Channels.Next(channelname)) {
        if (strcmp(channelname->Name(), name)==0)
            return channelname->Number();
    }
    return 0;
}


eOSState cMosaicOsd::ProcessKey(eKeys Key)
{
  eOSState state = cOsdObject::ProcessKey(Key);
  if (state == osUnknown) {
    switch (Key) {
      case kBack:
         if (extraInfo == false) {
            cChannel *Channel =  Channels.GetByNumber(ChannelOrigNumber);
            if (Channel){
                cDevice::PrimaryDevice()->SwitchChannel(Channel, true);
            }
           return osEnd;
	 } else {
           // Display normal info
           extraInfo = false;
           DisplayBitmap();
           DrawMenu(0,0);
           DisplayBitmap();
           return osContinue;
         }
        case k0 ... k9:
          if (extraInfo == false) {
             cRemote::Put(Key);
             return osEnd;
           }
   /*   case k0:
         if (extraInfo == false) {
           if (number == 0) {
              // keep the "Toggle channels" function working
              // FIXME Well, it doesn't :)
//              cRemote::Put(Key);
              return osContinue;
              }
         }
      case k1 ... k9:
         if (extraInfo == false) {
           if (number >= 0) {
              number = number * 10 + Key - k0;
              if (number > 0) {
                 cChannel *channel = Channels.GetByNumber(number);
	         // Display the current channel
                 DisplayChannel(channel);
                 lastTime = cTimeMs::Now();
                 }
              }
	 }
         break; */
      case kLeft|k_Repeat:
      case kLeft:
      case kRight|k_Repeat:
      case kRight:
	 if (extraInfo == false) {
              int oldmos = mos;
              if (NORMALKEY(Key) == kRight) 
               {
                   if (col < 4) {
                      col++;
                   } else {
                      col = 0;
                      if (mos < 3 && mosaic[mos+1] > 0) {
                         mos++;
                      } else {
                         mos = 0;
                      }
                   }
               } else 
               {
                   if (col > 0) {
                      col--;
                   } else {
                      col = 4;
                      if (mos > 0) {
                         mos--;
                      } else {
                         mos = 3;
                         while ( mosaic[mos] == 0)
                           mos--;
                      }
                   }
               }
               if ( mos != oldmos ) {
                  cChannel *Channel =  Channels.GetByNumber(mosaic[mos]);
                  if (Channel){
                      cChannel *channelold =  Channels.GetByNumber(mosaic[oldmos]);
		      cDevice::PrimaryDevice()->DetachAll(channelold->Vpid());
                      cDevice::PrimaryDevice()->SwitchChannel(Channel, true);
                  }
               }
               MosaicChannelNumber = mosa[mos][row][col];
               UpdateEPGInfo(1);
               DrawMenu(-16,1);
               DisplayBitmap();

	    lastTime = cTimeMs::Now();
	}
        break;
      case kUp|k_Repeat:
      case kUp:
      case kDown|k_Repeat:
      case kDown: {
	 if (extraInfo == false) {
              if (NORMALKEY(Key) == kDown) 
               {
                   if (row < 3) {
                      row++;
                   } else {
                      row = 0;
                   }
               } else 
               {
                   if (row > 0) {
                      row--;
                   } else {
                      row = 3;
                   }
               }
              MosaicChannelNumber = mosa[mos][row][col];
              UpdateEPGInfo(1);
              DrawMenu(-16,1);
              DisplayBitmap();
	 } else {
           // Scroll extra info up
	   if (NORMALKEY(Key) == kUp) {
              if (offset > 0)
	          offset--;
           } else {
	      if (NBLINES + offset < lines)
	          offset++;
           }
           extraInfo = true;
           DisplayExtraInfo();
           DisplayBitmap();
           return osContinue;
	   }
         break;
        }
      case kNone:
	 if (extraInfo == false) {
	    if (number && (int)cTimeMs::Now() - lastTime > 1000) {
              if (Channels.GetByNumber(number)) {
                MosaicChannelNumber = number;
		lastTime = cTimeMs::Now();
	        number = 0;
                cChannel *channel = Channels.GetByNumber(MosaicChannelNumber);
                if (channel)
                  group = channel->Index()-1;
	      } else {
                number = 0;
		 lastTime = cTimeMs::Now();
                return osContinue;
              }
              DisplayBitmap();
              UpdateEPGInfo(1);
              DrawMenu(0,0);
              DisplayBitmap();
              return osContinue;
            }
	  }
         break;
      case kOk:   
	 if (extraInfo == false) {
             MosaicChannelNumber = mosa[mos][row][col];
             cChannel *Channel =  Channels.GetByNumber(MosaicChannelNumber);
             if (Channel)
               cDevice::PrimaryDevice()->SwitchChannel(Channel, true);
             return osEnd;
	} else {
          extraInfo = false;
          cChannel *Channel =  Channels.GetByNumber(MosaicChannelNumber);
          if (Channel)
            cDevice::PrimaryDevice()->SwitchChannel(Channel, true);
          return osEnd;
	}
      case kRed: {
	 if ((extraInfo == false) && (!isempty(textPresentInfo))) {
           // Display extra infos
           extraInfo = true;
           offset = 0;
	   type = 1; // PRESENT
           DisplayExtraInfo();
           DisplayBitmap();
           return osContinue;
         } else {
           // Display normal info
           extraInfo = false;
           DisplayBitmap();
           DrawMenu(0,0);
           DisplayBitmap();
           return osContinue;
	 }
      }
      break;
      case kGreen: {
	 if ((extraInfo == false) && (!isempty(textFollowingInfo))) {
           // Display extra infos
           extraInfo = true;
           offset = 0;
	   type = 2; // PRESENT
           DisplayExtraInfo();
           DisplayBitmap();
           return osContinue;
         } else {
           // Display normal info
           extraInfo = false;
           DisplayBitmap();
           DrawMenu(0,0);
           DisplayBitmap();
           return osContinue;
	 }
      }
      break;
      case kYellow|k_Repeat:
      case kYellow: {
	 if (extraInfo == false) {
           // Scroll back in time
           UpdateEPGInfo(3);
           DrawMenu(0,0);
           DisplayBitmap();
           usleep(100000);
           DisplayBitmap();
           return osContinue;
	 }
      }
      break;
      case kBlue|k_Repeat:
      case kBlue: {
	 if (extraInfo == false) {
           // Scroll back in time
           UpdateEPGInfo(2);
           DrawMenu(0,0);
           DisplayBitmap();
           usleep(100000);
           DisplayBitmap();
           return osContinue;
	 }
      }
      break;
      default:
         return state;
    };
    if (extraInfo == false) {
      if ((int)cTimeMs::Now() - lastTime < 5000) {
        DisplayBitmap();
        DrawMenu(0,0);
        DisplayBitmap();
        return osContinue;
      }
    }
  }
  return osContinue;
}

void cMosaicOsd::SendPalette() {
  
    pal[10]= 0xFFCC0000; // 70% Red
    pal[11]= 0x00CC00FF; // 70% Green
    pal[12]= 0xFF00FCFC;
    pal[13]= 0xFF00FCFC;
    pal[14]= 0xFF00FCFC;
    pal[15]= 0xFF00FCFC;
    // Main window color
    pal[0]= 0x00000000;
    SetColor(1,   140, 128, 50,  200);
    // Line color
    SetColor(2,   0,   0,   0, 200);
    // Channel name background, Time and titles color
    SetColor(3, 255, 255, 255, 200);
    // Yellow button
    SetColor(4,   0, 255, 255, 200);
    // Blue button
    SetColor(5, 255,   0,   0, 200);
    // Green button color
    SetColor(6,   0, 255,   0, 200);
    // Dark Yellow
    SetColor(7,   0,  50,  50, 200);
    // Darker color for second half background
    SetColor(8, 90, 150, 150,200);
    // Subtitle text
    SetColor(9,   0, 255, 255, 200);
  
}

void cMosaicOsd::SetColor(int Index, int Red, int Green, int Blue, int Alpha) {
  if ((Index >= 0) && (Index <= 15) && (Red >= 0) && (Red <= 255) && (Blue >= 0) && (Blue <= 255) && (Green >= 0) && (Green <= 255)&& (Alpha >= 0) && (Alpha <= 255)) {
    pal[Index] = 0;
    pal[Index] += Alpha;
    pal[Index] <<= 8;
    pal[Index] += Blue;
    pal[Index] <<= 8;
    pal[Index] += Green;
    pal[Index] <<= 8;
    pal[Index] += Red;
  }
}

void cMosaicOsd::ClearBitmap() {
  osd->DrawRectangle(0, 0, config.width, config.height, pal[0]);
}

void cMosaicOsd::DrawMenu(int delta, int highlight) {

  ClearBitmap();
    if (mos == 0) {
        if ( row == 0 && col < 4) {
            cDevice::PrimaryDevice()->SetCurrentAudioTrack(types[track[1][row][col+1]]);
        } else if ( row == 0 && col == 4) {
            cDevice::PrimaryDevice()->SetCurrentAudioTrack(types[5]);
        } else if ( row == 1 && col == 0) {
            cDevice::PrimaryDevice()->SetCurrentAudioTrack(types[0]);
        } else {
            cDevice::PrimaryDevice()->SetCurrentAudioTrack(types[track[1][row][col]]);
        }
     }
    else {
      cDevice::PrimaryDevice()->SetCurrentAudioTrack(types[track[1][row][col]]);
    }
  DisplayInfo(delta);
}

void cMosaicOsd::DisplayBitmap() {
  osd->Flush();
}

// to display channel Infos ... 
void cMosaicOsd::DisplayInfo(int delta) {
  
  const int t = 140;
  
  bool withInfo=true;
  int width = config.width-1-config.right;
  int top = config.top;
  int bottom = config.height - (3+2*LINEHEIGHT);
  int yorig = bottom;//config.height+LINEHEIGHT-(2+LINEHEIGHT)*3;
  int x0 = 70;
  //osd->DrawRectangle(2, 2, config.width-10, yorig-10, pal[6]);
  

   for(int x=1; x < config.width-1; x++){
      for(int y=1; y<(bottom-1); y++){
        if( ((row*(bottom-config.bottom)/4)-5 < (y-top) && (y-top)< (row*(bottom-config.bottom)/4)+5 )|| ( ((row+1)*(bottom-config.bottom)/4)-5 < (y-top) && (y-top) < ((row+1)*(bottom-config.bottom)/4)+5 )) {
            if( (col*width/5)-5 < x &&  x < ((col+1)*width/5)+5 ) {
                osd->DrawPixel(x, y, pal[10]);
             }
         }
        if(( (col*width/5)-5 < x && x < (col*width/5)+5 ) || ( ((col+1)*width/5)-5 < x && x < ((col+1)*width/5)+5)) {
            if( (row*(bottom-config.bottom)/4)-5 < (y-top) && (y-top) < ((row+1)*(bottom-config.bottom)/4)+5 ) {
               osd->DrawPixel(x, y, pal[10]);
            }
         }
      }
    }
  if (config.showlogos) {
    const char *txt;
    cChannel *channel = Channels.GetByNumber(MosaicChannelNumber);
    if (channel) {
        txt = channel->Name();
    } else {
        txt = "MOSAIQUE";
    }
    const char *ConfigDirectory=cPlugin::ConfigDirectory("../logos");
    //if (config.logos)
    //   ConfigDirectory=config.logos;
    char *fname=new char[strlen(ConfigDirectory) + 1 + strlen(txt) + strlen(".xpm") + 1];
    sprintf(fname,"%s/%s.xpm",ConfigDirectory,txt);
    DrawXpm(fname, osd, 3, bottom+3, 0, 0);
  } else {
    osd->DrawRectangle( 3, bottom+3, 3+64, bottom+3+48, pal[3]);
  }

//  if (withInfo) {
    // Draw channel number and name
//  osd->DrawText(10, 3, ChanName, pal[2],pal[3],font);
  osd->DrawRectangle(x0, bottom, width, config.height, pal[3]);

    if (!isempty(titlePresentInfo)) {
  osd->DrawText(x0, yorig, Present->GetTimeString(), pal[2],pal[3],font);
  osd->DrawText(t, yorig, titlePresentInfo, pal[2],pal[3],font);
    }

    if (!isempty(titleFollowingInfo)) {
      osd->DrawText(x0, yorig+3+LINEHEIGHT, Following->GetTimeString(), pal[2],pal[3],font);
      osd->DrawText(t, yorig+3+LINEHEIGHT, titleFollowingInfo, pal[2], pal[3],font);
    }
 


  // Draw arrows
  DrawSymbol(width - 25, bottom, up_xpm, pal[4]);
  DrawSymbol(width - 25, config.height - 2 - LINEHEIGHT, down_xpm, pal[5]);
/*  for(int y=3+LINEHEIGHT; y < config.height-2; y++){
    osd->DrawPixel(config.width - 29, y, pal[2]);
    osd->DrawPixel(config.width - 30, y, pal[2]);
  } */

  // Time bar
  if ( (Present) && (Present->StartTime() < time(NULL)) && ( time(NULL) <= Present->StartTime() + Present->Duration()) ) {
    int width;
    int height;
    float percent = 0;

    width=6; //135
    height=2*LINEHEIGHT;
    if (Present->Duration() != 0)
      percent =  (float)(time(NULL) - Present->StartTime()) / (float)(Present->Duration());
    if (percent < 0) percent = 0;
    if (percent > 1.0) percent = 1.0;
    // Draw the time bar
    for(int x=1; x<width; x++){
   //   osd->DrawPixel(135+x, (3+LINEHEIGHT)*2, pal[2]);
   //   osd->DrawPixel(10+x, (3+LINEHEIGHT)*2+1, pal[2]);
   //   osd->DrawPixel(10+x, (3+LINEHEIGHT)*2+height, pal[2]);
   //   osd->DrawPixel(10+x, (3+LINEHEIGHT)*2+height-1, pal[2]);
      for(int y=1; y<height; y++){
        if( y > (int)(percent*(float)height)) {
          // Elapsed part
          osd->DrawPixel(t-10+x, yorig+2+y, pal[3]);
        } else {
          // Remaining part
          osd->DrawPixel(t-10+x, yorig+2+y, pal[10]);
        }
      }
    }
 /*   for(int y=(3+LINEHEIGHT)*2; y<(3+LINEHEIGHT)*2+height; y++){
      osd->DrawPixel(10, y, pal[2]);
      osd->DrawPixel(10+1, y, pal[2]);
      osd->DrawPixel(10+width, y, pal[2]);
      osd->DrawPixel(10+width-1, y, pal[2]);
    } */
  }
}

void cMosaicOsd::DisplayExtraInfo() {
  int height = config.height;
  int x0 = 70;
  int width = config.width-1-config.right;
  char *text = NULL;
  int l = 0;
  char *t = NULL;
  int yorig = config.height-(6+2*LINEHEIGHT);
  int bottom = yorig;
  // Clear channel name 
/*  osd->DrawRectangle(2, 2, config.width-2, LINEHEIGHT-2,pal[3]);

// Display Title
if (type == 1)
    osd->DrawText(10, 3, titlePresentInfo, pal[2], pal[3],font);
  else if (type == 2)
    osd->DrawText(10, 3, titleFollowingInfo, pal[2], pal[3],font);
  else 
    osd->DrawText(10, 3, "-", pal[2], pal[3],font);
*/

  // Clean channel info
  osd->DrawRectangle(2, bottom + 3, width, height-1, pal[3]);
  // Display close window symbol
  // osd->DrawText(config.width - 25, 2, "X", pal[10],pal[1],font);
  if (type == 1) 
    text = WrapText(textPresentInfo, width - x0 - 60, &lines);
  else if (type == 2)
    text = WrapText(textFollowingInfo, width - x0 - 60, &lines);
  else 
    osd->DrawText(x0, yorig+3, "-", pal[2], pal[3],font);
  t = text;
  while (*t) {
        char *n = strchr(t, '\n');
        if (l >= offset) {
           if (n)
              *n = 0;
           osd->DrawText( x0 + 2, bottom + (3+LINEHEIGHT)*( l - offset), t, pal[2], pal[3],font);
           if (n)
              *n = '\n';
           else
              break;
           }
        if (!n)
           break;
        t = n + 1;
        if (++l >= NBLINES + offset)
           break;
        }
  // Draw lift arrows
  if (offset > 0)   DrawSymbol(width - 25, bottom + 3, up_xpm, pal[2]);
  if (NBLINES + offset < lines) DrawSymbol(width - 25, config.height - 2 - LINEHEIGHT, down_xpm, pal[2]);
}

char * cMosaicOsd::WrapText(const char *Text, int Width, int *Height) {
  int Lines = 1;
  char *t = strdup(Text);
  char *Blank = NULL;
  char *Delim = NULL;
  int w = 0;

  // Width *= cOsd::CellWidth();

  while (*t && t[strlen(t) - 1] == '\n')
        t[strlen(t) - 1] = 0; // skips trailing newlines

  for (char *p = t; *p; ) {
      if (*p == '|')
         *p = '\n';
      if (*p == '\n') {
         Lines++;
         w = 0;
         Blank = Delim = NULL;
         p++;
         continue;
         }
      else if (isspace(*p))
         Blank = p;
      int cw = font->Width(*p);
      if (w + cw > Width) {
         if (Blank) {
            *Blank = '\n';
            p = Blank;
            continue;
            }
         else {
            // Here's the ugly part, where we don't have any whitespace to
            // punch in a newline, so we need to make room for it:
            if (Delim)
               p = Delim + 1; // let's fall back to the most recent delimiter
            char *s = MALLOC(char, strlen(t) + 2); // The additional '\n' plus the terminating '\0'
            int l = p - t;
            strncpy(s, t, l);
            s[l] = '\n';
            strcpy(s + l + 1, p);
            free(t);
            t = s;
            p = t + l;
            continue;
            }
         }
      else
         w += cw;
      if (strchr("-.,:;!?_", *p)) {
         Delim = p;
         Blank = NULL;
         }
      p++;
      }

  *Height = Lines;
  return t;
}
