//
// $Id: Frame.cc 79 2005-07-19 21:04:39Z amackenz $
// 

#include <ncurses.h>
#include <string>
#include <vector>
#include <stdlib.h>
#include <stdio.h>

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

#define BUF_SIZE 2040

using namespace std;

#ifdef __DEBUG__
extern FILE *ferr;
#endif


Frame::Frame()
{
    printf("Frame()\n");
}

Frame::~Frame()
{

    // Close the file.
    if (fin && !executable)
        fclose(fin);
    else if (fin && executable)
        pclose(fin);

    if ( win )
        delwin(win);
}

// This is really the constructor used..
// Filename, Number in queue, Total number of frames, config file name.
Frame::Frame(char* name, long n, long total,string conf)
{

    number=n;
    totalFrames=total;
    strncpy( fileName, name, FILE_NAME_SIZE);
    manip.init(fileName,conf);

    // All frames enabled by default...
    enable(true);

    // Derive other info
    lines=LINES/total; // Total lines for this frame.
    prevOpened=0;
    actualBufferSize=0;
    startLine=(LINES/totalFrames) * number; // Where to move to
    totalFileLines=0;
    scrollBackOffset=0;
    scrollRightOffset=0;
    executable=0; // Default to non-executable..

    fin=NULL;
    win=NULL;

}

void Frame::enable(bool b)
{
    enabled = b;
}

int Frame::resize()
{
    #ifdef __DEBUG__
    fprintf(ferr,"LINES,COLS = %d,%d\n",LINES,COLS);
    #endif
    startLine=(LINES/totalFrames) * number ;
    lines=LINES/totalFrames;

    delwin(win);
    win=newwin(lines,0,startLine,0 );

    return 1;
}


// Take a parameter to determine how far back to read.
// This function will find 'maxLines' back from the end of the file.  After
// the function has finished, our file pointer will be at this position.
long Frame::findPos(long maxLines)
{
    long i=-1;
    long cr_count=0;
    char c;
    maxLines = maxLines?maxLines:lines;

    // Assume file is open.
    if (!fin) clean_exit(1,"Frame::findPos File not open yet!");


    while (    cr_count < maxLines  // defaults to lines.
            && (fseek(fin, i, SEEK_END)==0) 
            && !feof(fin) )
    {

        c=getc(fin);
        if (c == '\n')
            cr_count++;

        i--;
    }
    // Go back to the last char read...
    fseek(fin, i+1, SEEK_END);

    // Remove the lines we counted back, they'll be counted
    // when we read the data into a buffer.
    totalFileLines -= cr_count; 

    return ftell(fin);
}

// This function will count all lines in the file.
long Frame::findTotalFileLines()
{
    long total=0;
    long bytes_read;
    char buf[BUF_SIZE];

    rewind(fin);

      while ((bytes_read = fread (buf, 1, BUF_SIZE, fin)) > 0)
      {
         register char *p = buf;
     
         while ((p = (char*) memchr (p, '\n', (buf + bytes_read) - p)))
         {
             ++p; // Move to space after '\n'
             ++total;
         }
      }

    totalFileLines=total;

    rewind(fin);

    return total;
}

void Frame::setStat(struct stat s)
{
    finStat=s;
}

bool Frame::statChanged(struct stat s)
{

    return false;
}

bool Frame::read()
{
    string Buf;
    bool newlines=false;
    struct stat fileStat;
    char errormsg[255];

    // Only do this if we haven't initialized our buffer.
    if ( !prevOpened )
    {
        newlines=true;

        // If the last character of our filename is '|'
        // we execute the prog..
        if ( !strncmp(fileName, "|",strlen(fileName)) )
        {
            fileName[strlen(fileName)]='\0';
            fin = popen(fileName, "r" );
            executable=1;
        }
        else
        {
            executable=0;
            fin = fopen(fileName, "r" );
            if (!fin) 
            {
                sprintf(errormsg,"Could not open file %s!",fileName);
                clean_exit(1,errormsg);
            }

            // Store the stat of our file.
            fstat(fileno(fin),&fileStat);
            setStat(fileStat);
        }

        if ( !fin ) 
        {
            clean_exit(1, "Frame::read() Error opening file!");
        }

        // Certain things can't be done to non-files...
        if ( !executable )
        {
            findTotalFileLines();

            // Set our starting point at however far back we are to read.
            findPos(bufferSize);
        }

        // Open our new windows.
        win=newwin(lines,0,startLine,0 );
        prevOpened=1;

    }
    else
    {
        stat(fileName,&fileStat);

        if ( fileStat.st_ino != finStat.st_ino)
        {
            fclose(fin);
            fopen(fileName,"r");
            findPos(bufferSize);
            finStat=fileStat;
        }
    }

    // Read the data in.
    // Our file pos should have already been set, read 
    // til EOF.
    char Tmp[BUF_SIZE];
    while ( !feof(fin) )
    {
        Buf = "";

        if ( fgets(Tmp, BUF_SIZE, fin) )
        {
            newlines=true;
            Buf = Tmp;
            // For whatever reason, Buf seems to be empty after
            // this call...
            if ( !manip.ignore(fileName, Buf))
            {
                // Add color codes to buffer.
                Screen.push_back(manip.addColor(string(Tmp)));
                
                totalFileLines++;
            }
        }
    }
    clearerr(fin);
    return newlines;
}

long Frame::setScrollBack(long s)
{
    if ( !enabled ) return s;

    if ( s < maxScrollBack() ) 
        scrollBackOffset = s;
    else
        scrollBackOffset = maxScrollBack();
    return s;
}

long Frame::incrScrollBack(long s)
{
    if ( !enabled ) return scrollBackOffset;

    // Only scroll up if it's not beyond our starting point.
    if ( scrollBackOffset + s < maxScrollBack() )
        scrollBackOffset +=s;
    else
        scrollBackOffset = maxScrollBack();
    return scrollBackOffset;
}

long Frame::maxScrollBack()
{
        return Screen.size() - ( lines-1) ;
}
        
long Frame::decrScrollBack( long s )
{
    if ( !enabled ) return scrollBackOffset;

    // Don't scroll past our starting point.
    // scrollBackOffset min is 0.
    
    if ( (signed)(scrollBackOffset - s) >= 0 ) 
    {
        scrollBackOffset -=s;
    }
    else
    {
        scrollBackOffset=0;
    }

    return scrollBackOffset;
}


long Frame::setScrollRight(long s)
{
    if ( !enabled ) return s;
    scrollRightOffset = s;
    return s;
}
long Frame::incrScrollRight(long s)
{
    if ( !enabled ) return scrollRightOffset;
    scrollRightOffset +=s;
    return scrollRightOffset;
}

long Frame::decrScrollRight(long s )
{
    if ( !enabled ) return scrollRightOffset;
    // Don't scroll past our starting point.
    if ( scrollRightOffset > 0 ) 
        scrollRightOffset -=s;
    return scrollRightOffset;
}

void Frame::print()
{
    long i,j;
    string padding;
    // To hold the number as a string.
    char numberBuf[100];
    char numberBuf2[100];
    char numberBuf3[100];

    string leftData = "";
    string rightData = "";

    // Print the title, in reverse!
    werase(win); // clear the screen.
    wmove (win,0,0);

    // Determine the header
    wattron(win,A_REVERSE);

    leftData += "File: <";
    leftData += fileName;
    leftData += ">";

    if ( enabled )
        leftData += " * ";

    sprintf(numberBuf,"%ld",totalFileLines);
    // How far back are we scrolled.
    // Top has 1 subtracted to lines because we need to account for the title.
    sprintf(numberBuf2,"%ld",totalFileLines - scrollBackOffset - (lines-1));
    sprintf(numberBuf3,"%ld",totalFileLines - scrollBackOffset);
    rightData += "Lines ";
    rightData += numberBuf2;
    rightData += "-";
    rightData += numberBuf3;
    rightData += " of ";
    rightData += numberBuf;
    rightData += "";

    // Pad between the 'left' and 'right' sides of the header.
    for ( i=(leftData.length()+rightData.length()); i<COLS; i++)
        padding += ' ';

    wprintw(win,"%s%s%s",leftData.c_str(), padding.c_str(),rightData.c_str());
    wattroff(win,A_REVERSE);

    // Set our line in buffer to start from...
    if ( Screen.size() < (unsigned)lines )
        i=0;
    else
        i=Screen.size() - lines+1 - scrollBackOffset;

    for ( j=1; j<lines && (unsigned)i < Screen.size(); j++)
    {
        // Move to the next line.
        wmove (win,j,0); 
        
        // Only print a line if we're not beyond it's end..
        if ( (unsigned)scrollRightOffset < Screen[i].length())
        {
            // Print with coloring.
            manip.printColor(win,Screen[i],scrollRightOffset);
        }

        i++;
    }
    wrefresh(win);

}
