#include <string>
#include <algorithm>
#include "tracks.h"
#include "log.h"
#include "deluxe_config.h"
#include "grabberthread.h"
#include "blacklist.h"

using std::string;

cTracks Tracks;

char *cTrack::buffer = NULL;


int FindIgnoreCase(const string& expr, const string& query)
{
    string exprLower = expr;
    std::transform(exprLower.begin(), exprLower.end(), exprLower.begin(), tolower);
    string queryLower = query;
    std::transform(queryLower.begin(), queryLower.end(), queryLower.begin(), tolower);
    int pos = exprLower.find(queryLower);
    return pos;
}

string ReplaceAll(const string& input, const string& what, const string& with)
{
    string result = input;
    int pos = 0;

    while((pos = FindIgnoreCase(result, what)) >= 0)
	result.replace(pos, what.size(), with);
    return result;
}

// -- cTrack -----------------------------------------------------------------
cTrack::cTrack(void)
{
    eventtitle = eventsubtitle = eventsummary = artist = title = channelID = "";
    year = 0;
    starttime = 0;
    status = 0;
    buffer = NULL;
}

cTrack::cTrack(const cEvent* e)
{
    eventtitle = eventsubtitle = eventsummary = artist = title = channelID = "";
    year = 0;
    starttime = 0;
    buffer = NULL;
    status = 0;

    if (e)
    {
	channelID = *e->ChannelID().ToString();
	eventtitle = e->Title()?e->Title():"";
	eventsubtitle = e->ShortText()?e->ShortText():"";
	eventsummary = e->Description()?e->Description():"";
	starttime = e->StartTime();
	duration = e->Duration();
	CreateTrackInfo();
    }
}

cTrack::~cTrack(void)
{
    if (buffer) {
	free(buffer);
	buffer = NULL;
    }
}

void cTrack::CreateTrackInfo()
{
    set<cChannelSupported>::iterator it = cGrabberThread::GetChannel(channelID);

    if (it->epgType == DELUXE_EPG)
    {
	int sepPos = eventtitle.find(" - ");
	if (sepPos < 0) return;
	artist = string(eventtitle.begin(), eventtitle.begin() + sepPos);
	int yearPos = eventtitle.rfind("(");
	if (yearPos < 0) return;
	title = string(eventtitle.begin() + sepPos + 3, eventtitle.begin() + yearPos - 1);
	string strYear(eventtitle.begin() + yearPos + 1, eventtitle.begin() + yearPos + 5);
	year = atoi(strYear.c_str());
    }
}

bool cTrack::operator== (const cTrack &arg) const
{
    return (starttime == arg.starttime && duration == arg.duration && eventtitle == arg.eventtitle && channelID == arg.channelID);
}

bool cTrack::Parse(const char *s)
{
    char *line;
    char *pos;
    char *pos_next;
    int parameter = 1;
    int valuelen;
    
#define MAXVALUELEN (10 * MaxFileName)
    
    char value[MAXVALUELEN];
    
    channelID = "S19.2E-1-1092-10100"; // default

    pos = line = strdup(s);
    pos_next = pos + strlen(pos);
    if (*pos_next == '\n') *pos_next = 0;
    while (*pos) {
	while (*pos == ' ') pos++;
	if (*pos) {
	    if (*pos != ':') {
		pos_next = strchr(pos, ':');
		if (!pos_next)
		    pos_next = pos + strlen(pos);
		valuelen = pos_next - pos + 1;
		if (valuelen > MAXVALUELEN) 
		{
		    LogFile.eSysLog("entry '%s' is too long. Will be truncated!", pos);  
		    valuelen = MAXVALUELEN;
		}
		strn0cpy(value, pos, valuelen);
		pos = pos_next;
		switch (parameter) {
		    case 1:  artist = value;
			break;
		    case 2:  title = value;
			break;
 		    case 3: year = atoi(value);
			break;
		    case 4:  starttime = atol(value);
			break;
		    case 5:  duration = atoi(value);
			break;
		    case 6:  status = atoi(value);
			break;
		    case 7:  channelID = value;
			break;
		} //switch
	    }
	    parameter++;
	}
	if (*pos) pos++;
    } //while
    
    title = ReplaceAll(title, "|", ":");
    artist = ReplaceAll(artist, "|", ":");
    
    free(line);
    return (parameter >= 5) ? true : false;
}

const char *cTrack::ToText(void) const
{
    free(buffer);
    asprintf(&buffer, "%s:%s:%d:%ld:%d:%d:%s", 
	     ReplaceAll(artist, ":", "|").c_str(),
	     ReplaceAll(title, ":", "|").c_str(),
	     year,
	     starttime,
	     duration,
	     status,
	     channelID.c_str());
    return buffer;
}

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

// -- cTracks -----------------------------------------------------------------
bool cTracks::InList(const cEvent* e)
{
    cTrack trackTemp(e);
    cTrack* track = First();
    while (track) 
    {
	if (*track == trackTemp)
	    return true;
	track = Next(track);
    }	
    return false;
}

bool cTracks::IsTrack(const cEvent* e)
{
    cTrack track(e);
    return (track.year > 0 && track.artist != "" && track.title != "");
}

void cTracks::SetConverted(cRecording* recording)
{
    int offset = DeluxeConfig.offset;
    cIndexFile index(recording->FileName(), false);
    for(cTrack* track = First(); track; track = Next(track))
    {
	int indexIStart = index.GetNextIFrame((track->starttime - recording->start + offset) * FRAMESPERSEC, true);
	int indexIStop = index.GetNextIFrame((track->starttime + track->duration - recording->start + offset) * FRAMESPERSEC, false);
	if (indexIStart < 0 || indexIStop < 0)
	    continue;
	track->status = 2;
    }
    Save();
}

bool cTracks::AlreadyDone(const cEvent* e)
{
    if (DeluxeConfig.avoidRepeats == 0) return false;

    cTrack trackTemp(e);
    cTrack* track = First();
    while (track) 
    {
	if (track->status == 2 && 
	    track->title == trackTemp.title &&
	    track->artist == trackTemp.artist &&
	    track->year == trackTemp.year)
	    return true;
	track = Next(track);
    }	
    return false;
}

bool cTracks::Blacklisted(const cEvent* e)
{
    cTrack track(e);
    return Blacklisted(track);
}

bool cTracks::Blacklisted(const cTrack& track)
{
    return Blacklist.InList(track.artist, track.title);
}
