/*
 * clock.c: A plugin for the Video Disk Recorder
 *
 * Clock Plugin for VDR. See http://vdr.aistleitner.info for download.
 * Copyright (C) 2007  Mario Aistleitner <vdr@aistleitner.info>
 *           (C) 2019  Martin Dummer <martin.dummer@gmx.net>
 *
 * 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
 * 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */

#include <vdr/plugin.h>
#include <vdr/font.h>
#include <vdr/remote.h>
#include <vdr/i18n.h>
#include <vdr/status.h>
#include <math.h>
#include "clock.h"
#include "enhancedbitmap.h"

#include "images/clock.xpm"
#include "images/tux.xpm"
#include "images/tuxpart.xpm"

static const char *VERSION = "1.0.2";
static const char *DESCRIPTION = trNOOP("A Simple Clock");
static const char *MAINMENUENTRY = trNOOP("Clock");

// Global variables

int TimeWidth = Setup.OSDWidth;
int TimeHeight = Setup.OSDHeight;
int XOffset = 100;
int YOffset = 100;
int Size = 2;
int Alarm = false;
int AlarmTime = -1;
int ShowSec = true;
int ShowAlways = false;
int ShowAlwaysDelay = 20;
int ShowAlarm = true;
int FullScreen = false;
int Type = 0;
int SW = 1;
int SL = 99;
int MW = 5;
int ML = 90;
int HW = 10;
int HL = 50;
double SecWidth = ((double)SW) / 100;
double SecLength = ((double)SL) / 100;
double MinWidth = ((double)MW) / 100;
double MinLength = ((double)ML) / 100;
double HourWidth = ((double)HW) / 100;
double HourLength = ((double)HL) / 100;
int ColorBG = 0;
int ColorFG = 1;
int ColorHour = 1;
int ColorMin = 1;
int ColorSec = 3;
int TeaTime = 5;
const char *TimeColors[] = {
    trNOOP("Transparent"), trNOOP("White"),  trNOOP("Black"), trNOOP("Red"),
    trNOOP("Green"),       trNOOP("Yellow"), trNOOP("Blue"),
};
const char *ClockType[] = {
    trNOOP("Digital"),   trNOOP("Analog"),   trNOOP("Analog modern"),
    trNOOP("MorphOS"),   trNOOP("TuxClock"), trNOOP("Internet time"),
    trNOOP("Tea clock"),
};

// --- cStatusTest -------------------------------------------------------------

void cStatusTest::OsdClear(void) {
#ifdef DEBUG_CLOCK
  dsyslog("clock: cStatusTest::OsdClear");
#endif
  cPluginClock::OtherOsdVisible = false;
  cPluginClock::NoOsdStartTime = 0;
}

void cStatusTest::OsdTitle(const char *Title) {
#ifdef DEBUG_CLOCK
  dsyslog("clock: cStatusTest::OsdTitle '%s'", Title);
#endif
  cPluginClock::OtherOsdVisible = true;
  cPluginClock::NoOsdStartTime = 0;
}

void cStatusTest::OsdStatusMessage(const char *Message) {
#ifdef DEBUG_CLOCK
  dsyslog("clock: cStatusTest::OsdStatusMessage '%s'", Message);
#endif
  cPluginClock::OtherOsdVisible = true;
  cPluginClock::NoOsdStartTime = 0;
}

void cStatusTest::OsdHelpKeys(const char *Red, const char *Green,
                              const char *Yellow, const char *Blue) {
#ifdef DEBUG_CLOCK
  dsyslog("clock: cStatusTest::OsdHelpKeys %s - %s - %s - %s", Red, Green,
          Yellow, Blue);
#endif
  cPluginClock::OtherOsdVisible = true;
  cPluginClock::NoOsdStartTime = 0;
}

void cStatusTest::OsdItem(const char *Text, int Index) {
#ifdef DEBUG_CLOCK
  dsyslog("clock: cStatusTest::OsdItem  %s %d", Text, Index);
#endif
  cPluginClock::OtherOsdVisible = true;
  cPluginClock::NoOsdStartTime = 0;
}

void cStatusTest::OsdCurrentItem(const char *Text) {
#ifdef DEBUG_CLOCK
  dsyslog("clock: cStatusTest::OsdCurrentItem %s", Text);
#endif
  cPluginClock::OtherOsdVisible = true;
  cPluginClock::NoOsdStartTime = 0;
}

void cStatusTest::OsdTextItem(const char *Text, bool Scroll) {
#ifdef DEBUG_CLOCK
  dsyslog("clock: cStatusTest::OsdTextItem %s %d", Text, Scroll);
#endif
  cPluginClock::OtherOsdVisible = true;
  cPluginClock::NoOsdStartTime = 0;
}

void cStatusTest::OsdChannel(const char *Text) {
#ifdef DEBUG_CLOCK
  dsyslog("clock: cStatusTest::OsdChannel %s", Text);
#endif
  cPluginClock::OtherOsdVisible = true;
  cPluginClock::NoOsdStartTime = 0;
}

void cStatusTest::OsdProgramme(time_t PresentTime, const char *PresentTitle,
                               const char *PresentSubtitle,
                               time_t FollowingTime, const char *FollowingTitle,
                               const char *FollowingSubtitle) {
  cPluginClock::OtherOsdVisible = true;
  cPluginClock::NoOsdStartTime = 0;
#ifdef DEBUG_CLOCK
  char buffer[25];
  struct tm tm_r;
  dsyslog("clock: cStatusTest::OsdProgramme");
  strftime(buffer, sizeof(buffer), "%R", localtime_r(&PresentTime, &tm_r));
  dsyslog("%5s %s", buffer, PresentTitle);
  dsyslog("%5s %s", "", PresentSubtitle);
  strftime(buffer, sizeof(buffer), "%R", localtime_r(&FollowingTime, &tm_r));
  dsyslog("%5s %s", buffer, FollowingTitle);
  dsyslog("%5s %s", "", FollowingSubtitle);
#endif
}

// --- cOSDClock -------------------------------------------------------------

cBitmap cOSDClock::bmClock(clock_xpm);
cBitmap cOSDClock::bmTux(tux_xpm);
cBitmap cOSDClock::bmTuxpart(tuxpart_xpm);

cOSDClock::cOSDClock() : cOsdObject(true), cThread() {
  osd = NULL;
  LastKey = kNone;
  if (Type == 3)
    Size = 3;
  if (Type == 4) {
    r = 40;
    XOffset = Setup.OSDWidth - 144;
  } else
    r = Size * 16;
}

cOSDClock::~cOSDClock() {
  if (Running())
    Cancel(3);

  while (!shutdown) {
    if (AlarmTime == -1)
      Alarm = false;
    usleep(100000);
  }

  if (osd)
    delete osd;

  cRemote::Put(LastKey);
  cPluginClock::ClockIsVisible = false;
  cPluginClock::NoOsdStartTime = 0;
#ifdef DEBUG_CLOCK
  dsyslog("clock: cOSDClock::~cOSDClock() called, cOSDClock deleted");
#endif
}

void cOSDClock::Show(void) {
  Start();
  cPluginClock::ClockIsVisible = true;
#ifdef DEBUG_CLOCK
  dsyslog("clock: cOSDClock::Show called");
#endif
}

void cOSDClock::DrawArrow(double width, double len, tColor col, double ang,
                          cEnhancedBitmap &img) {
  double s = sin(ang);
  double c = cos(ang);
  img.FillTriangle((int)(r - r * width * c), (int)(r - r * width * s),
                   (int)(r + r * width * c), (int)(r + r * width * s),
                   (int)(r + r * len * s), (int)(r - r * len * c), col);
  img.FillTriangle((int)(r - r * width * c), (int)(r - r * width * s),
                   (int)(r + r * width * c), (int)(r + r * width * s),
                   (int)(r - r * width * s), (int)(r + r * width * c), col);
}

void cOSDClock::DrawPoint(double x, double y, int r, tColor col,
                          cEnhancedBitmap &Analog) {
  double ang = 2 * 3.1415 / 20;
  for (int i = 0; i < 20; i++) {
    double s = sin(ang * i);
    double c = cos(ang * i);
    Analog.DrawLine((int)x, (int)y, (int)(x + r * 0.02 * s),
                    (int)(y + r * 0.02 * c), col);
  }
}

void cOSDClock::DrawScale(int r, tColor col, cEnhancedBitmap &Analog) {
  double ang = 2 * 3.1415 * 5 / 60;
  for (int i = 0; i < 12; i++) {
    double s = sin(ang * i);
    double c = cos(ang * i);
    DrawPoint(r + r * s, r + r * c, r, col, Analog);
    if (i % 3 == 0) {
      for (int j = 0; j < 10; j++) {
        DrawPoint(r + r * s - 0.1 * r * s * j / 10,
                  r + r * c - 0.1 * r * c * j / 10, r, col, Analog);
      }
    }
  }
}

void cOSDClock::DrawModernSec(int r, int sec, tColor col,
                              cEnhancedBitmap &Analog) {

  double ang = 2 * 3.1415 / 60;
  for (int i = 45; i < sec + 46; i++) {
    double c = sin(ang * i);
    double s = cos(ang * i);
    DrawPoint(r + r * s, r + r * c, r, col, Analog);
    ;
  }
}

tColor cOSDClock::getColor(int color) {
  switch (color) {
  case 0:
    return clrTransparent;
  case 1:
    return clrWhite;
  case 2:
    return clrBlack;
  case 3:
    return clrRed;
  case 4:
    return clrGreen;
  case 5:
    return clrYellow;
  case 6:
    return clrBlue;
  default:
    return clrBlack;
  }
  return clrBlack;
}

void cOSDClock::Action(void) {
  int ITime, Min, Hour, Sec, ActualTime, TimeLeft = 0;
  int SaveSec = -1;
  time_t Result;
  char TimeString[250];
  double SecAng, SecAng2, MinAng, HourAng;
  struct tm *CurTime;
  shutdown = false;
  const cFont *font = cFont::GetFont(fontOsd);

  if (FullScreen)
    r = 12 * (TimeHeight - 1);
  int ClockSize = 2 * r + 1;
  int ClockHeight = ClockSize;
  ClockSize = int(ClockSize / 4) * 4;

  cEnhancedBitmap Analog(ClockSize + 1, ClockSize + 1, 6);
  cEnhancedBitmap AnalogHigh(ClockSize + 1, ClockSize + 1, 8);
  cEnhancedBitmap TuxMap(154, 184, 8);
  cEnhancedBitmap Tea(5, 5, 6);

  ColorBackGround = getColor(ColorBG);
  ColorForeGround = getColor(ColorFG);
  ColorH = getColor(ColorHour);
  ColorM = getColor(ColorHour);
  ColorS = getColor(ColorSec);

  Analog.SetColor(0, ColorBackGround);
  Analog.SetColor(1, ColorForeGround);
  Analog.SetColor(2, ColorH);
  Analog.SetColor(3, ColorM);
  Analog.SetColor(4, ColorS);

#ifdef DEBUG_CLOCK
  dsyslog("clock: enter function Action type %d", Type);
#endif

  if (Type == 0 || Type > 4) {
    ClockHeight = font->Height() + 4;
    ClockSize = font->Width("XX:XX:XX") + 4;
  }
  if (Type == 4) {
    ClockHeight = 178;
    ClockSize = 148;
    Analog.SetSize(80, 80);
  }

  if (osd)
    delete osd;
  osd = cOsdProvider::NewOsd(XOffset, YOffset);
  tArea Area = {0, 0, ClockSize + 3, ClockHeight + 5, 8};
  osd->SetAreas(&Area, 1);

  while (Running()) {
    Result = time(NULL);
    CurTime = localtime(&Result);
    Hour = CurTime->tm_hour;
    Min = CurTime->tm_min;
    Sec = CurTime->tm_sec;
    if (Sec != SaveSec) {
      if (Type == 1) {
        SecAng = 2 * 3.1415 * Sec / 60;
        SecAng2 = 2 * 3.1415 * (Sec - 1) / 60;
        MinAng = 2 * 3.1415 * ((double)Min + ((double)Sec) / 60) / 60;
        HourAng = 2 * 3.1415 *
                  ((double)Hour + ((double)Min + ((double)Sec) / 60) / 60) / 12;
        Analog.DrawLine(0, 0, 1, 1, ColorBackGround);
        Analog.DrawRectangle(0, 0, ClockSize - 1, ClockHeight - 1,
                             ColorBackGround);
        Analog.DrawEllipse(0, 0, ClockSize, ClockSize, clrGray50);
        DrawScale(r, ColorForeGround, Analog);
        osd->DrawBitmap(0, 0, Analog);

        DrawArrow(HourWidth, HourLength, ColorH, HourAng, Analog);
        DrawArrow(MinWidth, MinLength, ColorM, MinAng, Analog);
        if (ShowSec)
          DrawArrow(SecWidth, SecLength, ColorS, SecAng, Analog);
        if (FullScreen)
          osd->DrawBitmap(0, 0, Analog);
        else
          osd->DrawBitmap(0, 0, Analog);
        if (ShowSec) {
          DrawArrow(SecWidth, SecLength, ColorBackGround, SecAng, Analog);
          DrawArrow(SecWidth, SecLength, ColorBackGround, SecAng2, Analog);
        }
        DrawArrow(MinWidth, MinLength, ColorBackGround, MinAng, Analog);
        DrawArrow(HourWidth, HourLength, ColorBackGround, HourAng, Analog);
        osd->Flush();
        Analog.Clean();
      }
      if (Type == 2) {
        SecAng = 2 * 3.1415 * Sec / 60;
        SecAng2 = 2 * 3.1415 * (Sec - 1) / 60;
        MinAng = 2 * 3.1415 * ((double)Min + ((double)Sec) / 60) / 60;
        HourAng = 2 * 3.1415 *
                  ((double)Hour + ((double)Min + ((double)Sec) / 60) / 60) / 12;
        Analog.DrawRectangle(0, 0, ClockSize, ClockHeight, 0x00000000);
        Analog.DrawEllipse(0, 0, ClockSize, ClockHeight, clrGray50);
        DrawScale(r, ColorForeGround, Analog);
        DrawArrow(HourWidth * 0.7, HourLength, ColorH, HourAng, Analog);
        DrawArrow(MinWidth * 0.7, MinLength, ColorM, MinAng, Analog);
        if (ShowSec)
          DrawModernSec(r, Sec, clrYellow, Analog);
        if (FullScreen)
          osd->DrawBitmap(0, 0, Analog);
        else
          osd->DrawBitmap(0, 0, Analog);
        DrawArrow(MinWidth * 0.7, MinLength, ColorBackGround, MinAng, Analog);
        DrawArrow(HourWidth * 0.7, HourLength, ColorBackGround, HourAng,
                  Analog);
        if (ShowSec)
          DrawModernSec(r, Sec, ColorBackGround, Analog);
        osd->Flush();
        Analog.Clean();
      } else if (Type == 3) {
        SecAng = 2 * 3.1415 * Sec / 60;
        SecAng2 = 2 * 3.1415 * (Sec - 1) / 60;
        MinAng = 2 * 3.1415 * ((double)Min + ((double)Sec) / 60) / 60;
        HourAng = 2 * 3.1415 *
                  ((double)Hour + ((double)Min + ((double)Sec) / 60) / 60) / 12;
        AnalogHigh.DrawRectangle(0, 0, ClockSize - 1, ClockHeight - 1,
                                 0x00000000);
        bmClock.SetColor(0, 0x00000000);
        AnalogHigh.DrawBitmap(9, 10, bmClock);
        osd->DrawBitmap(0, 0, AnalogHigh);
        DrawArrow(HourWidth * 0.7, HourLength * 0.8, ColorH, HourAng,
                  AnalogHigh);
        DrawArrow(MinWidth * 0.8, MinLength * 0.7, ColorM, MinAng, AnalogHigh);
        if (ShowSec)
          DrawArrow(SecWidth, SecLength * 0.7, ColorS, SecAng, AnalogHigh);
        if (FullScreen)
          osd->DrawBitmap(0, 0, AnalogHigh);
        else
          osd->DrawBitmap(0, 0, AnalogHigh);
        if (ShowSec) {
          DrawArrow(SecWidth, SecLength * 0.7, ColorBackGround, SecAng,
                    AnalogHigh);
          DrawArrow(SecWidth, SecLength * 0.7, ColorBackGround, SecAng2,
                    AnalogHigh);
        }
        DrawArrow(MinWidth * 0.8, MinLength * 0.7, ColorBackGround, MinAng,
                  AnalogHigh);
        DrawArrow(HourWidth * 0.7, HourLength * 0.8, ColorBackGround, HourAng,
                  AnalogHigh);
        osd->Flush();
        AnalogHigh.Clean();
      } else if (Type == 4) {
        SecAng = 2 * 3.1415 * Sec / 60;
        SecAng2 = 2 * 3.1415 * (Sec - 1) / 60;
        MinAng = 2 * 3.1415 * ((double)Min + ((double)Sec) / 60) / 60;
        HourAng = 2 * 3.1415 *
                  ((double)Hour + ((double)Min + ((double)Sec) / 60) / 60) / 12;
        bmTux.SetColor(12, 0x00000000);
        TuxMap.DrawBitmap(0, 0, bmTux);
        Analog.DrawBitmap(1, 0, bmTuxpart);
        DrawScale(r, clrRed, Analog);
        // osd->DrawBitmap(0, 0, TuxMap);
        DrawArrow(HourWidth, HourLength, clrBlack, HourAng, Analog);
        DrawArrow(MinWidth, MinLength, clrBlack, MinAng, Analog);
        DrawArrow(SecWidth, SecLength, clrRed, SecAng, Analog);
        TuxMap.DrawBitmap(38, 83, Analog);
        osd->DrawBitmap(0, 0, TuxMap);
        osd->Flush();
        TuxMap.Clean();
        Analog.Clean();
      } else if (Type == 0) {
#ifdef DEBUG_CLOCK
        dsyslog("clock: inside Action-Running type %d", Type);
#endif
        if (ShowSec)
          sprintf(TimeString, " %02d:%02d:%02d ", Hour, Min, Sec);
        else
          sprintf(TimeString, " %02d:%02d ", Hour, Min);
        osd->DrawRectangle(0, 0, ClockSize - 1, ClockHeight - 1,
                           ColorBackGround);
        osd->DrawText(0, 0, TimeString, ColorForeGround, ColorBackGround, font,
                      ClockSize - 1, font->Height(), taCenter);
        osd->Flush();
      } else if (Type == 5) {
        ITime = (int)((Hour * 3600 + Min * 60 + Sec) / 86.4);
        sprintf(TimeString, " @%04d ", ITime);
        osd->DrawRectangle(0, 0, ClockSize - 1, font->Height(),
                           ColorBackGround);
        osd->DrawText(0, 0, TimeString, ColorForeGround, ColorBackGround, font,
                      ClockSize - 1, font->Height(), taCenter);
        osd->Flush();
      } else if (Type == 6) {
        if (Alarm) {
          AlarmTime = -1;
          osd->DrawRectangle(0, 0, ClockSize - 1, font->Height(),
                             ColorBackGround);
          osd->DrawText(0, 0, tr("It's tea time"), ColorForeGround,
                        ColorBackGround, font, ClockSize - 1, font->Height(),
                        taCenter);
          osd->Flush();
          Tea.Clean();
        } else {
          ActualTime = Hour * 3600 + Min * 60 + Sec;
          if (AlarmTime == -1)
            AlarmTime = (ActualTime + TeaTime * 60);
          if (TimeLeft > 4000)
            AlarmTime = AlarmTime - 86400;
          TimeLeft = AlarmTime - ActualTime;
          if (TimeLeft <= 0) {
            Alarm = true;
            Min = 0;
            Sec = 0;
          } else {
            Min = TimeLeft / 60;
            Sec = TimeLeft % 60;
          }
          sprintf(TimeString, " %02d:%02d ", Min, Sec);
          if (ShowAlarm) {
            osd->DrawRectangle(0, 0, ClockSize - 1, font->Height(),
                               ColorBackGround);
            osd->DrawText(0, 0, TimeString, ColorForeGround, ColorBackGround,
                          font, ClockSize - 1, font->Height(), taCenter);
            osd->Flush();
          }
        }
      }
    }
    SaveSec = Sec;
    usleep(250000);
  } // while Running()
  shutdown = true;
}

eOSState cOSDClock::ProcessKey(eKeys Key) {
#ifdef DEBUG_CLOCK
  dsyslog("clock: inside ProcessKey %d", Key);
#endif
  if (Key != kNone) {
    if (AlarmTime == -1)
      Alarm = false;
    if (Key != kBack)
      LastKey = Key;
    if (Key != kChanUp && Key != kChanDn && Key != kVolUp && Key != kVolDn &&
        Key != kMute)
      return (osEnd);
  }
  return (osContinue);
}

// --- cMenuSetupClock -------------------------------------------------------

class cMenuSetupClock : public cMenuSetupPage {
private:
  char Color[16][32];
  int newSize;
  int newShowSec;
  int newShowAlways;
  int newShowAlwaysDelay;
  int newShowAlarm;
  int newFullScreen;
  int newType;
  int newHW;
  int newHL;
  int newMW;
  int newML;
  int newSW;
  int newSL;
  int newColorBG;
  int newColorFG;
  int newColorHour;
  int newColorMin;
  int newColorSec;
  int newXOffset;
  int newYOffset;
  int newTeaTime;
  int x;
  int y;

protected:
  virtual void Store(void);

public:
  cMenuSetupClock(void);
};

cMenuSetupClock::cMenuSetupClock(void) {
  cSetup data;
  newSize = Size;
  newShowSec = ShowSec;
  newShowAlways = ShowAlways;
  newShowAlwaysDelay = ShowAlwaysDelay;
  newShowAlarm = ShowAlarm;
  newFullScreen = FullScreen;
  newType = Type;
  newHW = HW;
  newHL = HL;
  newMW = MW;
  newML = ML;
  newSW = SW;
  newSL = SL;
  newColorBG = ColorBG;
  newColorFG = ColorFG;
  newColorHour = ColorHour;
  newColorMin = ColorMin;
  newColorSec = ColorSec;
  newXOffset = XOffset;
  newYOffset = YOffset;
  newTeaTime = TeaTime;

  if (!Type) {
    y = 2;
    if (ShowSec)
      x = 8;
    else
      x = 6;
  } else {
    x = 3 * Size;
    y = 0;
  }

  int current = Current();
  Add(new cMenuEditIntItem(tr("x Position"), &newXOffset, 0,
                           Setup.OSDWidth - x));
  Add(new cMenuEditIntItem(tr("y Position"), &newYOffset, 0,
                           Setup.OSDHeight - y));
  Add(new cMenuEditStraItem(tr("Type of the clock"), &newType, 7, ClockType));
  if (newType != 3) {
    Add(new cMenuEditStraItem(tr("Background color"), &newColorBG, 7,
                              TimeColors));
    Add(new cMenuEditStraItem(tr("Foreground color"), &newColorFG, 7,
                              TimeColors));
  }
  if (newType == 0) {
    Add(new cMenuEditBoolItem(tr("Show Seconds"), &newShowSec));
    Add(new cMenuEditBoolItem(tr("Always show clock"), &newShowAlways));
    Add(new cMenuEditIntItem(tr("- Delay before clock is shown"),
                             &newShowAlwaysDelay));
  } else if (newType == 1 || newType == 2) {
    Add(new cMenuEditBoolItem(tr("Show Seconds"), &newShowSec));
    Add(new cMenuEditBoolItem(tr("FullScreen"), &newFullScreen));
    Add(new cMenuEditIntItem(tr("Analog clock size"), &newSize, 0, 15));
    Add(new cMenuEditStraItem(tr("Arrow color"), &newColorHour, 7, TimeColors));
    Add(new cMenuEditIntItem(tr("Length of hour arrow [%]"), &newHL, 0, 100));
    Add(new cMenuEditIntItem(tr("Width of hour arrow [%]"), &newHW, 0, 100));
    Add(new cMenuEditIntItem(tr("Length of min arrow [%]"), &newML, 0, 100));
    Add(new cMenuEditIntItem(tr("Width of min arrow [%]"), &newMW, 0, 100));
    if (newType == 1) {
      Add(new cMenuEditStraItem(tr("Seconds arrow color"), &newColorSec, 7,
                                TimeColors));
      Add(new cMenuEditIntItem(tr("Length of sec arrow [%]"), &newSL, 0, 100));
      Add(new cMenuEditIntItem(tr("Width of sec arrow [%]"), &newSW, 0, 100));
    }
  }
  if (newType == 4) {
    Add(new cMenuEditIntItem(tr("Time till tea"), &newTeaTime, 0, 60));
    Add(new cMenuEditBoolItem(tr("Show Countdown"), &newShowAlarm));
  }
  SetCurrent(Get(current));
  Display();
}

void cMenuSetupClock::Store(void) {
  SetupStore("XOffset", XOffset = newXOffset);
  SetupStore("YOffset", YOffset = newYOffset);
  SetupStore("ShowSec", ShowSec = newShowSec);
  SetupStore("ShowAlways", ShowAlways = newShowAlways);
  SetupStore("ShowAlwaysDelay", ShowAlwaysDelay = newShowAlwaysDelay);
  SetupStore("ShowAlarm", ShowAlarm = newShowAlarm);
  SetupStore("FullScreen", FullScreen = newFullScreen);
  SetupStore("Type", Type = newType);
  SetupStore("ColorBG", ColorBG = newColorBG);
  SetupStore("ColorFG", ColorFG = newColorFG);
  SetupStore("ColorHour", ColorHour = newColorHour);
  SetupStore("ColorSec", ColorSec = newColorSec);
  SetupStore("ColorMin", ColorMin = newColorMin);
  SetupStore("Size", Size = newSize);
  SetupStore("HW", HW = newHW);
  SetupStore("HL", HL = newHL);
  SetupStore("MW", MW = newMW);
  SetupStore("ML", ML = newML);
  SetupStore("SW", SW = newSW);
  SetupStore("SL", SL = newSL);
  SetupStore("TeaTime", TeaTime = newTeaTime);
}
// --- End cMenuSetupClock

// --- cPluginClock ---------------------------------------------------

bool cPluginClock::ClockIsVisible;
bool cPluginClock::OtherOsdVisible;
time_t cPluginClock::NoOsdStartTime;

cPluginClock::cPluginClock(void) {
  // Initialize any member variables here.
  // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
  // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
  statusTest = NULL;
  OtherOsdVisible = false;
  ClockIsVisible = false;
  NoOsdStartTime = 0;
}

cPluginClock::~cPluginClock() {
  // Clean up after yourself!
  delete statusTest;
}

const char *cPluginClock::Version(void) { return VERSION; }

const char *cPluginClock::Description(void) { return tr(DESCRIPTION); }

const char *cPluginClock::CommandLineHelp(void) {
  // Return a string that describes all known command line options.
  return NULL;
}

bool cPluginClock::ProcessArgs(int argc, char *argv[]) {
  // Implement command line argument processing here if applicable.
  return true;
}

bool cPluginClock::Initialize(void) {
  // Initialize any background activities the plugin shall perform.
  return true;
}

bool cPluginClock::Start(void) {
  // Start any background activities the plugin shall perform.
  statusTest = new cStatusTest;
  return true;
}

void cPluginClock::Housekeeping(void) {
  // Perform any cleanup or other regular tasks.
}

void cPluginClock::MainThreadHook(void) {
#ifdef DEBUG_CLOCK
  dsyslog("clock: MainThreadHook  ShowAlways %d / ClockIsVisible %d / "
          "OtherOsdVisible %d / NoOsdStartTime %ld",
          ShowAlways, ClockIsVisible, OtherOsdVisible, NoOsdStartTime);
#endif
  if ((ClockIsVisible == false) && (OtherOsdVisible == false) &&
      (ShowAlways == true)) {
    // the Clock should be always visible
    // now the clock is NOT visible and no other OSD is visible
    if (NoOsdStartTime == 0) {
      // keep current time to determine delay until clock is displayed
#ifdef DEBUG_CLOCK
      dsyslog("clock: MainThreadHook set NoOsdStartTime");
#endif
      NoOsdStartTime = time(NULL);
    } else {
      // delay time has started, check if we waited long enough
      if ((time(NULL) - NoOsdStartTime) > ShowAlwaysDelay) {
#ifdef DEBUG_CLOCK
        dsyslog("clock: MainThreadHook NoOsdStartTime waited long enough");
#endif
        cRemote::CallPlugin("clock");
        //    cRemote  static bool CallPlugin(const char *Plugin);
        ///< Initiates calling the given plugin's main menu function.
        ///< The Plugin parameter is the name of the plugin, and must be
        ///< a static string. Returns true if the plugin call was successfully
        ///< initiated (the actual call to the plugin's main menu function
        ///< will take place some time later, during the next execution
        ///< of VDR's main loop). If there is already a plugin call pending
        ///< false will be returned and the caller should try again later.
        NoOsdStartTime = 0;
      }
    }
  }
  // Note: this is called very often
}

const char *cPluginClock::MainMenuEntry(void) { return tr(MAINMENUENTRY); }

cOsdObject *cPluginClock::MainMenuAction(void) {
  // Perform the action when selected from the main VDR menu.
  return new cOSDClock();
}

cMenuSetupPage *cPluginClock::SetupMenu(void) {
  // Return a setup menu in case the plugin supports one.
  return new cMenuSetupClock;
}

bool cPluginClock::SetupParse(const char *Name, const char *Value) {
  // Parse your own setup parameters and store their values.
  if (!strcasecmp(Name, "XOffset"))
    XOffset = atoi(Value);
  else if (!strcasecmp(Name, "YOffset"))
    YOffset = atoi(Value);
  else if (!strcasecmp(Name, "ShowSec"))
    ShowSec = atoi(Value);
  else if (!strcasecmp(Name, "ShowAlways"))
    ShowAlways = atoi(Value);
  else if (!strcasecmp(Name, "ShowAlwaysDelay"))
    ShowAlwaysDelay = atoi(Value);
  else if (!strcasecmp(Name, "ShowAlarm"))
    ShowAlarm = atoi(Value);
  else if (!strcasecmp(Name, "FullScreen"))
    FullScreen = atoi(Value);
  else if (!strcasecmp(Name, "Type"))
    Type = atoi(Value);
  else if (!strcasecmp(Name, "Size"))
    Size = atoi(Value);
  else if (!strcasecmp(Name, "ColorBG"))
    ColorBG = atoi(Value);
  else if (!strcasecmp(Name, "ColorFG"))
    ColorFG = atoi(Value);
  else if (!strcasecmp(Name, "ColorHour"))
    ColorHour = atoi(Value);
  else if (!strcasecmp(Name, "ColorMin"))
    ColorMin = atoi(Value);
  else if (!strcasecmp(Name, "ColorSec"))
    ColorSec = atoi(Value);
  else if (!strcasecmp(Name, "TeaTime"))
    TeaTime = atoi(Value);
  else if (!strcasecmp(Name, "HW"))
    HW = atoi(Value);
  else if (!strcasecmp(Name, "HL"))
    HL = atoi(Value);
  else if (!strcasecmp(Name, "MW"))
    MW = atoi(Value);
  else if (!strcasecmp(Name, "ML"))
    ML = atoi(Value);
  else if (!strcasecmp(Name, "SW"))
    SW = atoi(Value);
  else if (!strcasecmp(Name, "SL"))
    SL = atoi(Value);
  else
    return false;
  return true;
}

VDRPLUGINCREATOR(cPluginClock); // Don't touch this!
