/**
 * ==========================================================================
 * Copyright (c) 2003-2008  Martin Hauner
 *               2010       http://subcommander.tigris.org
 *
 * This file is part of Subcommander.
 *
 * Subcommander  is free software: you  can redistribute  it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 2 of the License,  or (at your option)
 * any later version.
 *
 * Subcommander  is  distributed  in  the  hope  that  it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS  FOR A  PARTICULAR PURPOSE. See the  GNU General Public License
 * for more details.
 *
 * You should have  received a copy of the  GNU General Public License  along
 * with Subcommander. If not, see <http://www.gnu.org/licenses/>.
 * ==========================================================================
 */

// sc
#include "TextLineNrWidget.h"
#include "TextPositionCalculator.h"
#include "ScrollPositionCalculator.h"
#include "ColorId.h"
#include "sublib/Line.h"
#include "sublib/TextModel.h"
#include "sublib/ColorStorage.h"
#include "sublib/NullTextModel.h"

// sys
#include <stdio.h>
#include <algorithm>
#include <assert.h>

// qt
#include <QtGui/QPaintEvent>
#include <QtGui/QPainter>


static int dashLinePad = 1;
static int borderPad   = 3;

static NullTextModel NullText;



TextLineNrWidget::TextLineNrWidget( QWidget *parent, unsigned long flags, const char *name )
: QWidget( parent, name ), _flags(flags), _ypos(0) 
{
  _lnColumns = 3;

  _lnLeftPad  = borderPad;
  _lnRightPad = borderPad;

  setSizePolicy( QSizePolicy(QSizePolicy::Fixed,QSizePolicy::Expanding) );

  setModel(&NullText);
}


TextLineNrWidget::~TextLineNrWidget()
{
}

void TextLineNrWidget::setModel( TextModel* model )
{
  _model = model;
  sc::Size lines = _model->getLineCnt();

  char buf[16] = "";
  _lnColumns   = sprintf( buf, "%lu", lines );

  updateGeometry();
}

void TextLineNrWidget::setFlags( unsigned long flags )
{
  _flags = flags;
}

void TextLineNrWidget::paintEvent( QPaintEvent *e )
{
  QRect pr = e->rect(); 
  
  // Qt/X11 does crash when pr is null
  // todo: why is pr null?
  if( pr.isNull() )
    return;

  QPainter pp(this);
  pp.setClipping(true);
  pp.setClipRect(pr);
  pp.setPen( ColorStorage::getColor(ColorLineNrFg) );
  pp.setBackgroundColor( ColorStorage::getColor(ColorLineNrBg) );

  QFontMetrics m(font());
  TextPositionCalculator tpc( m, pr, 0, _ypos );
  int topLine = tpc.getTopLine();
  int botLine = tpc.getBottomLine();
  int lnDefWidth = sizeHint().width();

  // calc the first line number we have to draw
  // TODO
  // we could optimize this if we would remember the first visible line
  // and then count from there....
  int clnr   = 0;
  int lineNr = clnr;

  for( int y = -_ypos; y < tpc.getLineY(topLine); y += m.height(), clnr++ )
  {
    Line line = _model->getLine( clnr );
    if( ! line.isEmpty() )
    {
      lineNr++;
    }    
  }

  for( int curLine = topLine; curLine <= botLine; curLine++ )
  {
    // clear line background
    pp.eraseRect( 0, tpc.getLineY(curLine), lnDefWidth, tpc.getFontHeight() );

    Line line = _model->getLine( curLine );
    
    if( ! line.isEmpty() )
    {
      // draw right aligned line number
      char lnBuf[32];
      int  lnLength  = sprintf( lnBuf, "%d", ++lineNr );
      int  lnWidth   = m.width( lnBuf, lnLength );

      int textX = 0;
      if( _flags & Right )
      {
        textX = lnDefWidth-lnWidth-_lnRightPad + 1;
      }
      if( _flags & Left )
      {
        textX = lnDefWidth-lnWidth-_lnRightPad-dashLinePad;
      }

      pp.drawText( textX, tpc.getTextY(curLine), lnBuf, lnLength );
    }
  }

  int dashX = 0;
  if( _flags & Right )
  {
    dashX = 0;
  }
  if( _flags & Left )
  {
    dashX = lnDefWidth - 1;
  }
  // draw dash line

  pp.setPen( ColorStorage::getColor(ColorDashBg) );
  pp.drawLine( dashX, pr.y(), dashX, pr.y()+pr.height() );

  pp.setPen( ColorStorage::getColor(ColorDashFg) );
  for( int d = pr.y()+(_ypos + pr.y())%2; d < pr.y()+pr.height(); d+=2 )
  {
    pp.drawPoint( dashX, d );
  }
}

void TextLineNrWidget::setScrollPosY( int ypos )
{
  ScrollPositionCalculator spc;
  int oy = _ypos;
  int ny = spc.calcPos( oy, ypos, height(), sizeHint().height() );
  _ypos = ny;

  if( oy != _ypos )
  {
    super::scroll( 0, oy - _ypos );
  }
  update();
}

QSize TextLineNrWidget::sizeHint() const
{
  QFontMetrics m(font());
  sc::Size width  = m.width('x') * _lnColumns + _lnLeftPad + _lnRightPad + dashLinePad;
  sc::Size height = m.height()   * _model->getLineCnt();
  return QSize( (int)width, (int)height );
}

