//
// $Id: LineManip.cc 72 2005-07-12 00:46:02Z amackenz $
// 

#include <unistd.h>
#include <ncurses.h>
#include <ctype.h>

#include "LineManip.h"
#include "Util.h"

#ifdef __DEBUG__
extern FILE * ferr;
#endif

LineManip::LineManip()
{
}
LineManip::~LineManip()
{
}

string LineManip::addColor(string str)
{
    regmatch_t matchList[MAXMATCHES];
    
    int matches=MAXMATCHES;
    bool ret=false;
    char line[5000]; 
    char c;
    string rStr="";
    char buf[255];

    if (colorOkay && logOpen )
    {
        start_color();
        // Setup some stock colors.
        init_pair(COLOR_RED,     COLOR_RED,     -1);
        init_pair(COLOR_GREEN,   COLOR_GREEN,   -1);
        init_pair(COLOR_BLUE,    COLOR_BLUE,    -1);
        init_pair(COLOR_YELLOW,  COLOR_YELLOW,  -1);
        init_pair(COLOR_CYAN,    COLOR_CYAN,    -1);
        init_pair(COLOR_MAGENTA, COLOR_MAGENTA, -1);
        init_pair(COLOR_BLACK,   COLOR_BLACK,   -1);
        init_pair(COLOR_WHITE,   COLOR_WHITE,   -1);
    }

    // Local char* of our line...
    strncpy(line,str.c_str(),5000);

    // If we have not opened a log file, return false.

    // Get each character to be displayed...
    for ( register unsigned int j=0; j<str.size() ; j++)
    {
        c = str[j];
        ret=false;

        if (logOpen == true && colorOkay) 
        {
            // What color to make this char???
            // NOTE: ret is used to flag when a color has been determined...
            for ( unsigned int i=0; i<colorExp.size() && !ret; i++)
            {
                // Does this regex exist in this string?
                if (!regexec(&(colorReg[i]),line,matches,matchList,(int)0))
                {
        
                    // Eatch match on the line...
                    // zero'th elt is the entire string...
                    for(int k = 0; matchList[k].rm_so != -1 && k < 20; k++) 
                    {
                        // Does the current char fall within the current regex?
                        if ( matchList[k].rm_so > -1 &&
                            j >= (unsigned)matchList[k].rm_so &&
                            j < (unsigned)matchList[k].rm_eo )
                        {
                            sprintf(buf,"^%d:%d^",colorPairs[i], (matchList[k].rm_eo-j) );
                            rStr+=buf;

                            for (;j < (unsigned)matchList[k].rm_eo ;j++)
                            {
                                c=str[j];
                                if (c=='^') rStr+='\\';
                                rStr+=c;
                            }
                            j--;
                            ret=true;
                            //use_default_colors();
                            sprintf(buf,"^%d:1^",NOCOLOR);
                            rStr+=buf;
                        }
                    }
                }
            } // color loop.
        }

        // No color matches..
        if (ret==false) 
        {
            //wprintw(win,"%c",c);
            if (c=='^') rStr+='\\';
            rStr+=c;
        }
        ret=false;

    } // character loop.

    return rStr;
}

string LineManip::printColor( WINDOW *win, string str, long offset)
{
    int curColor=NOCOLOR;
    int colorLen=1;
    register int curPos=0;
    register unsigned int j=0;
    int RIGHTPOS=COLS+offset;

    init_pair(COLOR_RED,     COLOR_RED,     -1);
    init_pair(COLOR_GREEN,   COLOR_GREEN,   -1);
    init_pair(COLOR_BLUE,    COLOR_BLUE,    -1);
    init_pair(COLOR_YELLOW,  COLOR_YELLOW,  -1);
    init_pair(COLOR_CYAN,    COLOR_CYAN,    -1);
    init_pair(COLOR_MAGENTA, COLOR_MAGENTA, -1);
    init_pair(COLOR_BLACK,   COLOR_BLACK,   -1);
    init_pair(COLOR_WHITE,   COLOR_WHITE,   -1);
            
    use_default_colors();

    // Get each character to be displayed...
    // NOTE: j marks the number of characters into the string we are,
    // including any 'color' codes to be used.
    // 
    // curPos marks the number of 'printable' characters into the string we
    // are.
    //
    // j is used as our current position within the string (including any
    // characters we won't print like ^1:1^).
    while ( j<str.size() && curPos < RIGHTPOS)
    {
        // Default no color, one character.
        curColor=NOCOLOR;
        colorLen=1;

        // Colors will be inserted into the string by addColor.  Each color
        // will have the form ^n:m^ where n is the number of the color
        // pair, and m is the length of the color.  The special value
        // NOCOLOR (99:1) indicates that the default colors should be used.
        // \^ will be used to indicate an actual '^'.
        while (str[j] == '\\' || str[j] == '^')
        {
            if (str[j] == '\\' && str[j+1] == '^')
            {
                // Skip an escaped '^'
                j++;
            }
            else if (str[j] == '^')
            {
                // determine the next color.
                j++;
                curColor=atoi(str.substr(j,str.size()).c_str());
                while (isdigit(str[j]) && j<str.size()) {j++;}

                j++;
                colorLen=atoi(str.substr(j,str.size()).c_str());
                while (isdigit(str[j]) && j<str.size()) { j++; } j++;

            }
            else
            {
                // this should *never* happen, but as I don't want an
                // infinite loop possibility...
                break;
            }
        }

        // Set color
        if (curColor!=NOCOLOR)
            wattron(win,COLOR_PAIR(curColor));
        
        while (colorLen > 0 && j<str.size() && curPos < RIGHTPOS)
        {
            // Only display if we are past the scroll-right offset.
            if (curPos >= offset)
            {
                wprintw(win,"%c",str[j]);
            }
            curPos++;

            // Advance our positions, decrement from the number of
            // colored characters.
            j++;
            colorLen --;
        }

        // Reset our color.
        if (curColor!=NOCOLOR)
            wattroff(win,COLOR_PAIR(curColor));

    } // character loop.

    return string("");
}


// Returns true if we ignore the line.
bool LineManip::ignore(char * fileName,string linel)
{
    regex_t reg;
    regmatch_t *matchList=NULL;
    
    int matches=0;
    bool ret=false;
    char line[5000]; 

    strncpy(line,linel.c_str(),5000);

    // Find the 'filename' without path..
    char * tmpFileName = strrchr(fileName,'/')+1;
    if (!tmpFileName) tmpFileName = fileName;

    char merr[255]; int rval=0;
    
    // If we have not opened a log file, return false.
    if (logOpen == false) return false;

    // If no expressions, return...
    if (ignoreExp.size() == 0 ) return false;
    
    // Will be set to true if we find something...
    ret=false;
    for ( unsigned int i=0; i<ignoreExp.size(); i++)
    {
        rval=regcomp(&reg,ignoreExp[i].c_str(),REG_EXTENDED|REG_ICASE|REG_NOSUB);
    
        if (rval!=0)
        {
            regfree(&reg);
            regerror(rval,&reg,merr,255);
            clean_exit(rval,merr);
        }
    
        if (!regexec(&reg,line,matches,matchList,(int)0))
        {
            ret=true;
            regfree(&reg);
            return true;
        }
        regfree(&reg);
    }

    return ret;
}

string LineManip::findConf()
{
    FILE *test;

    string home = getenv("HOME");
    string tmp;

    tmp=home+"/.logmonrc";
    if ( (test=fopen(tmp.c_str(),"r")) )
    {
        fclose(test);
        return tmp;
    }

    tmp="/etc/logmon.conf";
    if ( (test=fopen(tmp.c_str(),"r")) )
    {
        fclose(test);
        return tmp;
    }

    return string("");
}

// Scan our config file for lines pertaining to our filename.
// i.e. If there are 'ignore' lines for our filename, we add them
//      to an array.
void LineManip::init(char* fileName, string config)
{
    FILE *cf;
    string confFile;
    char buf[1024]; 
    string action,tmpFileName,lines;
    char *ptr; char*newbuf;
    char c; int i;
    bool comment=false;
    bool nonspace=false;
    regex_t reg;
    char merr[255]; int rval=0;
    bool isIgnore=false;
    bool Last=false;

    // Two steps.  Find a trailing '/'.  If we don't find one, use the filename,
    // else, use the string from that position plus one.
    char *baseFileName = strrchr(fileName,'/');
    if ( !baseFileName ) baseFileName=fileName;
    else baseFileName++;

    // If a file name isn't specified, find one.
    if ( config == "" )
        confFile=findConf();
    else
        confFile=config;

    cf=fopen(confFile.c_str(),"r");
    if (!cf)
    {
        logOpen=false;
        return;
    }
    else
    {
        logOpen=true;
    }

    if (has_colors())
    {
        colorOkay=true;

    }
    else 
        colorOkay=false;

    // for each line.
    while (!feof(cf))
    {
        memset(buf,'\0',1024);
        i=0;
        comment=nonspace=false;
        isIgnore=false; 

        // Read each line, inspecting the input...
        while ( ((c=fgetc(cf)) != '\n') && (!feof(cf)) && i <1024)
        {
            if (!isspace(c)) nonspace=true;

            if (nonspace && c=='#') comment=true;

            buf[i]=c;
            i++;
        }
        // Just in case...
        buf[i]='\0';

        if (comment || !nonspace) continue;

        if ( buf[0] )
        {
            newbuf=buf;
            // Get the action.
            ptr=strchr(newbuf,':');
            *ptr='\0'; ptr++;
            action=newbuf;
            newbuf=ptr;

            // Get the filename.
            ptr=strchr(newbuf,':');
            *ptr='\0'; ptr++;
            tmpFileName=newbuf;
            newbuf=ptr;

            if ( !strcmp(tmpFileName.c_str(),baseFileName) )
            {
                Last = false;
                // Parse the regex's. (regexi?  regexs? what's plural?)
                while ( (ptr=strchr(newbuf,',')) )
                {
                    // Null terminate our current string..
                    *ptr='\0';
                   // Add this regex to our list...
                    if (action=="ignore")
                    {
                        ignoreExp.push_back(newbuf);
                        isIgnore=true;
                    }

                    // Advance our string position..
                    newbuf=ptr+1;
                }

                // the last element..
                if (action=="ignore")
                {
                    ignoreExp.push_back(newbuf);
                } 
                else if (!strcasecmp(action.c_str(), "red") && colorOkay)
                {
                    colorPairs.push_back(COLOR_RED);
                    colorExp.push_back(newbuf);
                }
                else if (!strcasecmp(action.c_str(), "green") && colorOkay)
                {
                    colorPairs.push_back(COLOR_GREEN);
                    colorExp.push_back(newbuf);
                }
                else if (!strcasecmp(action.c_str(), "blue") && colorOkay)
                {
                    colorPairs.push_back(COLOR_BLUE);
                    colorExp.push_back(newbuf);
                }
                else if (!strcasecmp(action.c_str(), "yellow") && colorOkay)
                {
                    colorPairs.push_back(COLOR_YELLOW);
                    colorExp.push_back(newbuf);
                }
                else if (!strcasecmp(action.c_str(), "cyan") && colorOkay)
                {
                    colorPairs.push_back(COLOR_CYAN);
                    colorExp.push_back(newbuf);
                }
                else if (!strcasecmp(action.c_str(), "magenta") && colorOkay)
                {
                    colorPairs.push_back(COLOR_MAGENTA);
                    colorExp.push_back(newbuf);
                }
                else if (!strcasecmp(action.c_str(), "black") && colorOkay)
                {
                    colorPairs.push_back(COLOR_BLACK);
                    colorExp.push_back(newbuf);
                }
                else if (!strcasecmp(action.c_str(), "WHITE") && colorOkay)
                {
                    colorPairs.push_back(COLOR_WHITE);
                    colorExp.push_back(newbuf);
                }
                rval=regcomp(&reg,newbuf,REG_EXTENDED|REG_ICASE);
            
                // Something went *terribly* wrong...
                if (rval!=0)
                {
                    regfree(&reg);
                    regerror(rval,&reg,merr,255);
                    clean_exit(rval,merr);
                }
                if (!isIgnore)
                    colorReg.push_back(reg);
 
           }

        }
    }

    fclose(cf);

}
