/*
 * autotimeredit: A plugin for the Video Disk Recorder
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id: menuautotimer.c 1.7 2005/10/19 23:22:17 hflor Exp $
 */

#ifdef HAVE_ICONPATCH
  #include <vdr/iconpatch.h>
#else
  #define ICON_CLOCK '*'
#endif

#include "autotimeredit.h"
#include "autotimers.h"
#include "ddictionary.h"
#include "menuautotimer.h"
#if VDRVERSNUM < 10305
  #include "menueditbit.h"
#endif
#if VDRVERSNUM < 10307
  #include "menueditchan.h"
  #include "menuedittime.h"
#endif
#include "menueditweekdays.h"
#include "i18n.h"
#include "vdrtools.h"
// --- cMenuEditAutoTimer --------------------------------------------------------

cMenuEditAutoTimer::cMenuEditAutoTimer(cAutoTimer *AutoTimer, bool New)
:cOsdMenu(tr("Edit$Edit Autotimer"), 30)
{
#ifdef ATE_Debug2
  dsyslog("%s: cMenuEditAutoTimer::cMenuEditAutoTimer Autotimer=%s New=%s", plugin_name, AutoTimer ? AutoTimer->ToText() : "(null)", New ? "true" : "false");
#endif
  autotimer = AutoTimer;
  isdictionarselect = false;
  dictionarypos = -1;
  dictionarysel = 0;
  addIfConfirmed = New;
  if (autotimer) {
    data = *autotimer;
    selStartStopTime = (data.useStart ? 0x1 : 0) | (data.useStop ? 0x2 : 0);
    channel = data.channel->Number();
    int tmp_weekdays = data.weekdays;
    selWeekdays = 0x80000000;
    for (int pos = 6; pos >= 0; pos--) {
      selWeekdays |= (tmp_weekdays % 10) ? 1<<pos : 0;
      tmp_weekdays /= 10;
    }
#ifdef ATE_Debug1
    dsyslog("%s: cMenuEditAutoTimer::cMenuEditAutoTimer data.weekdays=%d bin=%02x", plugin_name, data.weekdays, selWeekdays );
#endif
    Set();
  }
}

cMenuEditAutoTimer::~cMenuEditAutoTimer()
{
#ifdef ATE_Debug2
  dsyslog("%s: cMenuEditAutoTimer::~cMenuEditAutoTimer Autotimer=%s addIfConfirmed=%s", plugin_name, autotimer ? autotimer->ToText() : "(null)", addIfConfirmed ? "true" : "false");
#endif
  if (autotimer && addIfConfirmed)
    delete autotimer; // apparently it wasn't confirmed
}

void cMenuEditAutoTimer::Set(void)
{
#ifdef ATE_Debug2
  dsyslog("%s: cMenuEditAutoTimer::Set Current=%d", plugin_name, Current());
#endif
  int current = Current();
  dictionarypos = -1;
  Clear();
  char trans_channel[MAXOSDTEXTWIDTH + 1] = "  ";
  strn0cpy(&trans_channel[2], tr("Channel"), MAXOSDTEXTWIDTH-1);
  if (use_weekdays.u)
    Add(new cMenuEditStraItem(tr("Active"),                        &data.active, 3, OffOnSingle));
  else
    Add(new cMenuEditBoolItem(tr("Active"),                        &data.active));
  Add(new cMenuEditStrItem(tr("Edit$Search Text"),                 data.search, sizeof(data.search), tr(FileNameChars)));
#if VDRVERSNUM >= 10338
  Add(new cMenuEditBitItem(tr("Edit$Search in Title"),             (uint *)&data.options, 0x01));
  Add(new cMenuEditBitItem(tr("Edit$Search in Subtitle"),          (uint *)&data.options, 0x02));
  Add(new cMenuEditBitItem(tr("Edit$Search in Description"),       (uint *)&data.options, 0x04));
#else
  Add(new cMenuEditBitItem(tr("Edit$Search in Title"),             &data.options, 0x01));
  Add(new cMenuEditBitItem(tr("Edit$Search in Subtitle"),          &data.options, 0x02));
  Add(new cMenuEditBitItem(tr("Edit$Search in Description"),       &data.options, 0x04));
#endif
  if (use_weekdays.u)
    Add(new cMenuEditWeekDaysItem(tr("Edit$Weekdays for search"),  &selWeekdays));
  Add(new cMenuEditStraItem(tr("Edit$Use start/stop Time"),        &selStartStopTime, 4, StartStopTime));
  if (selStartStopTime & 0x1)
    Add(new cMenuEditTimeItem(tr("Edit$  Search start Time"),      &data.startTime));
  if (selStartStopTime & 0x2)
    Add(new cMenuEditTimeItem(tr("Edit$  Search stop Time"),       &data.stopTime));
  Add(new cMenuEditBoolItem(tr("Edit$Use Channel select"),         &data.useChannel));
  if (data.useChannel)
    Add(new cMenuEditChanItem(trans_channel,                       &channel));
  dictionarypos = Count();
  if (isdictionarselect)
    Add(new cMenuEditStraItem(tr("Edit$Dictionary for Record <>"), &dictionarysel, CountDefaultDictionarys, SelDefaultDictionary));
  else
    Add(new cMenuEditStrItem(tr("Edit$Dictionary for Record"),     data.folder, sizeof(data.folder), tr(FileNameChars)));
  Add(new cMenuEditBoolItem(tr("Edit$Episode record"),             &data.serie));
  if (use_except_repeat.u)
    Add(new cMenuEditBoolItem(tr("Edit$Except repeat"),            &data.except_repeat));
  Add(new cMenuEditIntItem(tr("Priority"),                         &data.priority, 0, MAXPRIORITY));
  Add(new cMenuEditIntItem(tr("Lifetime"),                         &data.lifetime, 0, MAXLIFETIME));
  SetCurrent(Get(current));
}

eOSState cMenuEditAutoTimer::ProcessKey(eKeys Key)
{
#ifdef ATE_Debug3
  bool noneKey = Key == kNone;
  if (!noneKey)
    dsyslog("%s: cMenuEditAutoTimer::ProcessKey Key=%s", plugin_name, KeyName(Key));
#endif

  static int oldhelpkeys;
  int helpkeys;
#if VDRVERSNUM < 10307
  static char oldstatus[MAXOSDTEXTWIDTH];
  char status[MAXOSDTEXTWIDTH];
  int pathlen;
#endif
  int m_selStartStopTime = selStartStopTime;
  int m_useChannel = data.useChannel;

  eOSState state = cOsdMenu::ProcessKey(Key);

#ifdef ATE_Debug3
  if (!noneKey || (state != osUnknown && state != osContinue)) {
    dsyslog("%s: cMenuEditAutoTimer::ProcessKey OSState=%s", plugin_name, OSStateName(state));
    noneKey = false;
  }
#endif

  if (m_selStartStopTime != selStartStopTime || m_useChannel != data.useChannel) {
    Set();
    Display();
  }
  if (state == osUnknown) {
    switch (Key) {
      case kOk:     if (Current() != dictionarypos || !isdictionarselect) {
                      if (!strlen(data.search)) {
                        ERROR(tr("Error$Missing search text!"));
                        break;
                      }
                      if (data.useChannel) {
                        cChannel *ch = Channels.GetByNumber(channel);
                        if (ch)
                          data.channel = ch;
                        else {
                          ERROR(tr("*** Invalid Channel ***"));
                          break;
                        }
                      }
                      if ((int(strlen(data.search)) < searchlength.u) && !Interface->Confirm(tr("Edit$Search text to small - use anyway?")))
                        break;
                      if (isdictionarselect)
                        strn0cpy(data.folder, SelDefaultDictionary[dictionarysel], sizeof(data.folder));
                      data.useStart = (selStartStopTime & 0x1) ? true : false;
                      data.useStop = (selStartStopTime & 0x2) ? true : false;
                      int tmp_weekdays = selWeekdays;
                      data.weekdays = 0;
                      for (int pos = 0; pos < 7; pos++) {
                        data.weekdays *= 10;
                        if (tmp_weekdays & 0x1)
                          data.weekdays++;
                        tmp_weekdays >>= 1;
                      }
                      if (data.weekdays == 0)
                        data.weekdays = 1111111;
#ifdef ATE_Debug1
                      dsyslog("%s: cMenuEditAutoTimer::ProcessKey bin=%02x data.weekdays=%07d", plugin_name, selWeekdays, data.weekdays);
#endif
                      if (autotimer) {
                        if (*autotimer != data) {
                          *autotimer = data;
                          q_manual_update = true;
                          if (addIfConfirmed)
                            AutoTimers.Add(autotimer);
                          isyslog("%s: autotimer %d %s (%s)", plugin_name, autotimer->Index() + 1, addIfConfirmed ? "added" : "modified", autotimer->active ? "active" : "inactive");
                          AutoTimers.Save();
                          addIfConfirmed = false;
                        }
                      }
                      return osBack;
                    } // no break!
      case kBlue:   if (Current() == dictionarypos && CountDefaultDictionarys > 1) {
                      if (isdictionarselect)
                        strn0cpy(data.folder, SelDefaultDictionary[dictionarysel], sizeof(data.folder));
                      else
                        dictionarysel = DefaultDictionarys.SearchDictionary(data.folder);
                      isdictionarselect = !isdictionarselect;
                      Set();
                      Display();
                    } // no break!
      case kRed:
      case kGreen:
      case kYellow: state = osContinue;
      default: break;
    }
  }
#if VDRVERSNUM < 10307
  if (Current() == dictionarypos) {
    if (isdictionarselect)
      strn0cpy(data.folder, SelDefaultDictionary[dictionarysel], sizeof(data.folder));
    pathlen = strlen(data.folder);
    if (pathlen > AKTOSDTEXTWIDTH - 10) {
      sprintf(status, "...%s", &data.folder[pathlen - AKTOSDTEXTWIDTH + 10]);
    } else
      strcpy(status, data.folder);
    if (oldstatus != status) {
      SetStatus(status);
      strcpy(oldstatus, status);
    }
  } else
    if (strlen(oldstatus)) {
      SetStatus(NULL);
      oldstatus[0] = 0;
    }
#endif

  if (Current() == dictionarypos && CountDefaultDictionarys > 1)
    helpkeys = isdictionarselect ? 1 : 2;
  else
    helpkeys = 0;
  if (oldhelpkeys != helpkeys || Key == kOk) {
    oldhelpkeys = helpkeys;
    switch (helpkeys) {
      case 0: SetHelp(NULL, NULL, NULL, NULL);
              break;
      case 1: SetHelp(NULL, NULL, NULL, tr("Edit$Input"));
              break;
      case 2: SetHelp(NULL, NULL, NULL, tr("Edit$Select"));
              break;
    }
  }

#ifdef ATE_Debug3
  if (!noneKey || (state != osUnknown && state != osContinue))
    dsyslog("%s: cMenuEditAutoTimer::ProcessKey returned OSState=%s", plugin_name, OSStateName(state));
#endif
  return state;
}

// --- cMenuAutoTimerItem --------------------------------------------------------

cMenuAutoTimerItem::cMenuAutoTimerItem(cAutoTimer *AutoTimer)
{
#ifdef ATE_Debug2
  dsyslog("%s: cMenuAutoTimerItem::cMenuAutoTimerItem Autotimer=%s", plugin_name, AutoTimer ? AutoTimer->ToText() : "(null)");
#endif
  autotimer = AutoTimer;
  Set();
}

#if VDRVERSNUM >= 10315
int cMenuAutoTimerItem::Compare(const cListObject &ListObject) const
{
  const cMenuAutoTimerItem *ATI = (cMenuAutoTimerItem *)&ListObject;
  return autotimer->Compare(*(ATI->autotimer));
}
#else
bool cMenuAutoTimerItem::operator< (const cListObject &ListObject)
{
  return *autotimer < *((cMenuAutoTimerItem *)&ListObject)->autotimer;
}
#endif

void cMenuAutoTimerItem::Set(void)
{
  char *buffer = NULL;
  char *tmp[]  = { "", "", "", "" };
  int pos = 0;
  if (show_flags.u)
    asprintf(&tmp[pos++], "\t%s%s%s%s%s",
                          autotimer->Options() & Searchpos_Title ? tr("Display1$T") : "-",
                          autotimer->Options() & Searchpos_Subtitle ? tr("Display2$S") : "-",
                          autotimer->Options() & Searchpos_Description ? tr("Display3$D") : "-",
                          use_except_repeat.u ? autotimer->Except_Repeat() ? tr("Display4$R") : "-" : "",
                          use_except_repeat.u && use_weekdays.u ? autotimer->Weekdays() != 1111111 ? tr("Display5$W") : "-" : "");
  if (show_channel.u)
    if (autotimer->UseChannel()) {
      if (show_channelname.u)
        asprintf(&tmp[pos++], "\t%.7s", autotimer->Channel()->Name());
      else
        asprintf(&tmp[pos++], "\t%0*d", numdigits(Channels.MaxNumber()), autotimer->Channel()->Number());
    } else
      asprintf(&tmp[pos++], "\t--");
  if (show_startstop.u & 0x1) {
    if (autotimer->UseStart())
      asprintf(&tmp[pos++], "\t%02d:%02d", autotimer->StartTime() / 100, autotimer->StartTime() % 100);
    else
      asprintf(&tmp[pos++], "\t--:--");
  }
  if (show_startstop.u & 0x2) {
    if (autotimer->UseStop())
      asprintf(&tmp[pos++], "\t%02d:%02d", autotimer->StopTime() / 100, autotimer->StopTime() % 100);
    else
      asprintf(&tmp[pos++], "\t--:--");
  }

  asprintf(&buffer, "%c%s%s%s%s\t%s", autotimer->Active() ? ICON_CLOCK[0] : ' ', tmp[0], tmp[1], tmp[2], tmp[3], autotimer->Search());
  SetText(buffer, false);
#ifdef ATE_Debug2
  dsyslog("%s: cMenuAutoTimerItem::Set SetText=%s", plugin_name, buffer);
#endif
}

// --- cMenuAutoTimers -----------------------------------------------------------

cMenuAutoTimers::cMenuAutoTimers(void)
:cOsdMenu(tr("Display$AutoTimers"), GetColumn(0), GetColumn(1), GetColumn(2), GetColumn(3), GetColumn(4))
{
#ifdef ATE_Debug2
  dsyslog("%s: cMenuAutoTimers::cMenuAutoTimers", plugin_name);
#endif

  q_manual_update = false;
  ExpandEnvironment(&autotimerfile);
  AutoTimers.Load(autotimerfile.e);
  DefaultDictionarys.AddMergeFill();
  cAutoTimer *autotimer = AutoTimers.First();
  while (autotimer) {
    Add(new cMenuAutoTimerItem(autotimer));
    autotimer = AutoTimers.Next(autotimer);
  }
  Sort();
  AddUpdate();
  SetHelpKeys();
}

bool cMenuAutoTimers::IsAutoTimerItem(void)
{
#ifdef ATE_Debug2
  dsyslog("%s: cMenuAutoTimers::IsAutoTimerItem Count=%d Current=%d updateTop=%s updateBottom=%s", plugin_name, Count(), Current(), updateTop ? "true" : "false", updateBottom ? "true" : "false");
#endif
  bool IsNotEmpty = Count();
  if (IsNotEmpty) {
    return (Current() > 0 || !updateTop) && (Current() < Count() - 1 || !updateBottom);
  } else
    return false;
}

void cMenuAutoTimers::SetHelpKeys(void)
{
#ifdef ATE_Debug2
  dsyslog("%s: cMenuAutoTimers::SetHelpKeys", plugin_name);
#endif
  if (IsAutoTimerItem())
    SetHelp(tr("Edit"), tr("New"), tr("Delete"), tr("On/Off"));
  else
    SetHelp(NULL, tr("New"), NULL, NULL);
}

eOSState cMenuAutoTimers::Edit(void)
{
#ifdef ATE_Debug2
  dsyslog("%s: cMenuAutoTimers::Edit", plugin_name);
#endif
  if (HasSubMenu() || !CurrentAutoTimer())
    return osContinue;
  isyslog("%s: editing autotimer %d", plugin_name, CurrentAutoTimer()->Index() + 1);
  return AddSubMenu(new cMenuEditAutoTimer(CurrentAutoTimer()));
}

eOSState cMenuAutoTimers::New(void)
{
#ifdef ATE_Debug2
  dsyslog("%s: cMenuAutoTimers::New", plugin_name);
#endif
  if (HasSubMenu())
    return osContinue;
  return AddSubMenu(new cMenuEditAutoTimer(new cAutoTimer, true));
}

eOSState cMenuAutoTimers::Delete(void)
{
#ifdef ATE_Debug2
  dsyslog("%s: cMenuAutoTimers::Delete", plugin_name);
#endif
  cAutoTimer *cat = CurrentAutoTimer();
  if (cat) {
    if (Interface->Confirm(tr("Edit$Delete Autotimer?"))) {
      isyslog("%s: autotimer %d deleted", plugin_name, cat->Index() + 1);
      AutoTimers.Del(cat);
      AutoTimers.Save();
      cOsdMenu::Del(Current());
      if (updateTop && updateBottom && (Count() == num_entrys.u + 1)) {
        Del(Count() - 1);
        updateBottom = false;
      }
      if (!AutoTimers.Count())
        DelUpdate();
      Display();
    }
  }
  return osContinue;
}

eOSState cMenuAutoTimers::OnOff(void)
{
#ifdef ATE_Debug2
  dsyslog("%s: cMenuAutoTimers::OnOff", plugin_name);
#endif
  cAutoTimer *cat = CurrentAutoTimer();
  if (cat) {
    cat->OnOff();
    if (cat->Active())
      q_manual_update = true;
    RefreshCurrent();
    DisplayCurrent(true);
    isyslog("%s: autotimer %d %sactivated", plugin_name, cat->Index() + 1, cat->Active() ? "" : "de");
    AutoTimers.Save();
  }
  return osContinue;
}

cAutoTimer *cMenuAutoTimers::CurrentAutoTimer(void)
{
#ifdef ATE_Debug2
  dsyslog("%s: cMenuAutoTimers::CurrentAutoTimer", plugin_name);
#endif
  if (IsAutoTimerItem()) {
    cMenuAutoTimerItem *item = (cMenuAutoTimerItem *)Get(Current());
    return item ? item->AutoTimer() : NULL;
  }
  return NULL;
}

void cMenuAutoTimers::AddUpdate(void)
{
#ifdef ATE_Debug2
  dsyslog("%s: cMenuAutoTimers::AddUpdate Count=%d Current=%d", plugin_name, Count(), Current());
#endif
  updateTop = false;
  updateBottom = false;
  if (updatemethod.u != 0x0 && Count()) {
    if (update_b_e.u & 0x1)
      updateTop = true;
    if ((update_b_e.u & 0x2) && (!updateTop || Count() >= num_entrys.u)) {
      Add(new cOsdItem(tr("Edit$Force search update manual now ..."), osUser2));
      updateBottom = true;
    }
    if (updateTop)
      Ins(new cOsdItem(tr("Edit$Force search update manual now ..."), osUser2));
  }
}

void cMenuAutoTimers::DelUpdate(void)
{
#ifdef ATE_Debug2
  dsyslog("%s: cMenuAutoTimers::DelUpdate updateTop=%s updateBottom=%s", plugin_name, updateTop ? "true" : "false", updateBottom ? "true" : "false");
#endif
  if (updateTop)
    Del(0);
  if (updateBottom)
    Del(Count() - 1);
  updateTop = updateBottom = false;
}

eOSState cMenuAutoTimers::ProcessKey(eKeys Key)
{
#ifdef ATE_Debug3
  bool noneKey = Key == kNone;
  if (!noneKey)
    dsyslog("%s: cMenuAutoTimers::ProcessKey Key=%s", plugin_name, KeyName(Key));
#endif

  cOsdItem *Item;
  int n;
  int nAutoTimers = AutoTimers.Count();
  bool hSubMenu = HasSubMenu();
  bool o_manual_update = q_manual_update;
  q_manual_update = false;

  eOSState state = cOsdMenu::ProcessKey(Key);

#ifdef ATE_Debug3
  if (!noneKey || (state != osUnknown && state != osContinue)) {
    dsyslog("%s: cMenuAutoTimers::ProcessKey OSState=%s", plugin_name, OSStateName(state));
    noneKey = false;
  }
#endif

  if (!HasSubMenu() && hSubMenu && q_manual_update) {
    if (nAutoTimers == AutoTimers.Count()) {
      Item = Get(Current());
      DelUpdate();
    } else {
      DelUpdate();
      Add((Item = new cMenuAutoTimerItem(AutoTimers.Get(nAutoTimers))));
    }
    Sort();
    AddUpdate();
    SetCurrent(Item);
    Display();
  }
  q_manual_update |= o_manual_update;

  switch (state) {
    case osUnknown: switch (Key) {
                      case k0...k9: n = (Key == k0) ? 10 : (Key - k0);
                                    if (updateTop) n++;
                                    if (n > Count() - updateBottom ? 1 : 0)
                                      break;
                                    SetCurrent(Get(--n));
                                    // no break!
                      case kOk:
                      case kRed:    state = Edit(); break;
                      case kGreen:  state = New(); break;
                      case kYellow: state = Delete(); break;
                      case kBlue:   state = OnOff(); break;
                      default: break;
                    }
                    break;
    case osBack:    if (!(q_manual_update && (question.u == 0x2 || question.u == 0x1 && Interface->Confirm(tr("Edit$Start search update now?")))))
                      break;
    case osUser2:   AutoTimers.RunUpdate();
                    q_manual_update = false;
                    break;
    default:        break;
  }
  if (!HasSubMenu() && Key != kNone)
    SetHelpKeys();

#ifdef ATE_Debug3
  if (!noneKey || (state != osUnknown && state != osContinue))
    dsyslog("%s: cMenuAutoTimers::ProcessKey returned OSState=%s", plugin_name, OSStateName(state));
#endif
  return state;
}
