/*
 * statusandquestion: A plugin for the Video Disk Recorder
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id: display.c 0.1 2004/07/11 14:12:20 hflor Exp $
 */

#include <unistd.h>
#include "display.h"
#include "messages.h"
#include "statusandquestion.h"
#include "vdrtools.h"
#include <vdr/menu.h>
#if VDRVERSNUM >= 10307
#include <vdr/skins.h>
#endif
#include <vdr/status.h>
#include <vdr/remote.h>
#include <vdr/interface.h>
#include <vdr/menuitems.h>

cDisplayThread::cDisplayThread(void)
#if VDRVERSNUM > 10300
:cThread("Display messages on OSD")
#endif
{
  active = true;
  lasthousekeeping = 0;
}

cDisplayThread::~cDisplayThread(void)
{
}

void cDisplayThread::Action(void)
{
#ifdef SAQ_Debug
  dsyslog("%s: Display-Thread started (pid=%d)", plugin_name, getpid());
#endif
  active = true;
  while (active)
  {
    if (time(NULL) - lasthousekeeping > 30)
      Housekeeping();
#if VDRVERSNUM >= 10307
    if (!Skins.IsOpen() && Messages.SetLock(true))
#else
    if (!Interface->IsOpen() && Messages.SetLock(true))
#endif
    {
      cMessage *message = Messages.First();
      if (message)
      {
        Messages.Del(message, false); // message is in the future add the the responsequeue or deleted
#ifdef SAQ_Debug
        if (message->MessageTyp() == MessageTyp_Param)
          dsyslog("%s: new title=%c%s%c red=%c%s%c green=%c%s%c yellow=%c%s%c blue=%c%s%c at pos %d", plugin_name, DispText(message->Menu(MenuTyp_Title)), DispText(message->Menu(MenuTyp_Red)), DispText(message->Menu(MenuTyp_Green)), DispText(message->Menu(MenuTyp_Yellow)), DispText(message->Menu(MenuTyp_Blue)), message->Index() + 1);
        else if (message->MessageTyp() == MessageTyp_SetTime)
          dsyslog("%s: set timeout to %s%d seconds", plugin_name, message->Timeout() == 0 ? "default of " : "", message->Timeout() == 0 ? Setup.OSDMessageTime : message->Timeout());
        else 
          dsyslog("%s: %s with text '%s'", plugin_name, MessageTypName(message->MessageTyp()), message->Textorg());
#else
        if (verbose.u)
        {
          if (message->MessageTyp() == MessageTyp_Param)
            isyslog("%s: new title=%c%s%c red=%c%s%c green=%c%s%c yellow=%c%s%c blue=%c%s%c at pos %d", plugin_name, DispText(message->Menu(MenuTyp_Title)), DispText(message->Menu(MenuTyp_Red)), DispText(message->Menu(MenuTyp_Green)), DispText(message->Menu(MenuTyp_Yellow)), DispText(message->Menu(MenuTyp_Blue)), message->Index() + 1);
          else if (message->MessageTyp() == MessageTyp_SetTime)
            isyslog("%s: set timeout to %s%d seconds", plugin_name, message->Timeout() == 0 ? "default of " : "", message->Timeout() == 0 ? Setup.OSDMessageTime : message->Timeout());
          else 
            isyslog("%s: set timeout to %s%d seconds", plugin_name, message->Timeout() == 0 ? "default of " : "", message->Timeout() == 0 ? Setup.OSDMessageTime : message->Timeout());
        }
#endif
      }
      Messages.SetLock(false);
      if (message)
      {
        if (message->MessageTyp() == MessageTyp_Param)
        {
          for (int pos = MenuTyp_Title; pos <= MenuTyp_Blue; pos++)
            menu[pos] = message->Menu((eMenuTyp) pos);
          delete message;
        } else if (message->MessageTyp() == MessageTyp_SetTime)
        {
          timeout = message->Timeout();
          delete message;
        } else
        {
          eKeys response = k_Setup;
          dispid = message->ID();
#if VDRVERSNUM >= 10307
          controlstate = ControlState_MenuSelect;
          cRemote::Put(kMenu, true);
          while (controlstate != ControlState_Wait1)
          {
            usleep(100000);
          }
          controlstate = ControlState_Wait2;
          if (message->MessageTyp() == MessageTyp_Text)
          {
            cSkinDisplayMenu *displaymenu = Skins.Current()->DisplayMenu();
            if (displaymenu)
            {
              usleep(1000);
              displaymenu->SetTitle(menu[MenuTyp_Title]);
              displaymenu->SetButtons(menu[MenuTyp_Red], menu[MenuTyp_Green], menu[MenuTyp_Yellow], menu[MenuTyp_Blue]);
              displaymenu->SetText(message->Text(), false);
              cSkinDisplay::Current()->Flush();
              for (;;)
              {
                response = Wait(timeout == 0 ? Setup.OSDMessageTime : timeout);
                if (response == kUp || response == kDown || response == kLeft || response == kRight)
                  displaymenu->Scroll(response == kUp || response == kLeft, response == kLeft || response == kRight);
                else
                  break;
              }
              delete displaymenu;
            }
          } else
          {
            cSkinDisplayMessage *displayMessage = Skins.Current()->DisplayMessage();
            if (displayMessage)
            {
              usleep(1000);
              displayMessage->SetMessage((eMessageType) message->MessageTyp(), message->Text());
              cSkinDisplay::Current()->Flush();
              cStatus::MsgOsdStatusMessage(message->Text());
              response = Wait(timeout == 0 ? Setup.OSDMessageTime : timeout);
              delete displayMessage;
            }
          }
#else
          Interface->Open(1, -1); // open very small osd
          controlstate = ControlState_MenuSelect;
          cRemote::Put(kMenu, true);
          while (controlstate != ControlState_Wait1)
          {
            usleep(100000);
          }
          Interface->Close();
          controlstate = ControlState_Wait2;
          if (message->MessageTyp() == MessageTyp_Text)
          {
            Interface->Open();
            Interface->Clear();
            char *title;
            asprintf(&title, "%s\t%s", menu[MenuTyp_Title] ? menu[MenuTyp_Title] : "", DayDateTime(time(NULL)));
            Interface->Title(title);
            free(title);
            cMenuTextItem *menutextitem = new cMenuTextItem(message->Text(), 1, 2, Setup.OSDwidth - 2, Setup.OSDheight - 4);
            menutextitem->Display();
            Interface->Help(menu[MenuTyp_Red], menu[MenuTyp_Green], menu[MenuTyp_Yellow], menu[MenuTyp_Blue]);
            while (response == k_Setup)
            {
              response = Wait(timeout == 0 ? Setup.OSDMessageTime : timeout);
              if (menutextitem->ProcessKey(response) == osContinue)
              {
                Interface->ClearEol(1, 2); // bug in cMenuTextItem, scrollchars and text in the first and last line are not deleted
                Interface->ClearEol(1, Setup.OSDheight - 4);
                menutextitem->Display();
                response = k_Setup;
              }
            }
          } else
          {
            eDvbColor bg;
            eDvbColor fg;
            int lines = 1;
            char *z = strdup(message->Text());
            char *p = z;
            while (p && *p)
            {
              p = strchr(p, '\n');
              if (p)
              {
                p++;
                lines++;
              }
            }
            Interface->Open(0, -lines);
#ifdef HAVE_ELCHI
            if (Setup.Theme == themeVanilla)
              switch (message->MessageTyp())
              {
                case MessageTyp_Status:  bg = clrCyan;
                                         fg = clrBlack;
                                         break;
                case MessageTyp_Info:    bg = clrGreen;
                                         fg = clrBlack;
                                         break;
                case MessageTyp_Warning: bg = clrMagenta;
                                         fg = clrBlack;
                                         break;
                case MessageTyp_Error:   bg = clrRed;
                                         fg = clrWhite;
                                         break;
                default:                 bg = clrCyan;
                                         fg = clrBlack;
              }
            else            
              switch (message->MessageTyp())
              {
                case MessageTyp_Status:
                case MessageTyp_Info:    bg = clrInfoLine;
                                         fg = (eDvbColor) fginfofont;
                                         break;
                case MessageTyp_Warning: bg = clrConfirmLine;
                                         fg = (eDvbColor) fgconfirmfont;
                                         break;
                case MessageTyp_Error:   bg = clrErrorLine;
                                         fg = (eDvbColor) fgerrorfont;
                                         break;
                default:                 bg = clrCyan;
                                         fg = clrBlack;
              }
#else
            switch (message->MessageTyp())
            {
              case MessageTyp_Status:  bg = clrCyan;
                                       fg = clrBlack;
                                       break;
              case MessageTyp_Info:    bg = clrGreen;
                                       fg = clrBlack;
                                       break;
              case MessageTyp_Warning: bg = clrMagenta;
                                       fg = clrBlack;
                                       break;
              case MessageTyp_Error:   bg = clrRed;
                                       fg = clrWhite;
                                       break;
              default:                 bg = clrCyan;
                                       fg = clrBlack;
            }
#endif
            char *l = z;
            for (int y = 0; y < lines; y++)
            {
              p = l;
              l = strchr(p, '\n');
              if (l)
              {
                *l = 0;
                l++;
              }
              Interface->ClearEol(0, y, bg);
              int x = (Interface->Width() - int(strlen(p))) / 2;
              Interface->Write(x < 0 ? 0 : x, y, p, fg, bg);
              cStatus::MsgOsdStatusMessage(p);
            }
            free(z);
            response = Wait(timeout == 0 ? Setup.OSDMessageTime : timeout);
          }
          Interface->Close(); // included Clear()
#endif
          controlstate = ControlState_Normal;
          message->MessageData()->SetKey(response);
          while (!Responses.SetLock(true, true));
          Responses.Add(message);
          Responses.SetLock(false);
          dispid = 0;
        }
        usleep(100000);
      } else // message
        sleep(2);
    } else // Skins.IsOpen - Interface->IsOpen
      sleep(2);
  }
#ifdef SAQ_Debug
  dsyslog("%s: Display-Thread ended (pid=%d)", plugin_name, getpid());
#endif
}

void cDisplayThread::Stop(void)
{
  active = false;
}

eKeys cDisplayThread::Wait(int Seconds)
{
  eKeys key;
//#if VDRVERSNUM > 10300
//  key = Interface->Wait(Seconds);
//#else
  // Interface->Wait is private in vdr 1.2.6
  time_t to = time(NULL) + Seconds;
  for (;;)
  {
    while (controlstate != ControlState_Wait1 && controlstate != ControlState_Wait2)
    {
      usleep(100000);
    }
    controlstate = ControlState_Wait2;
    cOsdObject *Interact = cControl::Control();
    key = Interface->GetKey(!Interact || !Interact->NeedsFastResponse());
    if ((key != kNone && NORMALKEY(key) == key) || time(NULL) > to)
      break;
  }
//#endif
  return key;
}

void cDisplayThread::Housekeeping(void)
{
#ifdef SAQ_Debug
  dsyslog("%s: cDisplayThread::Housekeeping", plugin_name);
#endif
  lasthousekeeping = time(NULL);
  cMessage *message;
  if (Responses.SetLock(true))
  {
    if (Responses.Count())
    {
      message = Responses.First();
      while (message)
      {
        cMessage *nextmes = Responses.Next(message);
        if ((time(NULL) - message->ResponseTime()) > response_in_queue.u * 60)  // delete response after xx minutes
          Responses.Del(message);
        message = nextmes;
      }
    }
    Responses.SetLock(false);
  }
  if (History.SetLock(true))
  {
    if (History.Count())
    {
      message = History.First();
      while (message)
      {
        cMessage *nextmes = History.Next(message);
        time_t t = message->MessageTyp() <= MessageTyp_Text ? message->ResponseTime() : message->ReceiveTime();
        if (t && (time(NULL) - t) > message_in_queue.u * 60)  // delete message after xx minutes
          History.Del(message);
        message = nextmes;
      }
    }
    History.SetLock(false);
  }
}
