#include "tools.h"
#include "recdone.h"
#include "epgsearchcfg.h"
#include "epgsearchcats.h"
#include "searchtimer_thread.h"

#include <vdr/tools.h>

cRecsDone RecsDone;

char *cRecDone::buffer = NULL;

// -- cRecDone -----------------------------------------------------------------

cRecDone::cRecDone()
{
    title = shortText = description = aux = NULL;
    startTime = 0;
    duration = 0;
    if (buffer)
	free(buffer);
    buffer = NULL;
    searchID = -1;
}

cRecDone::cRecDone(cTimer* Timer, const cEvent* Event, cSearchExt* Search)
{
    title = shortText = description = aux = NULL;
    startTime = 0;
    duration = 0;
    searchID = Search?Search->ID:-1;
    buffer = NULL;

    if (Event)
    {
	if (Event->Title())
	    title = strdup(Event->Title());
	if (Event->ShortText())
	    shortText = strdup(Event->ShortText());
	if (Event->Description())
	    description = strdup(Event->Description());
	startTime = Event->StartTime();
	duration = Event->Duration();
	channelID = Timer->Channel()->GetChannelID();
#if VDRVERSNUM && VDRVERSNUM > 10343
	if (Timer && !isempty(Timer->Aux()))
	    aux = strdup(Timer->Aux());
#endif
    }
}

cRecDone::~cRecDone()
{
  if (buffer) {
    free(buffer);
    buffer = NULL;
  }
  if (title) {
    free(title);
    title = NULL;
  }
  if (shortText) {
    free(shortText);
    shortText = NULL;
  }
  if (description) {
    free(description);
    description = NULL;
  }
  if (aux) {
    free(aux);
    aux = NULL;
  }

}

bool cRecDone::Parse(char *s)
{
    char *t = skipspace(s + 1);
    switch (*s) {
	case 'T': title = strdup(t);
	    break;
	case 'S': shortText = strdup(t);
	    break;
	case 'D': strreplace(t, '|', '\n');
	    description = strdup(t);
	    break;
	case '@': strreplace(t, '|', '\n');
	    aux = strdup(t);
	    break;
	default:  esyslog("ERROR: unexpected tag while reading epgsearch done data: %s", s);
	    return false;
    }
  return true;
}

bool cRecDone::Read(FILE *f)
{
    cRecDone *recDone = NULL;
    char *s;
    cReadLine ReadLine;
    while ((s = ReadLine.Read(f)) != NULL) {
	char *t = skipspace(s + 1);
	switch (*s) {
	    case 'R': if (!recDone) {
		time_t StartTime;
		int Duration, SearchID;
		int n = sscanf(t, "%ld %d %d", &StartTime, &Duration, &SearchID);
		if (n == 3) {
		    recDone = new cRecDone;
		    if (recDone)
		    {
			recDone->searchID = SearchID;
			recDone->startTime = StartTime;
			recDone->duration = Duration;
			RecsDone.Add(recDone);
		    }
		}
	    }		
		break;
	    case 'C': 
	    {
		s = skipspace(s + 1);
		char *p = strchr(s, ' ');
		if (p)
		    *p = 0; // strips optional channel name
		if (*s) {
		    tChannelID channelID = tChannelID::FromString(s);
		    if (channelID.Valid()) {
			if (recDone)
			    recDone->channelID = channelID;
                    }
		    else {
			esyslog("ERROR: illegal channel ID: %s", s);
			LogFile.Log(1,"ERROR: illegal channel ID: %s", s);
			return false;
                    }
		}
	    }
	    break;
	    case 'r':
		recDone = NULL;
		break;
	    default:  if (recDone && !recDone->Parse(s))
	    {
		LogFile.Log(1,"ERROR: parsing %s", s);
		return false;
	    }
	}
    }
    return true;
}


const char *cRecDone::ToText(void)
{
    char* tmpDescr = description?strdup(description):NULL;
    if (tmpDescr)
	strreplace(tmpDescr, '\n', '|');

    char* tmpInfo = aux?strdup(aux):NULL;
    if (tmpInfo)
	strreplace(tmpInfo, '\n', '|');

    if (buffer)
	free(buffer);
    buffer = NULL;
    
    cChannel *channel = Channels.GetByChannelID(channelID, true);
    if (!channel)
	esyslog("EPGSearch: invalid channel in recs done!");

    asprintf(&buffer, "R %ld %d %d\nC %s\n%s%s%s%s%s%s%s%s%s%s%s%sr", 
	     startTime, duration, searchID, 
	     channel?CHANNELSTRING(channel):"",
	     title?"T ":"",title?title:"", title?"\n":"",
	     shortText?"S ":"",shortText?shortText:"", shortText?"\n":"",
	     tmpDescr?"D ":"",tmpDescr?tmpDescr:"", tmpDescr?"\n":"",
	     tmpInfo?"@ ":"",tmpInfo?tmpInfo:"", tmpInfo?"\n":"");

    if (tmpDescr)
	free(tmpDescr);
    if (tmpInfo)
	free(tmpInfo);
    return buffer;
}

bool cRecDone::Save(FILE *f)
{
    return fprintf(f, "%s\n", ToText()) > 0;
}

// -- cRecsDone -----------------------------------------------------------------
int cRecsDone::GetCountRecordings(const cEvent* event, cSearchExt* search, cRecDone** first)
{
    return GetCountRecordings(event, search->compareTitle, search->compareSubtitle, search->compareSummary, search->catvaluesAvoidRepeat, first);
}

int cRecsDone::GetCountRecordings(const cEvent* event, bool compareTitle, bool compareSubtitle, bool compareSummary, unsigned long catvaluesAvoidRepeat, cRecDone** first)
{
    if (first)
	*first = NULL;
    if (!event) return 0;
    
    int count = 0;

    const char* eTitle = event->Title();
    const char* eSubtitle = event->ShortText();
    const char* eDescr = event->Description();
    char* raweDescr = GetRawDescription(eDescr);
    cRecDone* firstrecDone = NULL;
    cRecDone* recDone = First();
    while (recDone) 
    {
	const char* rTitle = recDone->title;
	const char* rSubtitle = recDone->shortText;
	const char* rDescr = recDone->description;
	char* rawrDescr = GetRawDescription(rDescr);

	if ((!compareTitle || (!eTitle && !rTitle) || (eTitle && rTitle && strcmp(rTitle, eTitle) == 0)) &&
	    (!compareSubtitle || (!eSubtitle && !rSubtitle) || (eSubtitle && rSubtitle && strcmp(rSubtitle, eSubtitle) == 0)) &&
	    (!compareSummary || DescriptionMatches(raweDescr, rawrDescr)))
	{
	    if (catvaluesAvoidRepeat != 0) // check categories
	    {
		bool bCatMatch = ((rDescr && eDescr) || (!rDescr && !eDescr));
		cSearchExtCat *SearchExtCat = SearchExtCats.First();
		int index = 0;
		while (catvaluesAvoidRepeat > 0 && SearchExtCat && bCatMatch) 	    
		{
		    if (catvaluesAvoidRepeat & (1<<index))
		    {
			char* eCatValue = GetExtEPGValue(eDescr, SearchExtCat->name);
			char* rCatValue = GetExtEPGValue(rDescr, SearchExtCat->name);
			if ((!eCatValue && rCatValue) ||
			    (!rCatValue && eCatValue) ||
			    (eCatValue && rCatValue && strcmp(eCatValue, rCatValue) != 0))
			    bCatMatch = false;
			free(eCatValue);
			free(rCatValue);
		    }
		    SearchExtCat = SearchExtCats.Next(SearchExtCat);
		    index++;
		}
		if (bCatMatch)
		{
		    if (!firstrecDone) firstrecDone = recDone;
		    else
			if (firstrecDone->startTime > recDone->startTime)
			    firstrecDone = recDone;
		    count++;
		}
	    }
	    else
	    {
		if (!firstrecDone) firstrecDone = recDone;
		else
		    if (firstrecDone->startTime > recDone->startTime)
			firstrecDone = recDone;
		count++;
	    }
	}

	recDone = Next(recDone);

	free(rawrDescr);
    }
    free(raweDescr);

    if (first)
	*first = firstrecDone;

    return count;
}

void cRecsDone::RemoveSearchID(int ID)
{
    if (ID == -1)
	return;
    if (Count() == 0)
	Load(AddDirectory(CONFIGDIR, "epgsearchdone.data"));
    cRecDone* recDone = First();
    while (recDone) 
    {
	if (recDone->searchID == ID)
	{
	    recDone->searchID = -1;
	    LogFile.Log(2,"search timer %d removed in recording %s~%s", ID, recDone->title?recDone->title:"unknown title", recDone->shortText?recDone->shortText:"unknown subtitle");
	}
	recDone = Next(recDone);
    }
    Save();
}

bool cRecsDone::Load(const char *FileName)
{
    Clear();
    if (FileName) {
	free(fileName);
	fileName = strdup(FileName);
    }

    if (fileName && access(fileName, F_OK) == 0) {
	isyslog("loading %s", fileName);
	FILE *f = fopen(fileName, "r");
	bool result = false;
	if (f)
	    result = cRecDone::Read(f);
        fclose(f);
	if (result)
	    LogFile.Log(2,"loaded recordings done from %s (count: %d)", fileName, Count());
	else
	    LogFile.Log(1,"error loading recordings done from %s (count: %d)", fileName, Count());
	return result;
    }
    return false;
}

bool cRecsDone::Save(void)
{
    bool result = true;
    cRecDone* l = (cRecDone*)this->First();
    cSafeFile f(fileName);
    if (f.Open()) {
	while (l) {
	    if (!l->title) { // atleast a title should exist
		l = (cRecDone*)l->Next();
		continue;
	    }
	    if (!l->Save(f)) {
                result = false;
                break;
	    }
	    l = (cRecDone*)l->Next();
	}
	if (!f.Close())
	    result = false;
    }
    else
	result = false;
    LogFile.Log(2,"saved recordings done (count: %d)", Count());
    return result;
}
