#include "searchtimer_thread.h"
#include "epgsearchcfg.h"
#include "tools.h"
#include "changrp.h"
#include <vdr/tools.h>
#include <vdr/plugin.h>


cSearchTimerThread *cSearchTimerThread::m_Instance = NULL;

cSearchTimerThread::cSearchTimerThread(void)
: cThread("EPGSearch: searchtimer")
{
    m_Active = false;
    m_lastUpdate = time(NULL);
}

cSearchTimerThread::~cSearchTimerThread() {
    if (m_Active) 
	Stop();
}

void cSearchTimerThread::Init(void) {
    if (!EPGSearchConfig.useSearchTimers)
	return;
    if (m_Instance == NULL) {
	m_Instance = new cSearchTimerThread;
	m_Instance->Start();
    }
}

void cSearchTimerThread::Exit(void) {
    if (m_Instance != NULL) {
	m_Instance->Stop();
	DELETENULL(m_Instance);
    }
}

void cSearchTimerThread::Stop(void) {
    m_Active = false;
    Cancel(3);
}

cTimer *cSearchTimerThread::GetTimer(cSearchExt *searchExt, const cEvent *pEvent, bool& bTimesMatchExactly)
{
    cChannel *channel = Channels.GetByChannelID(pEvent->ChannelID(), true);
    if (!channel)
	return NULL;

    bool UseVPS = searchExt->useVPS && pEvent->Vps() && Setup.UseVps;
    struct tm tm_r;
    time_t eStart;
    if (!UseVPS)
	eStart = pEvent->StartTime();
    else
	eStart = pEvent->Vps();
    time_t eStop;
    if (!UseVPS)
	eStop = pEvent->EndTime();
    else
	eStop = pEvent->Vps() + pEvent->Duration();

    tm *tmStartEv = localtime_r(&eStart, &tm_r);
    
    for (cTimer *ti = Timers.First(); ti; ti = Timers.Next(ti)) 
    {
	if (ti->Channel() != channel)
	    continue;

	time_t tStart = ti->StartTime();
	time_t tStop = ti->StopTime();
	if (!UseVPS)
	{
	    tStart += searchExt->MarginStart * 60;
	    tStop  -= searchExt->MarginStop * 60; 
	}

	tm *tmStartTi = localtime_r(&tStart, &tm_r);
	if (tmStartEv->tm_mday != tmStartTi->tm_mday)
	    continue;

	bTimesMatchExactly = (tStart == eStart && tStop == eStop);
	
	if (abs(tStart - eStart) < 10 * 60 && abs(tStop - eStop) < 10 * 60) // accept a difference of max 10 min.
	    return ti;
    }
    return NULL;
}

void cSearchTimerThread::Action(void) 
{
    if (EPGSearchConfig.useExternalSVDRP && !SVDRPSendCmd)
    {
	esyslog("EPGSearch: ERROR - SVDRPSend script not specified or does not exist (use -f option)");
	return;
    }
    m_Active = true;
    // let VDR do its startup
    sleepSec(20);
    time_t nextUpdate = time(NULL);
    while (m_Active) 
    {
	time_t now = time(NULL);
	if (now >= nextUpdate || NeedUpdate())
	{
	    if (Timers.BeingEdited()) 		
	    {
		sleepSec(1);
		continue;
	    }
	    isyslog("EPGSearch: search timer update started");

	    cSearchExt *searchExt = SearchExts.First();
	    while (searchExt) {
		if (!searchExt->useAsSearchTimer)
		{
		    searchExt = SearchExts.Next(searchExt);
		    continue;
		}
		sleepMSec(1);
		cSchedulesLock schedulesLock;
		const cSchedules *schedules;
		schedules = cSchedules::Schedules(schedulesLock);
		if(!schedules) {
		    esyslog("cSearchTimerThread::Action scheduler is empty!");
		    continue;
		}

		const cSchedule *Schedule = schedules->First();
		const cEvent *pPrevEvent = NULL;
		const cEvent *pEvent = NULL;

		while (Schedule) {
		    if (searchExt->useChannel ==1 && searchExt->channelMin && searchExt->channelMax)
		    {
			cChannel* channel = Channels.GetByChannelID(Schedule->ChannelID(),true);
			if (channel && (searchExt->channelMin->Number() > channel->Number() || searchExt->channelMax->Number() < channel->Number()))
			{
			    Schedule = (const cSchedule *)schedules->Next(Schedule);
			    continue;
			}
		    }
		    if (searchExt && searchExt->useChannel == 2 && searchExt->channelGroup)
		    {
			cChannel* channel = Channels.GetByChannelID(Schedule->ChannelID(),true);
			cChannelGroup* group = ChannelGroups.GetGroupByName(searchExt->channelGroup);
			if (!group || !group->ChannelInGroup(channel))
			{
			    Schedule = (const cSchedule *)schedules->Next(Schedule);
			    continue;
			}
		    }

		    pPrevEvent = NULL;
		    do {
			pEvent = searchExt->GetEventBySearchExt(Schedule, pPrevEvent);
			pPrevEvent = pEvent;
			if (pEvent) {
			    
			    cChannel *channel = Channels.GetByChannelID(pEvent->ChannelID(), true);
			    if (channel) {
				int index = 0;
				cTimer *timer = new cTimer(pEvent);

				// create the file 
				char* file = NULL;
				if ((file = searchExt->BuildFile(pEvent)) != NULL)
				{
				    timer->SetFile(file);
				    free(file);
				}
				int Priority = searchExt->Priority;
				int Lifetime = searchExt->Lifetime;

				// search for an already existing timer
				bool bTimesMatchExactly = false;
				cTimer *t = GetTimer(searchExt, pEvent, bTimesMatchExactly);
				if (t) 
				{ // already exists
				    if (!t->HasFlags(tfActive))
				    { // do not update inactive timers
					delete timer;
					continue;
				    }

				    char* pFile = NULL; // File is prepared for svdrp, so prepare t->File for comparision too
				    asprintf(&pFile, "%s", t->File());
				    pFile = strreplace(pFile, ':', '|');
				    pFile = strreplace(pFile, " ~", "~");
				    pFile = strreplace(pFile, "~ ", "~");
				    
				    if (bTimesMatchExactly && strcmp(pFile, timer->File()) == 0 && 
				       ((t->Summary() == NULL && pEvent->Description() == NULL) ||
					(t->Summary() != NULL && pEvent->Description() != NULL) && strcmp(t->Summary(), pEvent->Description()) == 0))
				    { // dir, title and episode name have not changed
					delete timer;
					free(pFile);
					continue;
				    }
				    else
				    {
					index = t->Index()+1;
					Priority = t->Priority();
					Lifetime = t->Lifetime();
				    }
				    free(pFile);
				}

				if (index == 0 && searchExt->announceOnly) // only announce if there no timer for the event
				{
				    char* Message = NULL;
				    asprintf(&Message, "%s: %s - %s %s", pEvent->Title(), CHANNELNAME(channel), GETDATESTRING(pEvent), GETTIMESTRING(pEvent));
				    SendMsg(Message);
				    free(Message);
				    sleepSec(5);
				    continue;
				}
				
				AddModTimer(timer, index, searchExt, pEvent, Priority, Lifetime);
				delete timer;
				if (index == 0)
				    isyslog("EPGSearch: added timer for %s (%s - %s)", pEvent->Title(), GETDATESTRING(pEvent), GETTIMESTRING(pEvent));
				else
				    isyslog("EPGSearch: modified timer %d for %s (%s - %s)", index, pEvent->Title(), GETDATESTRING(pEvent), GETTIMESTRING(pEvent));
			    }
			}
		    } while(pPrevEvent);
		    Schedule = (const cSchedule *)schedules->Next(Schedule);
		}

		searchExt = SearchExts.Next(searchExt);
	    }
	    m_lastUpdate = time(NULL);
	    nextUpdate = m_lastUpdate + (EPGSearchConfig.UpdateIntervall * 60);
	    isyslog("EPGSearch: search timer update finished");

	    // check for conflicts
	    if (EPGSearchConfig.checkTimerConflicts)
	    {
		isyslog("EPGSearch: check for timer conflicts");
		cPlugin *p = cPluginManager::GetPlugin("timeline");
		if (p)
		{
		    if (!Timers.BeingEdited()) 		
		    {
			char *args[]={"timeline_command_interface","conflicts"};
			bool timeline_found_conflict=p->ProcessArgs(1,args);
			if (timeline_found_conflict)
			{
			    isyslog("EPGSearch: conflict found");	    
			    SendMsg(tr("Attention: conflicting timers found!"));			
			}
		    }
		}
		isyslog("EPGSearch: check for timer conflicts - done");	    
	    }
	}
	sleepSec(5);
    };
    isyslog("EPGSearch: Leaving search timer thread");
}

bool cSearchTimerThread::NeedUpdate()
{
    return  m_lastUpdate <= LastModifiedTime(AddDirectory(CONFIGDIR, ".epgsearchupdate"));
}

void cSearchTimerThread::AddModTimer(cTimer* Timer, int index, cSearchExt* searchExt, const cEvent* pEvent, int Prio, int Lifetime) 
{
    char *cmdbuf = NULL;

    static char bufStart[25];
    static char bufEnd[25];

    struct tm tm_r;    time_t eStart = pEvent->StartTime();
    time_t eStop = pEvent->EndTime();
    time_t start = eStart - (searchExt->MarginStart * 60);
    time_t stop  = eStop + (searchExt->MarginStop * 60);
    int Flags = Timer->Flags();
    if (searchExt->useVPS && pEvent->Vps() && Setup.UseVps)
    {
	start = pEvent->Vps();
	stop = start + pEvent->Duration();
    }
    else
	Flags = 1; // don't use VPS, if not set in this search

    strftime(bufStart, sizeof(bufStart), "%H%M", localtime_r(&start, &tm_r));
    strftime(bufEnd, sizeof(bufEnd), "%H%M", localtime_r(&stop, &tm_r));

    char* tmpSummary = NULL;
    if (pEvent->Description())
    {
	tmpSummary = strdup(pEvent->Description());
	strreplace(tmpSummary, '\n', '|');
    }
    else
	tmpSummary = strdup("");

    if (index==0)
#if VDRVERSNUM && VDRVERSNUM >= 10323
	asprintf(&cmdbuf, "NEWT %d:%d:%s:%s:%s:%d:%d:%s:%s", 
#else
	asprintf(&cmdbuf, "NEWT %d:%d:%d:%s:%s:%d:%d:%s:%s", 
#endif
		 Flags,
		 Timer->Channel()->Number(),
#if VDRVERSNUM && VDRVERSNUM >= 10323
		 *Timer->PrintDay(start, Timer->WeekDays()),
#else
		 localtime_r(&start, &tm_r)->tm_mday,
#endif
		 bufStart,
		 bufEnd,
		 Prio, 
		 Lifetime, 
		 Timer->File(),
		 tmpSummary);
		 else
#if VDRVERSNUM && VDRVERSNUM >= 10323
	asprintf(&cmdbuf, "MODT %d %d:%d:%s:%s:%s:%d:%d:%s:%s", 
#else
	asprintf(&cmdbuf, "MODT %d %d:%d:%d:%s:%s:%d:%d:%s:%s", 
#endif
		 index,
		 Flags,
		 Timer->Channel()->Number(),
#if VDRVERSNUM && VDRVERSNUM >= 10323
		 *Timer->PrintDay(start, Timer->WeekDays()),
#else
		 localtime_r(&start, &tm_r)->tm_mday,
#endif
		 bufStart,
		 bufEnd,
		 Prio, 
		 Lifetime, 
		 Timer->File(),
		 tmpSummary);

    SendViaSVDRP(cmdbuf);
	      
    free(cmdbuf);
    free(tmpSummary);
}

