// $Id: canvas.cpp,v 1.38 2003/05/19 17:44:07 mlaurent Exp $

#include <kdebug.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <strings.h>
#include <qpainter.h>
#include <qimage.h>
#include <qwmatrix.h>
#include <qclipboard.h>
#include <qimage.h>

#include <klocale.h>
#include <kapplication.h>

#include <math.h>

#include "canvas.h"
#include "tool.h"

//#define CUT_FILL_RGB 0xFFFFFF //white

int Canvas::inst = 0;

Canvas::Canvas(int width, int height, QWidget *parent, const char *name)
  : QWidget(parent, name)
{
  setBackgroundMode(NoBackground);

  m_currentTool = 0;
  m_state = INACTIVE;
  m_zoomed = 0;

  // Create pixmap
  m_pix = new QPixmap(width, height);
  if (!m_pix) {
    kdDebug(4400) << "Canvas::Canvas(): Cannot create pixmap\n" << endl;
    exit(1);
  }

  m_pix->fill(white);

  setZoom(100);

  // Set keyboard focus policy
  setFocusPolicy(QWidget::StrongFocus);
  emit sizeChanged();

  Canvas::inst++;
}

Canvas::Canvas(const QString & filename, QWidget *parent, const char *name)
  : QWidget(parent, name)
{
  setBackgroundMode(NoBackground);

  m_currentTool= 0;
  m_state= INACTIVE;
  m_zoomed= 0;

  // Create pixmap
  m_pix= new QPixmap(filename);
  if (!m_pix)
  {
    kdDebug(4400) << "Canvas::Canvas(): Cannot create pixmap\n" << endl;
    exit(1);
  }

  resize(m_pix->width(), m_pix->height());

  setZoom(100);

  emit sizeChanged();

  // Set keyboard focus policy
  setFocusPolicy(QWidget::StrongFocus);

  Canvas::inst++;
}

Canvas::~Canvas()
{
  delete m_pix;
  delete m_zoomed;
}

bool Canvas::isModified()
{
    return m_modified;
}

void Canvas::clearModified()
{
    m_modified= false;
}

void Canvas::markModified()
{
    m_modified= true;
    kdDebug(4400) << "Canvas: emitting modified()\n" << endl;
    emit modified();
}

void Canvas::setSelection(const QRect &rect)
{
    m_selection= rect.normalize();
    m_haveSelection= true;
}

const QRect &Canvas::selection() const
{
    return m_selection;
}

void Canvas::clearSelection()
{
    m_haveSelection= false;
    m_selection = QRect(0,0,0,0);
}


// -------- CUT / COPY / PASTE ------------

void Canvas::cut()
{
    copy();

    int w  = m_haveSelection ? m_selection.right() - m_selection.left() : m_pix->width();
    int h  = m_haveSelection ? m_selection.bottom() - m_selection.top() : m_pix->height();
    if (w <= 0 || h <= 0)
	return;

    QPixmap p( w, h );
    p.fill(white);

    int x = m_haveSelection ? m_selection.left() : 0;
    int y  = m_haveSelection ? m_selection.top() : 0;
    bitBlt( m_pix, x, y, &p, 0, 0, w, h, CopyROP);

    clearSelection();
    updateZoomed();
}

void Canvas::copy()
{
    int x = m_haveSelection ?  m_selection.left() : 0;
    int y  = m_haveSelection ? m_selection.top() : 0;
    int w  = m_haveSelection ? m_selection.right() - m_selection.left() : m_pix->width();
    int h  = m_haveSelection ? m_selection.bottom() - m_selection.top() : m_pix->height();
    if (w <= 0 || h <= 0)
	return;

    kdDebug(4400) << "copying: (" << x << ", " << y << ") - (" << w << ", " << h << ")";
    QPixmap m_clipboardPix( w, h );
    bitBlt( &m_clipboardPix, 0, 0, m_pix, x, y, w, h, CopyROP);
    kapp->clipboard()->setPixmap( m_clipboardPix );
}

void Canvas::paste( int x, int y )
{
    QClipboard *cb = kapp->clipboard();
    QPixmap clipPix = cb->pixmap();
    if ( !clipPix.isNull() ) {
       bitBlt( m_pix, x, y, &clipPix, 0, 0, clipPix.width(), clipPix.height(), CopyROP );
       updateZoomed();
    }
}

void Canvas::clear()
{
    m_pix->fill(white);

    clearSelection();
    updateZoomed();
}

void Canvas::undo()
{
    KPaintUndoData *data = dynamic_cast<KPaintUndoData*>(m_undoStack.undo());
    if (!data)
	return;

    // TODO: set the pixmap, toolID and zoomID
}

void Canvas::redo()
{
    KPaintUndoData *data = dynamic_cast<KPaintUndoData*>(m_undoStack.redo());
    if (!data)
	return;

    // TODO: set the pixmap, toolID and zoomID
}

// ---------- ZOOM ------------------------

void Canvas::setZoom(int z)
{
  QPainter p;
  int w, h;

  m_zoomFactor = z;
  m_matrix.reset();
  m_matrix.scale((float) z/100.0, (float) z/100.0);

  m_rev_matrix.reset();
  m_rev_matrix.translate(-0.5, -0.5);
  m_rev_matrix.scale(100.0/z, 100.0/z);

  if (m_zoomed != 0)
    delete m_zoomed;

  w = (int) (m_pix->width()* ((float) m_zoomFactor/100.0));
  h = (int) (m_pix->height()*((float) m_zoomFactor/100.0));

  m_zoomed = new QPixmap(w, h);
  m_zoomed->fill(white);

  p.begin(m_zoomed);
  p.setWorldMatrix(m_matrix);
  p.drawPixmap(0,0,*m_pix);
  p.end();

  if ((w != width()) || (h != height())) {
    resize(w,h);
    emit sizeChanged();
  }
  repaint(0);
}

void Canvas::updateZoomed()
{
  QPainter p;
/*  int w, h; */

  m_zoomed->fill(white);

  p.begin(m_zoomed);
  p.setWorldMatrix(m_matrix);
  p.drawPixmap(0,0,*m_pix);
  p.end();

  repaint( false );
  markModified();
}

/*
    The offscreen clipboard pixmap must also be adjusted to
    zoom factor for pasting to a zoomed canvas pixmap
*/

void Canvas::updateZoomedClipboard()
{
  if (!m_clipboardPix.isNull())
  {
    QPainter p;

    int clipWid = m_clipboardPix.width();
    int clipHgt = m_clipboardPix.height();

    int newClipWid = (int) (clipWid * ((float) m_zoomFactor/100));
    int newClipHgt = (int) (clipHgt * ((float) m_zoomFactor/100));

    /* make copy of clipboard data to temporary pixmap */
    QPixmap *zc = new QPixmap(clipWid, clipHgt);
    zc->fill(white);
    p.begin(zc);
    p.drawPixmap(0,0, m_clipboardPix);
    p.end();

    /* adjust clipboard size to zoom factor */
    m_clipboardPix.resize(QSize(newClipWid, newClipHgt));
    m_clipboardPix.fill(white);

    /* copy to adjusted clipboard using matrix */
    p.begin(&m_clipboardPix);
    p.setWorldMatrix(m_matrix);
    p.drawPixmap(0,0,*zc);
    p.end();

    /* clean up */
    delete zc;
  }
}


int Canvas::zoom()
{
  return m_zoomFactor;
}

void Canvas::activate(Tool *t)
{
  assert(!isActive());
  m_currentTool = t;
  m_state = ACTIVE;
}

void Canvas::deactivate()
{
  assert(isActive());
  m_state = INACTIVE;
  m_currentTool = 0;
}

QPixmap *Canvas::pixmap() const
{
  return m_pix;
}

QPixmap *Canvas::zoomedPixmap() const
{
  return m_zoomed;
}

void Canvas::setPixmap(QPixmap *px)
{
    QPainter p;
    int w, h;

    *m_pix= *px;
    emit pixmapChanged(m_pix);

    delete m_zoomed;

    w = (int) (px->width()* ((float) m_zoomFactor/100));
    h = (int) (px->height()*((float) m_zoomFactor/100));

    m_zoomed = new QPixmap(w, h);

    p.begin(m_zoomed);
    p.setWorldMatrix(m_matrix);
    p.drawPixmap(0,0,*m_pix);
    p.end();

    if ((w != width()) || (h != height()))
    {
        resize(w,h);
        emit sizeChanged();
    }
    repaint(0);
}

void Canvas::setDepth(int d)
{
  QImage i;
  QPixmap *px;

  assert((d == 1) || (d == 8) || (d == 32));

  if (d != m_pix->depth()) {
    i= m_pix->convertToImage().convertDepth( d );
    px= new QPixmap(m_pix->width(), m_pix->height(), d);
    *px= i;
    setPixmap(px);
    updateZoomed();
	emit imageChanged();
    delete px;
  }
}

void Canvas::mirrorImage()
{
  QImage img = m_pix->convertToImage();
  delete m_pix;
  m_pix = new QPixmap(img.mirror());

  setZoom(zoom());
  emit pixmapChanged(m_pix);
  markModified();

  repaint(0);
}

void Canvas::rotateImage(int degrees)
{
  QWMatrix matrix;
  QPainter p;

  QPixmap *newpix = 0;

  matrix.rotate(degrees);

  QImage tmp = m_pix->convertToImage();

  newpix = new QPixmap(tmp.xForm(matrix));
  delete m_pix;
  m_pix = newpix;

  setZoom(zoom());
  emit pixmapChanged(m_pix);
  markModified();

  repaint(0);
}

void Canvas::resizeImage(int w, int h)
{
  QWMatrix matrix;
  QPainter p;

  if ((w != m_pix->width()) || (h != m_pix->height())) {
    QPixmap *newpix= new QPixmap(w, h);
    matrix.scale((float) w/m_pix->width(), (float) h/m_pix->height());
    p.begin(newpix);
    p.setWorldMatrix(matrix);
    p.drawPixmap(0,0,*m_pix);
    p.end();

    delete m_pix;
    m_pix = newpix;

    setZoom(zoom());
    emit pixmapChanged(m_pix);
    updateZoomed();
  }
  repaint(0);
}

int Canvas::imageWidth() const
{
  return m_pix->width();
}

int Canvas::imageHeight() const
{
  return m_pix->height();
}

/*
    paint Event is the ONLY place where the canvas is
    updated, normally, by copying the offscreen zoomed pixmap
    to the canvas
*/

void Canvas::paintEvent(QPaintEvent *)
{
  bitBlt(this, 0, 0, m_zoomed);
}

void Canvas::mousePressEvent(QMouseEvent *e)
{
    // give Manager the posibillity to update tool settings
    emit startDrawing();

    kdDebug(4400) << "Canvas::mousePressEvent() redirector called" << endl;

    if (isActive())
	m_currentTool->mousePressEvent(e);
}

void Canvas::mouseMoveEvent(QMouseEvent *e)
{
  if (isActive())
    m_currentTool->mouseMoveEvent(e);
}

void Canvas::mouseReleaseEvent(QMouseEvent *e)
{
  kdDebug(4400) << "Canvas::mouseReleaseEvent() redirector called\n" << endl;
  if ( isActive() )
    m_currentTool->mouseReleaseEvent(e);
}

bool Canvas::isActive()
{
  if (m_state == ACTIVE)
    return true;
  else
    return false;
}

bool Canvas::load(const QString & filename, const char *format)
{
  bool s;
  QPixmap p;

  s = p.load(filename, format);

  if (s)
  {
    QPixmap q( p.size(), p.depth() ); // Fix UMR when reading transparent pixels (they hold junk)
    q.fill(white);
    bitBlt(&q, 0, 0, &p);
    setPixmap(&q);
  }

  repaint(0);

  return s;
}

bool Canvas::save(const QString & filename, const char *format)
{
	bool s;
	QString fm(format);

	kdDebug(4400) << "Canvas::save() file= " << filename << ", format= " << format << "\n" << endl;

	if (fm.isEmpty())	// no format given, use default
		s = m_pix->save(filename, "PNG");
	else
		s = m_pix->save(filename, format);

	kdDebug(4400) << "Canvas::save() returning " << s << "\n" << endl;

	return s;
}

void Canvas::keyPressEvent(QKeyEvent *e)
{
  //kdDebug(4400) << "Canvas::keyPressEvent() redirector called\n" << endl;
  if (isActive())
    m_currentTool->keyPressEvent(e);
}


void Canvas::keyReleaseEvent(QKeyEvent *e)
{
  //kdDebug(4400) << "Canvas::keyReleaseEvent() redirector called\n" << endl;
  if (isActive())
    m_currentTool->keyReleaseEvent(e);
}

const QWMatrix &Canvas::zoomMatrix() const
{
   return m_rev_matrix;
}

#include "canvas.moc"
