/* 
 * menu_main.c: A plugin for the Video Disk Recorder
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id$
 */
#include "menu_main.h"
#include "menu_whatson.h"
#include "menu_myedittimer.h"
#include "epgsearchext.h"
#include "menu_event.h"
#include "menu_searchresults.h"
#include "menu_search.h" 
#include "menu_commands.h"
#include "epgsearchcfg.h"
#include "tools.h"
#include <vdr/menu.h>

int toggleKeys=0;
int exitToMainMenu = 0;
extern char menuScheduleTemplate[];
extern int menuScheduleTabs[];

// --- cMenuSearchMain ---------------------------------------------------------
cMenuSearchMain::cMenuSearchMain(void)
:cOsdMenu("", GetTab(1), GetTab(2), GetTab(3), GetTab(4), GetTab(5))
{
    otherChannel = 0;
    toggleKeys = 0;
    shiftTime = 0;
    InWhatsOnMenu = false;
    cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel());
    if (channel) {
	cMenuWhatsOnSearch::SetCurrentChannel(channel->Number());
	schedules = cSchedules::Schedules(schedulesLock);
	PrepareSchedule(channel);
	SetHelpKeys();
	cMenuWhatsOnSearch::currentShowMode = showNow;
    }
    if (EPGSearchConfig.StartMenu == 1)
    {
	InWhatsOnMenu = true;
	AddSubMenu(new cMenuWhatsOnSearch(schedules, cDevice::CurrentChannel()));
    }
}

cMenuSearchMain::~cMenuSearchMain()
{
    cMenuWhatsOnSearch::ScheduleChannel(); // makes sure any posted data is cleared
}

int cMenuSearchMain::GetTab(int Tab)
{
    if (strlen(menuScheduleTemplate) > 0)
	return menuScheduleTabs[Tab-1];
    else
	return 0;
}

void cMenuSearchMain::PrepareSchedule(cChannel *Channel)
{
    Clear();
    char *buffer = NULL;
    asprintf(&buffer, "%s - %s", tr("Schedule"), Channel->Name());
    SetTitle(buffer);
    free(buffer);

    if (schedules) {
	const cSchedule *Schedule = schedules->GetSchedule(Channel->GetChannelID());
	currentChannel = Channel->Number();
	if (Schedule && Schedule->Events()->First()) 
	{
#if VDRVERSNUM && VDRVERSNUM >= 10341
	    const cEvent *PresentEvent = Schedule->GetPresentEvent();
#else
	    const cEvent *PresentEvent = Schedule->GetPresentEvent(currentChannel == cDevice::CurrentChannel());
#endif
	    time_t now;
	    if (shiftTime == 0)
		now = time(NULL) - Setup.EPGLinger * 60;
	    else
		now = time(NULL) + shiftTime * 60;
	    time_t lastEventDate = Schedule->Events()->First()->StartTime();
	    for (const cEvent *Event = Schedule->Events()->First(); Event; Event = Schedule->Events()->Next(Event)) {
		if (Event->EndTime() > now || (shiftTime==0 && Event == PresentEvent))
		{
		    if (EPGSearchConfig.showDaySeparators)
		    {
			struct tm tm_rEvent;
			struct tm tm_rLastEvent;
			time_t EventDate = Event->StartTime();
			struct tm *t_event = localtime_r(&EventDate, &tm_rEvent);			
			struct tm *t_lastevent = localtime_r(&lastEventDate, &tm_rLastEvent);			
			if (t_event->tm_mday != t_lastevent->tm_mday)
			{
			    char* szSep = NULL;
			    asprintf(&szSep, "----------------------------------\t %s ----------------------------------------------------------------------------------------------", GETDATESTRING(Event));
			    cOsdItem* pSepItem = new cOsdItem(szSep);
			    free(szSep);
			    pSepItem->SetSelectable(false);
			    Add(pSepItem);
			}
			lastEventDate = EventDate;
		    }
		    Add(new cMenuMyScheduleItem(Event), Event == PresentEvent);
		}
            }
        }
    }
    if (shiftTime)
    {
	char *buffer = NULL;
	asprintf(&buffer, "%s (%s%dh %dm)", Channel->Name(), shiftTime>0?"+":"", shiftTime/60, shiftTime%60);
	SetTitle(buffer);
	free(buffer);
    }
}

bool cMenuSearchMain::Update(void)
{
  bool result = false;
  for (cOsdItem *item = First(); item; item = Next(item)) {
      if (item->Selectable() && ((cMenuMyScheduleItem *)item)->Update())
         result = true;
      }
  return result;
}

eOSState cMenuSearchMain::Record(void)
{
  cMenuMyScheduleItem *item = (cMenuMyScheduleItem *)Get(Current());
  if (item) {
      if (item->timerMatch == tmFull) 
      {
	  int tm = tmNone;
	  cTimer *timer = Timers.GetMatch(item->event, &tm);
	  if (timer)
#if VDRVERSNUM && VDRVERSNUM >= 10331
	      if (EPGSearchConfig.useVDRTimerEditMenu)
		  return AddSubMenu(new cMenuEditTimer(timer));
	      else
#endif
		  return AddSubMenu(new cMenuMyEditTimer(timer, false, item->event, item->channel));
      }

     cTimer *timer = new cTimer(item->event);
     PrepareTimerFile(item->event, timer);
     cTimer *t = Timers.GetTimer(timer);
     if (EPGSearchConfig.onePressTimerCreation == 0 || t || !item->event || (!t && item->event && item->event->StartTime() - (Setup.MarginStart+2) * 60 < time(NULL))) 
     {
	 if (t)
	 {
	     delete timer;
	     timer = t;      
	 }
#if VDRVERSNUM && VDRVERSNUM >= 10331
	 if (EPGSearchConfig.useVDRTimerEditMenu)
	     return AddSubMenu(new cMenuEditTimer(timer, !t));
	 else
#endif
	     return AddSubMenu(new cMenuMyEditTimer(timer, !t, item->event));     
     }
     else
     {
	 Timers.Add(timer);
	 timer->Matches();

#if VDRVERSNUM && VDRVERSNUM >= 10315
	 Timers.SetModified();
#endif

#if VDRVERSNUM && VDRVERSNUM >= 10323
	 isyslog("timer %s added (active)", *timer->ToDescr());
#else
	 isyslog("timer %d added (active)", timer->Index()+1);
#endif

	 if (HasSubMenu())
	     CloseSubMenu();
	 if (Update())
	     Display();
	 SetHelpKeys();
     }
  }
  return osContinue;
}

eOSState cMenuSearchMain::Switch(void)
{
  cMenuMyScheduleItem *item = (cMenuMyScheduleItem *)Get(Current());
  if (item) {
     cChannel *channel = Channels.GetByChannelID(item->event->ChannelID(), true);
     if (channel && cDevice::PrimaryDevice()->SwitchChannel(channel, true))
        return osEnd;
     }
  Skins.Message(mtInfo, tr("Can't switch channel!")); 
  return osContinue;
}

eOSState cMenuSearchMain::ExtendedSearch(void)
{
    return AddSubMenu(new cMenuEPGSearchExt());
}
 
eOSState cMenuSearchMain::Commands(eKeys Key)
{
  if (HasSubMenu() || Count() == 0)
     return osContinue;
  cMenuMyScheduleItem *mi = (cMenuMyScheduleItem *)Get(Current());
  if (mi && mi->event) {
        cMenuSearchCommands *menu;
        eOSState state = AddSubMenu(menu = new cMenuSearchCommands(tr("EPG Commands"), mi->event, true));
        if (Key != kNone)
           state = menu->ProcessKey(Key);
        return state;
     }
  return osContinue;
}

eOSState cMenuSearchMain::ShowSummary()
{
    if (Count())
    {
	const cEvent* prev = NULL;
	const cEvent* next = NULL;
	if (Current() > 0)
	{
	    int Offset = 1;
	    while(Current() - Offset > 0 && !Get(Current()-Offset)->Selectable()) Offset++;
	    if (Get(Current()-Offset)->Selectable())
		prev = ((cMenuMyScheduleItem *)Get(Current()-Offset))->event;
	}
	if (Current() < Count()-1)
	{
	    int Offset = 1;
	    while(Current() + Offset < Count()-1 && !Get(Current()+Offset)->Selectable()) Offset++;
	    if (Get(Current()+Offset)->Selectable())
		next = ((cMenuMyScheduleItem *)Get(Current()+Offset))->event;		     
	}
	return AddSubMenu(new cMenuEventSearch(((cMenuMyScheduleItem *)Get(Current()))->event, 
					       otherChannel, prev, next));
    }
    return osContinue;
}

void cMenuSearchMain::SetHelpKeys()
{
    cMenuMyScheduleItem *item = (cMenuMyScheduleItem *)Get(Current());
    bool hasTimer = item && item->Selectable() && item->timerMatch == tmFull;

    if (toggleKeys==0)
	SetHelp((EPGSearchConfig.redkeymode==0?(hasTimer?tr("Button$Timer"):tr("Button$Record")):tr("Button$Commands")), tr("Button$Now"), tr("Button$Next"), EPGSearchConfig.bluekeymode==0?tr("Button$Switch"):tr("Button$Search"));
    else
    {	
	const char* szGreenToggled = CHANNELNAME(Channels.GetByNumber(currentChannel-1,-1));
	const char* szYellowToggled = CHANNELNAME(Channels.GetByNumber(currentChannel+1,1));
	    
	SetHelp((EPGSearchConfig.redkeymode==1?(hasTimer?tr("Button$Timer"):tr("Button$Record")):tr("Button$Commands")), (EPGSearchConfig.toggleGreenYellow==0?tr("Button$Now"):szGreenToggled), (EPGSearchConfig.toggleGreenYellow==0?tr("Button$Next"):szYellowToggled), EPGSearchConfig.bluekeymode==1?tr("Button$Switch"):tr("Button$Search"));
    }
}

eOSState cMenuSearchMain::Shift(int iMinutes)
{
    shiftTime += iMinutes;
    cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel());
    PrepareSchedule(channel);
    Display();
    SetHelpKeys();
    return osContinue;
}

eOSState cMenuSearchMain::ProcessKey(eKeys Key)
{ 
    bool HadSubMenu = HasSubMenu();
    eOSState state = cOsdMenu::ProcessKey(Key);

    if (exitToMainMenu == 1)
    {
	exitToMainMenu = 0;
	return osBack;
    }

    if (state == osUnknown) {
	switch (Key) {
	 case kFastRew:
	     if (HasSubMenu() && !InWhatsOnMenu)
	     {
		 if (Count())
		 {
		     CursorUp();
		     return ShowSummary();
		 }
	     }
	     else
		 return Shift(-EPGSearchConfig.timeShiftValue);
	 case kFastFwd:
	     if (HasSubMenu() && !InWhatsOnMenu)
	     {
		 if (Count())
		 {
		     CursorDown();
		     return ShowSummary();
		 }
	     }
	     else
		 return Shift(EPGSearchConfig.timeShiftValue);
	 case kRecord:
	 case kRed:
	     if(HasSubMenu()) {
		 state = Record();
		 break;
	     } 
	     if (Count())
	     {
		 if (EPGSearchConfig.redkeymode==toggleKeys)
		     state = Record();
		 else
		 {
		     cMenuMyScheduleItem *mi = (cMenuMyScheduleItem *)Get(Current());
		     if (mi) {
			 if (mi->event) {
			     return AddSubMenu(new cMenuSearchCommands(tr("EPG Commands"),mi->event));
			 }
		     }
		 }
	     }
	     break;
	 case k0:
	     if(!HasSubMenu()) 
	     {
		 toggleKeys = 1 - toggleKeys;
		 SetHelpKeys();
	     }
	     state = osContinue;
	     break;
	 case k1...k9: 
	     if (!HasSubMenu())
		 return Commands(Key);
	     else
		 state = osContinue;
	     break;
	 case kGreen:  
	     if (schedules) 
	     {
		 if (HasSubMenu() && !InWhatsOnMenu)
		 {
		     if (Count())
		     {
			 CursorUp();
			 return ShowSummary();
		     }
		 }
		 else if (toggleKeys == 0 || (toggleKeys == 1 && EPGSearchConfig.toggleGreenYellow == 0))
		 {
		     int ChannelNr = cDevice::CurrentChannel();
		     if (Count()) {
			 cMenuMyScheduleItem* Item = (cMenuMyScheduleItem *)Get(Current());
			 if (Item && Item->event)
			 {
			     cChannel *channel = Channels.GetByChannelID(Item->event->ChannelID(), true);
			     if (channel)
				 ChannelNr = channel->Number();
			 }
		     }
		     InWhatsOnMenu = true;
		     return AddSubMenu(new cMenuWhatsOnSearch(schedules, ChannelNr));
		 }
		 else
		 {
		     cChannel *channel = Channels.GetByNumber(currentChannel-1,-1);
		     
		     if (channel) {
			 PrepareSchedule(channel);
			 if (channel->Number() != cDevice::CurrentChannel()) {
			     otherChannel = channel->Number();
			 }
			 Display();
		     }
		     SetHelpKeys();
		     return osContinue;
		 }
	     }
	 case kYellow: 
	     if (schedules)
	     {
		 if (HasSubMenu())
		 {
		     if (Count())
		     {
			 CursorDown();
			 return ShowSummary();
		     }
		 }
		 else if (toggleKeys == 0 || (toggleKeys == 1 && EPGSearchConfig.toggleGreenYellow == 0))
		 {
		     cMenuWhatsOnSearch::currentShowMode = showNext;
		     InWhatsOnMenu = true;
		     return AddSubMenu(new cMenuWhatsOnSearch(schedules, cMenuWhatsOnSearch::CurrentChannel()));
		 }
		 else
		 {
		     cChannel *channel = Channels.GetByNumber(currentChannel+1,1);
		     if (channel) {
			 PrepareSchedule(channel);
			 if (channel->Number() != cDevice::CurrentChannel()) {
			     otherChannel = channel->Number();
			 }
			 Display();
		     }
		     SetHelpKeys();
		     return osContinue;
		 }
	     }
	     break;
	 case kBlue:
	     if (HasSubMenu())
		 return Switch();
	     if (EPGSearchConfig.bluekeymode==toggleKeys)
		 return Switch();
	     else
		 return ExtendedSearch();
	     break;
       case kOk: 
	   if (Count())
	       return ShowSummary();
	   break;
	 default:
	     break;
     }
  }
  if (!HasSubMenu()) {
      cChannel *ch = cMenuWhatsOnSearch::ScheduleChannel();
      InWhatsOnMenu = false;
      if (ch) {
	  PrepareSchedule(ch);
	  if (ch->Number() != cDevice::CurrentChannel()) {
	      otherChannel = ch->Number();
	  }
	  Display();
      }
      else if ((HadSubMenu || gl_TimerProgged) && Update())
      {
	  if (gl_TimerProgged) // when using epgsearch's timer edit menu, update is delayed because of SVDRP
	  {
	      gl_TimerProgged = 0;
	      SetHelpKeys();
	  }
	  Display();
      }
      if (Key != kNone)
	  SetHelpKeys();
  }
  return state;
}

