//$Id: output.cpp,v 1.4 1997/12/01 05:49:48 parallax Exp $
#include "output.h"
#include "output.moc"

#define OFFSET_X 5
#define OFFSET_Y 5
#define LINE_BORDER 2
#define SCROLLBAR_WIDTH 16
#define PIXMAP_BORDER 2
#define LOWER_BORDER 6
#define BORDER_Y 2
#define BORDER_X 2

ObjectString::ObjectString(const char *txt)
        : Object(), QString(txt)
{
    type = STRING;
    font = QFont("Helvetica", 12); // the default font
    color = QColor(black);
    inverse = FALSE;
}

ObjectString::~ObjectString()
{
    
}

int ObjectString::Width()
{
    QFontMetrics fm(font);
    return fm.width(data());
}

int ObjectString::Height()
{
    QFontMetrics fm(font);
    return fm.height();
}
    
QFont &ObjectString::Font()
{
    return font;
}

void ObjectString::SetFont(QFont &fnt)
{
    font = fnt;
}

QColor &ObjectString::Color()
{
    return color;
}

void ObjectString::SetColor(QColor &c)
{
    color = c;
}

void ObjectString::DrawText(QPainter *painter, const char *word, int x, int y)
{
    QPen oldPen = painter->pen();
    QFontMetrics fm(painter->font());
    if (inverse)
    {
        QColor bgColor = painter->backgroundColor();
        painter->fillRect(x, y-Height()+LINE_BORDER, fm.width(word), \
                          Height()+LINE_BORDER, QBrush(QColor(0xffffffff ^  bgColor.pixel())));
        painter->setPen(bgColor);
    }
    painter->drawText(x, y, word);
    painter->setPen(oldPen);
}
            
            
            
        

ObjectPixmap::ObjectPixmap(const char *filename)
        : Object(), QPixmap(filename)
{
    type = PIXMAP;
}

ObjectPixmap::~ObjectPixmap()
{

}

OutputLine::OutputLine()
{
    objects = new QList<Object>();
    objects->setAutoDelete(TRUE);
    height = 0;
    numLines = 1;
}

OutputLine::~OutputLine()
{
//    delete objects;
}

Object *OutputLine::ObjectAt(int x)
{
    int currX = 0;
    int prevX = 0;
    for (unsigned int i=0; i<objects->count(); i++)
    {
        currX += objects->at(i)->Width();
        if (currX <= x && prevX >= x)
            return objects->at(i);
        prevX = currX;
    }
    return NULL;
}
        
void OutputLine::SelectText(int x1, int x2)
{
    int currX = OFFSET_X;
    int prevX = OFFSET_X;
    for (unsigned int i=0; i<objects->count(); i++)
    {
        currX += objects->at(i)->Width();
        if (currX >= x1 && prevX <= x2)
        {
            objects->at(i)->SetSelected(TRUE, x1, x2);
        }
        prevX = currX;
    }
}

void OutputLine::UnselectText()
{
    for (unsigned int i=0; i<objects->count(); i++)
    {
        objects->at(i)->SetSelected(FALSE);
    }
}

void OutputLine::InsertObject(Object *o)
{
    //   debug ("inserting object");
    // check to see if the last object is empty
    Object *lst = objects->last();
    if (lst)
    {
        if (lst->type == Object::STRING)
        {
            if (((ObjectString *)lst)->isEmpty())
            {
                debug("Removing empty text object.");
                objects->remove();
            }
        }
    }
    int tmpH= o->Height() + LINE_BORDER;
    
    if (tmpH > height)
        height = tmpH;    
    objects->append(o);
}


void OutputLine::PaintObjects(QPainter *painter, int maxWidth)
{
//    debug("drawing row");
    int x=0;
    int y = 0;
    numLines = 1;
    for (unsigned int i=0; i<objects->count(); i++)
    {
        DrawObject(i, painter, x, y, maxWidth);      
    }// end for
}

void OutputLine::DrawObject(int i, QPainter *painter, int &currX, int &currY, int maxWidth)
{
    Object *o = objects->at(i);
    if (o->type == Object::STRING)
    {            
        ObjectString *str = (ObjectString *)o;
        QPen pen(str->Color());
        if (painter)
        {
            painter->setPen(pen);
            painter->setFont(str->Font());
        }
        QFontMetrics fm(str->Font());
        QString txt = str->data();
        QString word;
        int space = txt.find(' ');
        while (space != -1)
        {
            word = txt.left(space+1);
            txt.remove(0, space +1);
//                debug("multiple spaces");
            if (currX + fm.width(word) > maxWidth)
            {
//                debug("adding newline1");
                currY += height;
                numLines++;
                currX = OFFSET_X;
            }
            if (painter)
                str->DrawText(painter, word, currX, height +currY-LINE_BORDER/2);
            currX += fm.width(word) + fm.leading();
            space = txt.find(' ');
        }
        
        // the last word of the string...check for the next space available
        word = txt.copy();
        if (currX + fm.width(word) + NextSpace(i+1) > maxWidth)
        {
            //          debug("adding newline2");
            currY += height;
            numLines++;
            currX = OFFSET_X;
        }
        
//            debug("last word: %s", word.data());
        if (painter)
            str->DrawText(painter, word, currX,height + currY-LINE_BORDER/2);
        currX += fm.width(word) + fm.leading();
        
    }// if STRING
    else
        // if (o->type == Object::PIXMAP)
    {
        currX += PIXMAP_BORDER;
        //            debug("drawing pixmap");
        ObjectPixmap *pic = (ObjectPixmap *)o;
        if ((currX + pic->width()) > maxWidth)
        {
            // debug("adding newline3");
            currY += height;
            numLines++;
            currX = OFFSET_X;
        }
        int dx = (height - (pic->height()))/2;
        if (painter)
            painter->drawPixmap(currX, currY + dx, *pic);
        currX += pic->width() + PIXMAP_BORDER;
    } // if PIXMAP
}

int OutputLine::NextSpace(int startingIndex)
{
    int width = 0;
    for (unsigned int i=startingIndex;i<objects->count(); i++)
    {
        Object *o = objects->at(i);
        if (o->type == Object::STRING)
        {
            ObjectString *str = (ObjectString *)o;
            QFontMetrics fm(str->Font());
            int space = str->find(' ');
            if (space == -1)
                width += fm.width(*str) + fm.leading();
            else
            {
                QString txt = str->left(space+1);
                width += fm.width(txt);
                return width;
            }
        }
        if (o->type == Object::PIXMAP)
        {
            width += ((ObjectPixmap *)o)->width();
            return width;
        }
    }
    return width;
}
    

                      
        
OutputWidget::OutputWidget(QWidget *parent=0,const char *name=0)
  : QTableView(parent,name)
{
    lines = new QList<OutputLine>();
    lines->setAutoDelete(TRUE);
    scrollPixmap = true;
    setNumCols(1);
    setNumRows(1);
    setCellWidth(1); // constant width
    setTableFlags( Tbl_vScrollBar );
    const QScrollBar *v = verticalScrollBar();
    connect(v, SIGNAL(sliderMoved(int)), SLOT(Update()));
    setFrameStyle( QFrame::WinPanel | QFrame::Sunken );
    setBackgroundMode( PaletteBase );
    setFocusPolicy( StrongFocus );
    setMouseTracking(TRUE);
    offx = OFFSET_X;
    offy = OFFSET_Y;
    currentFont = QFont("Helvetica", 12);
    currentColor = QColor(black);
    settingsChanged = FALSE;
    picNames = new QList<QString>();
    pics = new QList<ObjectPixmap>();
//    vertScroll = new QScrollBar(0, 0, 12, height(), 0,QScrollBar::Vertical, this, "vert" );
//    connect( vertScroll, SIGNAL(valueChanged(int)), SLOT(ScrollVert(int)) );
    docHeight = 0;
    caching = TRUE;
    updateAll = FALSE;
    isTextSelected = FALSE;
    inverse = FALSE;
    selectPoint = QPoint(-1, -1);
    NewLine();
}

OutputWidget::~OutputWidget()
{

}

Object *OutputWidget::ObjectAt(int x, int y)
{
    int currY = 0;
    int prevY = 0;
    for (unsigned int i=0; i<lines->count(); i++)
    {
        currY += lines->at(i)->ActualHeight();
        if (currY <=y && prevY >= y)
        {
            return (lines->at(i)->ObjectAt(x));
        }
        prevY = currY;
    }
    return NULL;
}
            
        
void OutputWidget::EnableCaching (bool yn)
{
    caching = yn;
}

QFont &OutputWidget::CurrentFont()
{
    return currentFont;
    settingsChanged = FALSE;
}


void OutputWidget::SetCurrentFont(QFont &f)
{
    currentFont = f;
}

QColor &OutputWidget::CurrentColor()
{
    return currentColor;
}

void OutputWidget::SetCurrentColor(QColor &c)
{
    currentColor = c;
    settingsChanged = TRUE;
}

void OutputWidget::NewLine()
{
    OutputLine *lastLine = lines->last();
    OutputLine *newLine = new OutputLine();
    lines->append(newLine);
    setNumRows(lines->count()+1);
    if (lastLine)
    {
        lastLine->PaintObjects(NULL, cellWidth());
        updateTableSize();
        int t = totalHeight();
        int v = viewHeight();
        if (t > v)
            setYOffset(totalHeight()-viewHeight());
//        updateScrollBars();
        if (lastLine->NumLines() > 1)
            updateCell(numRows()-1, 1, false);
        Update();
    }
}

void OutputWidget::UpdateLastRow()
{
//  int h = lines->last()->ActualHeight();
    //   if (offy == OFFSET_Y)
    //  repaint(0,DocHeight()-h,width(), h, TRUE);
//    AdjustScrollBar();
}

void OutputWidget::InsertText(const char *txt)
{
    // if (settingsChanged)
    // {
    //    NewTextObject();
    //    settingsChanged = FALSE;
    // }
    //*textObj += txt;
    ObjectString *str = new ObjectString(txt);
    str->SetFont(currentFont);
    str->SetColor(currentColor);
    str->SetInverse(inverse);
    (lines->last())->InsertObject(str);
}

void OutputWidget::InsertPixmap(const char *filename)
{
    ObjectPixmap *pic;
    if (caching)
    {
        for (unsigned int i=0; i < picNames->count(); i++)
        {
            if (*(picNames->at(i)) == filename)
            {
//                debug("using cached image");
                pic = pics->at(i);
                (lines->last())->InsertObject(pic);
                //              NewTextObject(); // we now need a new text object
                return;
            }
        }
    }
    // didn't find in cache, or caching disabled
    pic =  new ObjectPixmap(filename);
    (lines->last())->InsertObject(pic);
    // add to our cache
    picNames->append(new QString(filename));
    pics->append(pic);
//    NewTextObject(); // we now need a new text object
}

void OutputWidget::SelectText(int x1, int y1, int x2, int y2)
{
    int currY = -offy;
    int prevY = -offy;
    for (unsigned int i=0; i<lines->count(); i++)
    {
        currY += lines->at(i)->ActualHeight();
        if (currY >= y1 && prevY <= y2)
        {
            lines->at(i)->SelectText(x1, x2);
        }
        prevY = currY;
    }
//    repaint(x1, y1, x2, y2);
}       

void OutputWidget::DrawBackground( QPainter *painter,int row )
{
    int x = 0;
    int y;
    if (!rowYPos(row, &y))
    {
        debug("row is not visible.");
        return;
    }
    int w = cellWidth();
    int h = cellHeight(row);
    
    if (bgPixmap.isNull())
    {
        painter->eraseRect( x, y, w, h );
        return;
    }
    
    QPixmap *buffer = new QPixmap(w, h);
    QPainter *p = new QPainter();
    p->begin(buffer);
    
    int pw = bgPixmap.width();
    int ph = bgPixmap.height();
    int ox = xOffset();
    int oy = yOffset();
    int xOrigin = x/pw*pw - ox%pw;
    int yOrigin = y/ph*ph - oy%ph;
    p->setClipRect(0,0,w, h);
    p->setClipping( TRUE);

    for ( int yp = yOrigin; yp < y + h; yp += ph )
    {
        for ( int xp = xOrigin; xp < x + w; xp += pw )
        {
            p->drawPixmap( xp, yp-y, bgPixmap );
        }
    }
    p->end();
    painter->drawPixmap(0,0, *buffer);
    delete buffer;
    delete p;
}

void OutputWidget::NewTextObject()
{
    //   debug("creating new text object in current line");
    ObjectString *str = new ObjectString();
    str->SetFont(currentFont);
    str->SetColor(currentColor);
    str->SetInverse(inverse);
    (lines->last())->InsertObject(str);
    textObj = str;
}

void OutputWidget::paintCell(QPainter *painter, int row, int)
{
    if (scrollPixmap)
        DrawBackground(painter, row);
    if (row != 0)
        lines->at(row-1)->PaintObjects(painter, cellWidth());
}

void OutputWidget::resizeEvent(QResizeEvent *e)
{
    setCellWidth(viewWidth()-BORDER_X);
    for (OutputLine *l=lines->first(); l!= 0; l = lines->next())
    {
        l->PaintObjects(NULL, cellWidth());
    }
    QTableView::resizeEvent(e);
    repaint(true);
    setYOffset(totalHeight()-viewHeight());
//    updateScrollBars();
    // repaint(true);
    /* int num=0;
    for (OutputLine *l=lines->first(); l!= 0; l = lines->next())
    {
        debug("Cell %d: Height: %d NumLines: %d", num, l->Height(), l->NumLines());
        num++;
    }*/
}

int OutputWidget::cellHeight(int row)
{
    if (row == 0)
    {
        if (lines->first())
            return (viewHeight()-lines->first()->Height());
        else
            return viewHeight();
    }
    else
        return (lines->at(row-1)->ActualHeight()+BORDER_Y);
}

int OutputWidget::totalHeight()
{
    int th = cellHeight(0);
    for (OutputLine *l = lines->first(); l!= 0; l = lines->next())
        th += (l->ActualHeight() + BORDER_Y);
    return th;
}

void OutputWidget::Update()
{
    if (scrollPixmap)
        return;
  update();
}
            
        
