#include <string>
#include <algorithm>
#include <regex.h>
#include "blacklist.h"
#include "log.h"

using std::string;
string ReplaceAll(const string& input, const string& what, const string& with);

bool CompareNoCase(const string& S1, const string& S2)
{
    string s1 = S1;
    std::transform(s1.begin(), s1.end(), s1.begin(), tolower);
    string s2 = S2;
    std::transform(s2.begin(), s2.end(), s2.begin(), tolower);
    return s1 == s2;
}

cBlacklist Blacklist;

// -- cBlacklistEntry -----------------------------------------------------------------
cBlacklistEntry::cBlacklistEntry()
{
    artist = title = "";
}

cBlacklistEntry::cBlacklistEntry(const string& Artist, const string& Title)
{
    artist = Artist;
    title = Title;
}

bool cBlacklistEntry::operator== (const cBlacklistEntry &arg) const
{
    return (artist == arg.artist && title == arg.title);
}

const string cBlacklistEntry::Strip(const string& input) const
{
    string res = input;
    res = ReplaceAll(res, " ", "");
    res = ReplaceAll(res, "'", "");
    res = ReplaceAll(res, "-", "");
    res = ReplaceAll(res, "´", "");
    res = ReplaceAll(res, "`", "");
    res = ReplaceAll(res, ":", "");
    return res;
}

bool cBlacklistEntry::Matches(const cBlacklistEntry &arg) const
{
    string artist1 = Strip(artist);
    string title1 = Strip(title);
    string artist2 = Strip(arg.artist);
    string title2 = Strip(arg.title);

    bool artistmatch = CompareNoCase(Strip(artist), Strip(arg.artist));
    bool titlematch = CompareNoCase(Strip(title), Strip(arg.title));
   
    // check for regexp match in artist
    regex_t re_artist;

    if (!artistmatch && 0 == regcomp(&re_artist, artist.c_str(), REG_EXTENDED) )
    {
	int status = regexec( &re_artist, arg.artist.c_str(), 0, NULL, 0);
	regfree(&re_artist);
	artistmatch = (status == 0);
    }
    LogFile.Log(3, "artist match '%s' against '%s': %s", arg.artist.c_str(), artist.c_str(), artistmatch?"yes":"no");
    if (!artistmatch) return false;

    // check for regexp match in title
    regex_t re_title;

    if (!titlematch && 0 == regcomp(&re_title, title.c_str(), REG_EXTENDED) )
    {
	int status = regexec( &re_title, arg.title.c_str(), 0, NULL, 0);
	regfree(&re_title);
	titlematch = (status == 0);
    }
    LogFile.Log(3, "title match '%s' against '%s': %s", arg.title.c_str(), title.c_str(), titlematch?"yes":"no");
    return titlematch;
}

bool cBlacklistEntry::Parse(const char *s)
{
    char *line;
    char *pos;
    char *pos_next;
    int parameter = 1;
    int valuelen;
    
#define MAXVALUELEN (10 * MaxFileName)
    
    char value[MAXVALUELEN];
    
    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;
		} //switch
	    }
	    parameter++;
	}
	if (*pos) pos++;
    } //while
    
    free(line);
    return (parameter >= 2) ? true : false;
}


// -- cBlacklist -----------------------------------------------------------------
bool cBlacklist::InList(const string& Artist, const string& Title)
{
    cBlacklistEntry tmpEntry(Artist, Title); 
    for(cBlacklistEntry* entry = First(); entry; entry = Next(entry))
	if (entry->Matches(tmpEntry)) return true;
    return false;
}

