#include "searchtimer_thread.h"
#include "epgsearchcfg.h"
#include "epgsearchcats.h"
#include "tools.h"
#include "changrp.h"
#include "recdone.h"
#include "switchtimer.h"
#include <vdr/tools.h>
#include <vdr/plugin.h>
#include "switchtimer_thread.h"

#if VDRVERSNUM >= 10330 || defined(PATCH_CUSTOMSERVICES)
#include "services.h"
#endif

extern int updateForced;

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(6);
}


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;
}

bool cSearchTimerThread::TimerWasModified(cTimer* t)
{
#if VDRVERSNUM > 10343 
    char* start = GetAuxValue(t, "Start");
    char* stop = GetAuxValue(t, "Stop");
    bool bMod = false;
    if (start || stop)
    {
	time_t StartTime = time_t(atol(start));
	time_t StopTime = time_t(atol(stop));
	if (t->StartTime() != StartTime || t->StopTime() != StopTime)
	    bMod = true;
    }
    if (start) free(start);
    if (stop) free(stop);
    return bMod;
#else
    return false;
#endif    
}

void cSearchTimerThread::Action(void) 
{
    if (EPGSearchConfig.useExternalSVDRP && !SVDRPSendCmd)
    {
	esyslog("EPGSearch: ERROR - SVDRPSend script not specified or does not exist (use -f option)");
	return;
    }
#if VDRVERSNUM >= 10325 
    SetPriority(19);
#endif

   m_Active = true;
    // let VDR do its startup
    sleepSec(20);
    time_t nextUpdate = time(NULL);
    while (m_Active) 
    {
	time_t now = time(NULL);
	bool needUpdate = NeedUpdate();
	if (now >= nextUpdate || needUpdate)
	{
	    if (Timers.BeingEdited()) 		
	    {
		sleepSec(1);
		continue;
	    }
	    isyslog("EPGSearch: search timer update started");
	    LogFile.Log(1,"search timer update started");

	    cTimerObjList* pOutdatedTimers = NULL;

	    cSearchExt *searchExt = SearchExts.First();
	    while (searchExt) 
	    {
		if (!searchExt->useAsSearchTimer)
		{
		    searchExt = SearchExts.Next(searchExt);
		    continue;
		}
//		sleepMSec(1);
#if VDRVERSNUM > 10343 
		pOutdatedTimers = searchExt->GetTimerList(pOutdatedTimers);
#endif
		cSearchResults* pSearchResults = searchExt->Run(false, true);
		if (!pSearchResults)
		{
		    searchExt = SearchExts.Next(searchExt);
		    continue;
		}
		pSearchResults->SortBy(CompareEventTime);

		if (searchExt->avoidRepeats)
		    searchExt->CheckRepeatTimers(pSearchResults);

		if (searchExt->pauseOnNrRecordings > 0)
		    searchExt->CheckExistingRecordings(pSearchResults);

		for (cSearchResult* pResultObj = pSearchResults->First(); 
		     pResultObj; 
		     pResultObj = pSearchResults->Next(pResultObj))
		{
			const cEvent* pEvent = pResultObj->event;
			if (!pEvent)
			    continue;
			    
			cChannel *channel = Channels.GetByChannelID(pEvent->ChannelID(), true);
			if (!channel)
			    continue;
			
			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);

			char* Summary = NULL;
			if (t) 
			{ // already exists
			    pOutdatedTimers->DelTimer(t);

			    if (!t->HasFlags(tfActive))
			    { // do not update inactive timers
				LogFile.Log(2,"timer for '%s~%s' (%s - %s, channel %d) not active - won't be touched", pEvent->Title()?pEvent->Title():"no title", pEvent->ShortText()?pEvent->ShortText():"no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent));
				delete timer;
				continue;
			    }
			    
			    if (!pResultObj->needsTimer && !t->Recording()) // not needed
			    {
 				LogFile.Log(1,"delete timer for '%s~%s' (%s - %s, channel %d)", pEvent->Title()?pEvent->Title():"no title", pEvent->ShortText()?pEvent->ShortText():"no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent));
				DelTimer(t->Index()+1);
				delete timer;
				continue;
			    }
#if VDRVERSNUM > 10343 			    
			    if (TimerWasModified(t)) // don't touch timer modified by user 
			    {
 				LogFile.Log(2,"timer for '%s~%s' (%s - %s, channel %d) modified by user - won't be touched", pEvent->Title()?pEvent->Title():"no title", pEvent->ShortText()?pEvent->ShortText():"no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent));
				delete timer;
				continue;
			    }
#endif
			    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, "~ ", "~");
			    
			    Summary =  SummaryExtended(searchExt, t, pEvent);
			    
			    if (bTimesMatchExactly && strcmp(pFile, timer->File()) == 0
#if VDRVERSNUM && VDRVERSNUM <= 10343
				&& (t->Summary() != NULL && strcmp(t->Summary(), Summary) == 0)
#else
				&& (t->Aux() != NULL && strcmp(t->Aux(), Summary) == 0)
#endif
				)
			    { // dir, title, episode name and summary have not changed
				if (Summary) free(Summary);
				delete timer;
				free(pFile);
				continue;
			    }
			    else
			    {
				index = t->Index()+1;
				Priority = t->Priority();
				Lifetime = t->Lifetime();
			    }
			    free(pFile);
			}			       
			else
			{
			    if (!pResultObj->needsTimer)
			    {
				delete timer;
				continue;
			    }
			}
		
			if (index == 0 && searchExt->action == searchTimerActionAnnounceOnly) // only announce if there is 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);
			    if (Summary) free(Summary);
			    delete timer;
			    continue;
			}

			if (searchExt->action == searchTimerActionSwitchOnly) // add to switch list
			{
			    time_t now = time(NULL);
			    if (now < pEvent->StartTime())
			    {
				if (!SwitchTimers.InSwitchList(pEvent))
				{				
				    SwitchTimers.Add(new cSwitchTimer(pEvent, searchExt->switchMinsBefore));
				    SwitchTimers.Save();
				    cSwitchTimerThread::Init(); 
				}
			    }
			    if (Summary) free(Summary);
			    delete timer;
			    continue;
			}
			
			AddModTimer(timer, index, searchExt, pEvent, Priority, Lifetime, Summary);
			if (Summary) free(Summary);
			delete timer;
			if (index == 0)
			    LogFile.Log(1,"added timer for '%s~%s' (%s - %s); search timer: '%s'", pEvent->Title(), pEvent->ShortText()?pEvent->ShortText():"", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), searchExt->search);
			else
			    LogFile.Log(1,"modified timer %d for '%s~%s' (%s - %s); search timer: '%s'", index, pEvent->Title(), pEvent->ShortText()?pEvent->ShortText():"", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), searchExt->search);
		}
		searchExt = SearchExts.Next(searchExt);
	    }

	    if (pOutdatedTimers)
	    {
		if (pOutdatedTimers->Count() > 0)
		{
		    LogFile.Log(1,"removing outdated timers");
		    for(cTimerObj *tObj = pOutdatedTimers->First(); tObj; tObj = pOutdatedTimers->Next(tObj))
		    {
			cTimer* t = tObj->timer;
			// timer could have been deleted meanwhile, so check if its still there
			bool found = false;
                        for(cTimer* checkT = Timers.First(); checkT; checkT = Timers.Next(checkT)) 
			    if (checkT == t)
			    {
				found = true;
				break;
			    }
			if (!found) continue;

			if (TimerWasModified(t)) continue;
			if (!t->Event()) continue; // if there is no event, we keep the timer, since EPG could have been cleared
			time_t now = time(NULL);
			if (abs(t->StopTime() - now) < 120) continue; // if this timer has just finished, let VDR do the cleanup
			LogFile.Log(1,"delete timer for '%s' (%s, channel %s)", t->File(), DAYDATETIME(t->StartTime()), CHANNELNAME(t->Channel()));
			DelTimer(t->Index()+1);
		    }
		    LogFile.Log(1,"removing outdated timers - done");
		}
		delete pOutdatedTimers;
	    }

	    isyslog("EPGSearch: search timer update finished");
	    LogFile.Log(1,"search timer update finished");

	    // check for conflicts
	    if (EPGSearchConfig.checkTimerConflicts)
	    {
		isyslog("EPGSearch: check for timer conflicts");
		LogFile.Log(1,"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");	    
			    LogFile.Log(1,"conflict found");	    
			    SendMsg(tr("Attention: conflicting timers found!"));			
			}
		    }
		}
		isyslog("EPGSearch: check for timer conflicts - done");	    
		LogFile.Log(1,"check for timer conflicts - done");	    
	    }

	    // delete expired recordings
	    CheckExpiredRecs();
	    LogFile.Log(1, "check for expired recordings done");

	    if (updateForced == 2)
		SendMsg(tr("Search timer update done!"));

	    // reset service call flag
	    updateForced = 0;

	    m_lastUpdate = time(NULL);
	    nextUpdate = long(m_lastUpdate/60)*60 + (EPGSearchConfig.UpdateIntervall * 60);
	}
	sleepSec(2); // to avoid high system load if time%30==0 
	while (m_Active && !NeedUpdate() && time(NULL)%30 != 0) // sync heart beat to a multiple of 5secs
	    sleepSec(1);
    };
    isyslog("EPGSearch: Leaving search timer thread");
}

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

int cSearchTimerThread::GetCountTimers(const cEvent* event, bool compareTitle, bool compareSubtitle, bool compareSummary, unsigned long catvaluesAvoidRepeat, cTimer** first)
{
    if (!event) return 0;

    *first = NULL;
    int count = 0;
    for (cTimer *ti = Timers.First(); ti; ti = Timers.Next(ti)) 
    {
	if (!ti->HasFlags(tfActive))
	    continue;

	const cEvent* t_event = ti->Event();
	if (!t_event)
	    t_event = GetEvent(ti);
	if (!t_event)
	    continue;

	if (EventsMatch(event, t_event, compareTitle, compareSubtitle, compareSummary, catvaluesAvoidRepeat))
	{
	    if (!*first) *first = ti;
	    else
		if ((*first)->StartTime() > ti->StartTime())
		    *first = ti;	    
	    count++;
	}
    }
    return count;
}

char* cSearchTimerThread::SummaryExtended(cSearchExt* searchExt, cTimer* Timer, const cEvent* pEvent)
{
    char* addSummaryHeader = NULL;
    const char* title = pEvent->Title();
    const char* subtitle = pEvent->ShortText();
    char sep = '\n';

#if VDRVERSNUM && VDRVERSNUM > 10343
    time_t eStart = pEvent->StartTime();
    time_t eStop = pEvent->EndTime();
    time_t start = eStart - (searchExt->MarginStart * 60);
    time_t stop  = eStop + (searchExt->MarginStop * 60);
    sep = '|';
#endif
    asprintf(&addSummaryHeader, "%s%c%s%s%s", 
	     title?title:"", sep,
	     subtitle?tr("Subtitle"):"", subtitle?": ":"", subtitle?subtitle:"");

    char* addSummaryFooter = NULL;
#if VDRVERSNUM && VDRVERSNUM > 10343
    asprintf(&addSummaryFooter, "<Channel>%d - %s</Channel><Search timer>%s</Search timer><Start>%ld</Start><Stop>%ld</Stop><S-ID>%d</S-ID>", 
	     Timer->Channel()->Number(), CHANNELNAME(Timer->Channel()),
	     searchExt->search, 
	     start, 
	     stop, 
	     searchExt->ID);
#else
    asprintf(&addSummaryFooter, "%s: %d - %s%c%s: %s%c%s: %d",
             tr("Channel"), Timer->Channel()->Number(), CHANNELNAME(Timer->Channel()), sep,
             tr("Search timer"), searchExt->search, sep,
             "S-ID", searchExt->ID);
#endif

#if VDRVERSNUM && VDRVERSNUM <= 10343
    // already there?
    if (pEvent->Description() && strstr(pEvent->Description(), addSummaryHeader) && 
	strstr(pEvent->Description(), addSummaryFooter))
    {
	free(addSummaryHeader);
	free(addSummaryFooter);
	return strdup(pEvent->Description());
    }
    else
    {
	char* tmpSummary = NULL;
	asprintf(&tmpSummary, "%s%c%c%s%c%s", 
		 addSummaryHeader,sep,sep,
		 pEvent->Description()?pEvent->Description():"", sep,
		 addSummaryFooter);
	free(addSummaryHeader);
	free(addSummaryFooter);
	return tmpSummary;
    }
#else
    const char* aux = Timer->Aux();
    // remove epgsearch entries
    char* tmpaux = NULL;
    if (!isempty(aux))
    {
	tmpaux = strdup(aux);
	char* begin = strstr(aux, "<epgsearch>");
	char* end = strstr(aux, "</epgsearch>");
	if (begin && end)
	{
	    if (begin == aux) strcpy(tmpaux, ""); else strn0cpy(tmpaux, aux, begin-aux+1);
	    strcat(tmpaux, end + strlen("</epgsearch>"));
	}
    }

    char* tmpSummary = NULL;
    asprintf(&tmpSummary, "<epgsearch>%s</epgsearch>%s", addSummaryFooter, tmpaux?tmpaux:"");
    free(addSummaryHeader);
    free(addSummaryFooter);
    if (tmpaux) free(tmpaux);
    return tmpSummary;
#endif
}

void cSearchTimerThread::AddModTimer(cTimer* Timer, int index, cSearchExt* searchExt, const cEvent* pEvent, int Prio, int Lifetime, char* Summary) 
{
    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));

    // add additonal info
    char* tmpSummary = NULL;
    if (Summary)
    {
	tmpSummary = strdup(Summary);
	strreplace(tmpSummary, '\n', '|');
    }
    else 
	tmpSummary = SummaryExtended(searchExt, Timer, pEvent);

    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?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?tmpSummary:"");

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

void cSearchTimerThread::DelRecording(int index)
{
    char *cmdbuf = NULL;   // first LSTR required 
    asprintf(&cmdbuf, "DELR %d", index);    
    LogFile.Log(2, "delete recording %d", index);
    SendViaSVDRP(cmdbuf);
	      
    free(cmdbuf); 
}

// counts the currently existent recordings triggered by this search timer
int cSearchTimerThread::GetCountRecordings(cSearchExt* search)
{
    int countRecs = 0;
#if VDRVERSNUM <= 10310    
    cRecordings Recordings;
    bool recordings = Recordings.Load();
    if (!recordings)
	return 0;
#endif

    for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) 
    {
	if (recording->IsEdited()) continue; // ignore recordings edited
#if VDRVERSNUM <= 10324 
	const char* summary = recording->Summary();
	char* searchID = GetExtEPGValue(summary, "S-ID");	
#else
	if (!recording->Info()) continue;
#if VDRVERSNUM <= 10343 
	const char* summary = recording->Info()->Description();
	if (isempty(summary)) continue;
	char* searchID = GetExtEPGValue(summary, "S-ID");	
#else
	char* searchID = GetAuxValue(recording, "S-ID");
#endif
#endif

	if (!searchID) continue;
	if (search == SearchExts.GetSearchFromID(atoi(searchID)))
	    countRecs++;
	free(searchID);
    }
    LogFile.Log(3, "found %d recordings for search '%s'", countRecs, search->search);
    return countRecs;
}

void cSearchTimerThread::CheckExpiredRecs()
{
#if VDRVERSNUM <= 10310    
    cRecordings Recordings;
    bool recordings = Recordings.Load();
    if (!recordings)
	return;
#endif

#if VDRVERSNUM >= 10333    
    cThreadLock RecordingsLock(&Recordings);
#endif
    cList<cRecordingObj> DelRecordings;
    for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) 
    {
	LogFile.Log(3, "check recording %s from %s for expiration", recording->Name(), DAYDATETIME(recording->start));
	if (recording->start == 0)
	{
	    LogFile.Log(2, "oops, recording %s has no start time, skipped", recording->Name());
	    continue;
	}
	if (recording->IsEdited()) continue;

#if VDRVERSNUM <= 10324 
	const char* summary = recording->Summary();
	char* searchID = GetExtEPGValue(summary, "S-ID");	
	char* searchName = GetExtEPGValue(summary, tr("Search timer"));	
#else
	if (!recording->Info()) continue;
#if VDRVERSNUM <= 10343 
	const char* summary = recording->Info()->Description();
	if (isempty(summary)) continue;
	char* searchID = GetExtEPGValue(summary, "S-ID");	
	char* searchName = GetExtEPGValue(summary, tr("Search timer"));	
#else
	char* searchID = GetAuxValue(recording, "S-ID");	
	char* searchName = GetAuxValue(recording, "Search timer");	
	if (!searchName)
	    searchName = GetAuxValue(recording, tr("Search timer"));	
#endif
#endif
	if (!searchID || !searchName)
	{
	    if (searchID) free(searchID);
	    if (searchName) free(searchName);
	    continue;
	}
	cSearchExt* search = SearchExts.GetSearchFromID(atoi(searchID));
	if (!search || strcmp(search->search, searchName) != 0) 
	{
	    if (searchID) free(searchID);
	    if (searchName) free(searchName);
	    continue;
	}
	LogFile.Log(3, "recording triggered from search timer %s", search->search);
	if (search->delAfterDays == 0) continue;
	time_t now = time(NULL);
	int daysBetween = int(double((now - recording->start)) / (60*60*24));
	if (daysBetween  >= search->delAfterDays)
	    DelRecordings.Add(new cRecordingObj(recording, search));
	else
	    LogFile.Log(3, "recording will expire in %d days, search timer %s", search->delAfterDays - daysBetween, search->search);
    }    
    for (cRecordingObj *recordingObj = DelRecordings.First(); recordingObj; recordingObj = DelRecordings.Next(recordingObj)) 
    {
	cRecording* recording = recordingObj->recording;
	cSearchExt* search = recordingObj->search;
	if (search->recordingsKeep > 0 && search->recordingsKeep >= GetCountRecordings(search))
	{
	    LogFile.Log(1, "recording '%s' from %s expired, but will be kept, search timer %s", recording->Name(), DAYDATETIME(recording->start), recordingObj->search->search);
	    continue;
	}
	LogFile.Log(1, "delete expired recording '%s' from %s, search timer %s", recording->Name(), DAYDATETIME(recording->start), recordingObj->search->search);
	cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
	if (!rc) 
	{
	    if (!recording->Delete())
		LogFile.Log(1, "error deleting recording!");
	    else
#if VDRVERSNUM >= 10312    
		::Recordings.DelByName(recording->FileName());
#else
	    Recordings.Del(recording);
#endif
	}
	else
	    LogFile.Log(1, "recording already in use by a timer!");
    }
}
