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

#define HOURS(x) ((x)/100)
#define MINUTES(x) ((x)%100)

// --- cMenuWhatsOnSearchItem ------------------------------------------------------

cMenuWhatsOnSearchItem::cMenuWhatsOnSearchItem(const cEvent *Event, cChannel *Channel, int iProgress, showMode ShowMode)
{
  event = Event;
  channel = Channel;
  char *buffer = NULL;
  int TimerMatch;
  char t = Timers.GetMatch(Event, &TimerMatch) ? (TimerMatch == tmFull) ? 'T' : 't' : ' ';
  char v = event->Vps() && (event->Vps() - event->StartTime()) ? 'V' : ' ';
  char r = event->IsRunning() ? '*' : ' ';
  if (iProgress > 0)
  {
      int progress = 0;
      if (ShowMode == showNow)
	  progress = (int)roundf( (float)(time(NULL) - event->StartTime()) / (float)(event->Duration()) * 8.0 );
      if (ShowMode == showNext)
	  progress = (int)roundf( (float)(1 - min(float(30*60), float(event->StartTime() - time(NULL))) / (30*60)) * 8.0 );
      if (progress < 0) progress = 0;
      else if (progress > 8) progress = 8;

      char szProgress[9]="";
      memset(szProgress, 0, sizeof(szProgress));
      memset(szProgress, ' ', sizeof(szProgress)-1);
      for(int i = 0; i < progress; i++)
	  szProgress[i] = (iProgress == 1 ? '|' : 127);

      if (EPGSearchConfig.showChannelNr)
	  asprintf(&buffer, "%d\t%.*s\t%s\t%c%s%c\t %c%c%c \t%s", channel->Number(), 10, CHANNELNAME(channel), GETTIMESTRING(event), (iProgress==1?'[':128), szProgress, (iProgress==1?']':129), t, v, r, event->Title());
      else
	  asprintf(&buffer, "%.*s\t%s\t%c%s%c\t %c%c%c \t%s", 12, CHANNELNAME(channel), GETTIMESTRING(event), (iProgress==1?'[':128), szProgress, (iProgress==1?']':129), t, v, r, event->Title());
  }
  else
      if (EPGSearchConfig.showChannelNr)
	  asprintf(&buffer, "%d\t%.*s\t%s\t %c%c%c \t%s", channel->Number(), 10, CHANNELNAME(channel), GETTIMESTRING(event), t, v, r, event->Title());
      else
	  asprintf(&buffer, "%.*s\t%s\t %c%c%c \t%s", 12, CHANNELNAME(channel), GETTIMESTRING(event), t, v, r, event->Title());
  SetText(buffer, false);
}


// --- cMenuWhatsOnSearch ----------------------------------------------------------

int cMenuWhatsOnSearch::currentChannel = 0;
showMode cMenuWhatsOnSearch::currentShowMode = showNow;
const cEvent *cMenuWhatsOnSearch::scheduleEvent = NULL;
extern const char *ShowModes[];

cMenuWhatsOnSearch::cMenuWhatsOnSearch(const cSchedules *Schedules, int CurrentChannelNr)
    :cOsdMenu(EPGSearchConfig.ShowModes[currentShowMode].GetDescription(), GetTab(1), GetTab(2), GetTab(3), GetTab(4), GetTab(5))
{
    shiftTime = 0;
    schedules = Schedules;

    LoadSchedules();

    currentChannel = CurrentChannelNr;
    
    char* szTitle = NULL;
    asprintf(&szTitle, "%s - %s", tr("Overview"), EPGSearchConfig.ShowModes[currentShowMode].GetDescription());
    SetTitle(szTitle);
    free(szTitle);
    SetHelpKeys();
}

cMenuWhatsOnSearch::~cMenuWhatsOnSearch()
{
}

int cMenuWhatsOnSearch::GetTab(int Tab)
{
    if (Tab==1)
    {
	return EPGSearchConfig.showChannelNr?CHNUMWIDTH:6+CHNUMWIDTH;
    }
    if (Tab==2)
	return EPGSearchConfig.showChannelNr?10:6;
    if (Tab==3)
	if (currentShowMode==showNow || currentShowMode==showNext)
	    return EPGSearchConfig.showChannelNr?7:EPGSearchConfig.ProgressWidth;
	else
	    return EPGSearchConfig.showChannelNr?5:4;
    if (Tab==4)
	return EPGSearchConfig.showChannelNr?EPGSearchConfig.ProgressWidth:4;
    if (Tab==5)
	return 4;
    return 0;
}

void cMenuWhatsOnSearch::LoadSchedules()
{
    Clear();

    time_t SeekTime;
    char* szTitle = NULL;
    if (shiftTime != 0)
    {
	if (currentShowMode == showNow || currentShowMode == showNext)
	    SeekTime = time(NULL);
	else
	    SeekTime = GetTimeT(EPGSearchConfig.ShowModes[currentShowMode].GetTime());
	SeekTime += shiftTime*60;

	struct tm tm_r;
	time_t now = time(NULL);
	tm tm_seek = *localtime_r(&SeekTime, &tm_r);
	tm tm_now = *localtime_r(&now, &tm_r);
	if (tm_seek.tm_mday != tm_now.tm_mday)
	    asprintf(&szTitle, "%s - %s", tr("Overview"), DAYDATETIME(SeekTime));
	else
	    asprintf(&szTitle, "%s - %02d:%02d", tr("Overview"), tm_seek.tm_hour, tm_seek.tm_min);
    }
    else
	asprintf(&szTitle, "%s - %s", tr("Overview"), EPGSearchConfig.ShowModes[currentShowMode].GetDescription());
    
    SetTitle(szTitle);
    free(szTitle);
	
    for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) {
	if (!Channel->GroupSep()) {
	    const cSchedule *Schedule = schedules->GetSchedule(Channel->GetChannelID());

         if (Schedule) {
	     const cEvent *Event = NULL;
	     if (shiftTime != 0)
		 Event = Schedule->GetEventAround(SeekTime);
	     else
	     {
		 switch(currentShowMode)
		 {  
		     default:
		     case showNow: 
			 Event = Schedule->GetPresentEvent();
			 break;
		     case showNext:
			 Event = Schedule->GetFollowingEvent();
			 break;
		     case showUserMode1:
		     case showUserMode2:
		     case showUserMode3:
		     case showUserMode4:
			 time_t SeekTime = GetTimeT(EPGSearchConfig.ShowModes[currentShowMode].GetTime());
			 Event = Schedule->GetEventAround(SeekTime);
			 break;
		 }
	     }
	     if (Event)
	     {
		 int iProgress = 0;
		 if (currentShowMode==showNow)
		     iProgress = EPGSearchConfig.showProgress;
		 if (currentShowMode==showNext)
		     iProgress = EPGSearchConfig.showProgressNext;
		 Add(new cMenuWhatsOnSearchItem(Event, Channel, iProgress, currentShowMode), Channel->Number() == currentChannel);
	     }
	 }
	}
    }
}

time_t cMenuWhatsOnSearch::GetTimeT(int iTime)
{
    struct tm tm_r;
    time_t t = time(NULL);
    tm* tmnow = localtime_r(&t, &tm_r);
    tmnow->tm_hour = HOURS(iTime);
    tmnow->tm_min = MINUTES(iTime);
    return mktime(tmnow);
}

showMode cMenuWhatsOnSearch::GetNextMode()
{
    showMode nextShowMode = currentShowMode;
    time_t now = time(NULL);
    do
    {
	nextShowMode = (showMode)((nextShowMode+1) % showModeMax);
	if (nextShowMode >= showUserMode1)
	{
	    if (!EPGSearchConfig.ShowModes[nextShowMode].GetUsage())
		continue;

	    time_t TestTime = GetTimeT(EPGSearchConfig.ShowModes[nextShowMode].GetTime());    
	    if (TestTime < now)
		continue;
	    break;
	}
	else
	    break;
    }
    while(true);
    return nextShowMode;
}
const cEvent *cMenuWhatsOnSearch::ScheduleEvent(void)
{
  const cEvent *ei = scheduleEvent;
  scheduleEvent = NULL;
  return ei;
}


eOSState cMenuWhatsOnSearch::Switch(void)
{
  cMenuWhatsOnSearchItem *item = (cMenuWhatsOnSearchItem *)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 cMenuWhatsOnSearch::Record(void)
{
  cMenuWhatsOnSearchItem *item = (cMenuWhatsOnSearchItem *)Get(Current());
  if (item) {
     cTimer *timer = new cTimer(item->event);
     cTimer *t = Timers.GetTimer(timer);
     if (t) {
        delete timer;
        timer = t;
        }
     return AddSubMenu(new cMenuMyEditTimer(timer, !t, item->event));
     }
  return osContinue;
}

eOSState cMenuWhatsOnSearch::Commands(eKeys Key)
{
  if (HasSubMenu() || Count() == 0)
     return osContinue;
  cMenuWhatsOnSearchItem *mi = (cMenuWhatsOnSearchItem *)Get(Current());
  if (mi) {
        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 cMenuWhatsOnSearch::ExtendedSearch(void)
{
    return AddSubMenu(new cMenuEPGSearchExt());
}

void cMenuWhatsOnSearch::SetHelpKeys()
{
    showMode nextShowMode = GetNextMode();    
    char* szButtonGreen = EPGSearchConfig.ShowModes[nextShowMode].GetDescription();
  if (toggleKeys==0)
      SetHelp((EPGSearchConfig.redkeymode==0?tr("Record"):tr("Button$Commands")), szButtonGreen, tr("Button$Schedule"), EPGSearchConfig.bluekeymode==0?tr("Switch"):tr("Button$Search"));
  else
      SetHelp((EPGSearchConfig.redkeymode==1?tr("Record"):tr("Button$Commands")), (EPGSearchConfig.toggleGreenYellow==0?szButtonGreen:"<<"), (EPGSearchConfig.toggleGreenYellow==0?tr("Button$Schedule"):">>"), EPGSearchConfig.bluekeymode==1?tr("Switch"):tr("Button$Search"));
}

eOSState cMenuWhatsOnSearch::Shift(int iMinutes)
{
    shiftTime += iMinutes;
    cMenuWhatsOnSearchItem *mi = (cMenuWhatsOnSearchItem *)Get(Current());
    int TempChannel = currentChannel;
    if (mi) 
    {
	scheduleEvent = mi->event;
	currentChannel = mi->channel->Number();
    }
    LoadSchedules();
    Display();
    currentChannel = TempChannel;
    return osContinue;
}

eOSState cMenuWhatsOnSearch::ShowSummary()
{
    if (Count())
    {
	const cEvent *ei = ((cMenuWhatsOnSearchItem *)Get(Current()))->event;
	if (ei) 
	{
	    cChannel *channel = Channels.GetByChannelID(ei->ChannelID(), true);
	    if (channel)
		return AddSubMenu(new cMenuEventSearch(ei, channel->Number() != cDevice::CurrentChannel()));
	}
    }
    return osContinue;
}

eOSState cMenuWhatsOnSearch::ProcessKey(eKeys Key)
{
    if (!HasSubMenu() && Key == kBack)
	return osEnd;

    eOSState state = cOsdMenu::ProcessKey(Key);

    if (state == osUnknown) {
	switch (Key) {
	    case kFastRew:
		if(HasSubMenu()) 	     
		{
		    CursorUp();
		    return ShowSummary();
		}
		else
		    return Shift(-EPGSearchConfig.timeShiftValue);
	    case kFastFwd:
		if(HasSubMenu()) 	     
		{
		    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
			{
				cMenuWhatsOnSearchItem *mi = (cMenuWhatsOnSearchItem *)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: return Commands(Key);
	 case kYellow: 
	     if(HasSubMenu()) 	     
	     {
		 CursorDown();
		 return ShowSummary();
	     }
	     else if (toggleKeys == 0 || (toggleKeys == 1 && EPGSearchConfig.toggleGreenYellow == 0))
	     {
		 state = osBack;
		 // continue with kGreen
	     }
	     else
		 return Shift(EPGSearchConfig.timeShiftValue);
	 case kGreen:  
	 {
	     if(HasSubMenu()) 	     
	     {
		 CursorUp();
		 return ShowSummary();
	     }
	     else if (toggleKeys == 0 || (toggleKeys == 1 && EPGSearchConfig.toggleGreenYellow == 0))
	     {
		 if (Key == kYellow)
		     currentShowMode = showNow;
		 else
		     currentShowMode = GetNextMode();
		 cMenuWhatsOnSearchItem *mi = (cMenuWhatsOnSearchItem *)Get(Current());
		 if (mi) 
		 {
		     scheduleEvent = mi->event;
		     currentChannel = mi->channel->Number();
		 }
	     }
	     else
		 return Shift(-EPGSearchConfig.timeShiftValue);
	 }
	 break;
	 case kBlue:   
	     if (HasSubMenu())
		 return Switch();
	     if (EPGSearchConfig.bluekeymode==toggleKeys)
		 return Switch();
	     else
		 return ExtendedSearch();
	   break;
	 case kOk:
	     return ShowSummary();
	     break;
       default:      break;
     }
  }
  return state;
}


