/****************************************************************************
* MeshLab                                                           o o     *
* An extendible mesh processor                                    o     o   *
*                                                                _   O  _   *
* Copyright(C) 2005, 2009                                          \/)\/    *
* Visual Computing Lab                                            /\/|      *
* ISTI - Italian National Research Council                           |      *
*                                                                    \      *
* All rights reserved.                                                      *
*                                                                           *
* This program 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.                                       *
*                                                                           *
* This program 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 (http://www.gnu.org/licenses/gpl.txt)          *
* for more details.                                                         *
*                                                                           *
****************************************************************************/

#include "maskImageWidget.h"
#include "maskRenderWidget.h"
#include "fillImage.h"
#include <QPen>
#include <QBrush>
#include <QPolygon>
#include <QPixmap>
#include <QImage>
#include <QPainter>
#include <QPalette>
#include <QMouseEvent>
#include <QPaintEvent>
#include <QApplication>
#include <QMessageBox>
#include <QAction>
#include <QVBoxLayout>
#include <QToolBar>
#include <QSpinBox>
#include <QFileDialog>
#include <QDesktopWidget>

#include <stack>
#include <cmath>
#include <sstream>
#include <cassert>
#include <iostream>

#ifdef WIN32
#undef min
#undef max
#endif


namespace ui
{

  struct maskImageWidget::Impl
  {
		enum DrawMode { Pen, Eraser } mode_;

		maskRenderWidget *render_area_;
		int threshold_gradient_, threshold_fixed_;
		int realwidth_, realheight_;
    Impl();
  };


  maskImageWidget::Impl::Impl()
  {
		mode_ = Pen;
		threshold_gradient_ = 100;
		threshold_fixed_ = 30;
  }


  maskImageWidget::maskImageWidget(const QImage& image, QWidget *parent) : QDialog(parent), pimpl_(new Impl)
  {
		init(image);
  }


  maskImageWidget::~maskImageWidget() throw()
  {
		delete pimpl_;
  }

	QImage maskImageWidget::getMask() const
	{
		return pimpl_->render_area_->getMask(pimpl_->realwidth_, pimpl_->realheight_);
	}

	void maskImageWidget::loadMask(const QString& filename)
	{
		pimpl_->render_area_->load(filename);
	}

	void maskImageWidget::init(const QImage& image)
	{
		setWindowTitle(tr("Mask Editor"));

		QPixmap load("coral_open32x32.png");
		QPixmap save("coral_save32x32.png");
		QPixmap undo("coral_undo32x32.png");
		QPixmap redo("coral_redo32x32.png");
		QPixmap pen("coral_pencil32x32.png");
		QPixmap eraser("coral_eraser32x32.png");			

		QAction *canvasloadmask = new QAction(this);
		canvasloadmask->setIcon(load);
		canvasloadmask->setText(tr("&Load Mask"));
		QAction *canvassavemask = new QAction(this);
		canvassavemask->setIcon(QIcon(save));
		canvassavemask->setText(tr("&Save Mask"));
		QAction *canvasundo = new QAction(this);
		canvasundo->setIcon(QIcon(undo));
		canvasundo->setText(tr("&Undo"));
		canvasundo->setShortcut(QKeySequence("Ctrl+Z"));
		QAction *canvasredo = new QAction(this);
		canvasredo->setIcon(QIcon(redo));
		canvasredo->setText(tr("&Redo"));
		canvasredo->setShortcut(QKeySequence("Ctrl+Shift+Z"));
		QAction *canvasclear = new QAction(tr("&Clear"), this);
		canvasclear->setShortcut(QKeySequence("Ctrl+C"));

		QAction *canvaspen = new QAction(this);
		canvaspen->setIcon(QIcon(pen));
		canvaspen->setText(tr("&Pen"));
		QAction *canvaseraser = new QAction(this);
		canvaseraser->setIcon(QIcon(eraser));
		canvaseraser->setText(tr("&Eraser"));

		QActionGroup *actions(new QActionGroup(this));
		actions->addAction(canvaspen);
		actions->addAction(canvaseraser);
		canvaspen->setCheckable(true);
		canvaseraser->setCheckable(true);
		canvaspen->setChecked(true);
		actions->setExclusive(true);

		QAction *canvasOK = new QAction(this);
		canvasOK->setText("OK");
		QAction *canvasCancel = new QAction(this);
		canvasCancel->setText("Cancel");

		QBoxLayout *layout(new QVBoxLayout(this));

		// We don't want a real-size image. We will downscale it!
		QImage image_to_use = image;
		pimpl_->realwidth_ = image.width();
		pimpl_->realheight_ = image.height();
		qDebug("maskImageWidget::Init real wxh %i x%i",pimpl_->realwidth_,pimpl_->realheight_);
		QDesktopWidget *desktop(QApplication::desktop());
		if (image.width() > (desktop->width() * .8) ||
				image.height() > (desktop->height() * .8))
		{
			int width(desktop->width()), height(desktop->height());
			image_to_use = image.scaled((int)std::floor(width * .75),
																	(int)std::floor(height * .75), Qt::KeepAspectRatio);
		}
		pimpl_->render_area_ = new maskRenderWidget(image_to_use, this);

		QToolBar *canvas_toolbar(new QToolBar(this));
		canvas_toolbar->addSeparator();
		canvas_toolbar->addAction(canvasloadmask);
		canvas_toolbar->addAction(canvassavemask);
		canvas_toolbar->addSeparator();
		
		canvas_toolbar->addAction(canvasundo);
		canvas_toolbar->addAction(canvasredo);
		canvas_toolbar->addSeparator();
		
		QSpinBox *pen_width(new QSpinBox(canvas_toolbar));
		pen_width->setToolTip(tr("Pen Width"));
		pen_width->setRange(0, 80);
		pen_width->setSingleStep(2);
		pen_width->setValue(16);
		connect(pen_width, SIGNAL(valueChanged(int)), SLOT(setCanvasPenWidth(int)));
		canvas_toolbar->addWidget(pen_width);
		canvas_toolbar->addAction(canvaspen);
		canvas_toolbar->addAction(canvaseraser);
		canvas_toolbar->addSeparator();

		QSpinBox *gradient(new QSpinBox(canvas_toolbar));
		gradient->setToolTip("Gradient Threshold");
		gradient->setRange(0, 255);
		gradient->setValue(pimpl_->threshold_gradient_);
		connect(gradient, SIGNAL(valueChanged(int)), SLOT(setGradientThreshold(int)));
		
		QSpinBox *fixed(new QSpinBox(canvas_toolbar));
		fixed->setToolTip("Fixed Threshold");
		fixed->setRange(0, 255);
		fixed->setValue(pimpl_->threshold_fixed_);
		connect(fixed, SIGNAL(valueChanged(int)), SLOT(setFixedThreshold(int)));
		
		canvas_toolbar->addWidget(gradient);
		canvas_toolbar->addWidget(fixed);
		canvas_toolbar->addSeparator();

		canvas_toolbar->addAction(canvasOK);
		canvas_toolbar->addAction(canvasCancel);

		layout->addWidget(canvas_toolbar);
		layout->addWidget(pimpl_->render_area_);
		layout->setSizeConstraint(QLayout::SetFixedSize);

		connect(canvasloadmask, SIGNAL(activated()), SLOT(loadMask()));
		connect(canvassavemask, SIGNAL(activated()), SLOT(saveMask()));
		connect(canvasundo, SIGNAL(activated()), pimpl_->render_area_, SLOT(undo()));
		connect(canvasredo, SIGNAL(activated()), pimpl_->render_area_, SLOT(redo()));
		connect(canvasclear, SIGNAL(activated()), pimpl_->render_area_, SLOT(clear()));
		connect(canvaspen, SIGNAL(activated()), SLOT(setCanvasPen()));
		connect(canvaseraser, SIGNAL(activated()), SLOT(setCanvasEraser()));

		connect(pimpl_->render_area_, SIGNAL(pointSelected(const QPoint &)), SLOT(automaticMask(const QPoint &)));

		connect(canvasOK, SIGNAL(activated()), SLOT(accept()));
		connect(canvasCancel, SIGNAL(activated()), SLOT(reject()));
	}

	void maskImageWidget::setCanvasPenWidth(int width)
	{
		QPen pen(pimpl_->render_area_->pen());
		pen.setWidth(width);
		pimpl_->render_area_->setPen(pen);
	}


	void maskImageWidget::setCanvasPen()
	{
		QPen pen(pimpl_->render_area_->pen());
		pen.setColor(QColor(Qt::black));
		pen.setJoinStyle(Qt::RoundJoin);
		pimpl_->render_area_->setPen(pen);
	}


	void maskImageWidget::setCanvasEraser()
	{
		QPen pen(pimpl_->render_area_->pen());
		pen.setColor(QColor(Qt::transparent));
		pen.setJoinStyle(Qt::RoundJoin);
		pimpl_->render_area_->setPen(pen);
	}


	void maskImageWidget::setGradientThreshold(int threshold_gradient)
	{
		pimpl_->threshold_gradient_ = threshold_gradient;
	}


	void maskImageWidget::setFixedThreshold(int threshold_fixed)
	{
		pimpl_->threshold_fixed_ = threshold_fixed;
	}

	void maskImageWidget::loadMask()
	{
		try
		{
			QString filename(QFileDialog::getOpenFileName(this, QString("Open mask file"), QString(), QString("*.png")));
			if (QString::null != filename)
				pimpl_->render_area_->load(filename);
		}
		catch (std::exception &e)
		{
			QMessageBox::warning(this, tr("Problem"), e.what());
		}
	}

	namespace
	{
		bool check_extension(QString &filename, const QString &ext)
		{
			bool ret(false);
			if (ext != filename.section('.', -1))
			{
				int index(filename.lastIndexOf('.'));
				if (-1 == index)
				{
					filename += '.';
					index += filename.size();
				}
				filename.replace(index + 1, ext.size(), ext);
				filename.resize(index + 1 + ext.size());
				ret = true;
			}
			return ret;
		}
	};

	void maskImageWidget::saveMask()
	{
		try
		{
			QString filename(QFileDialog::getSaveFileName(this, QString("Save mask file"), QString(), QString("*.png")));
			if (QString::null != filename)
			{
				check_extension(filename, QString("png"));
				pimpl_->render_area_->save(filename, pimpl_->realwidth_, pimpl_->realheight_);
			}
		}
		catch (std::exception &e)
		{
			QMessageBox::warning(this, tr("Epoch 3D Webservice"), e.what());
		}
	}


	void maskImageWidget::automaticMask(const QPoint &p)
	{
		QImage image = (pimpl_->render_area_->palette().base().texture()).toImage();
		QImage out;
		fillImage fi;
		fi.Compute(image, p.x(), p.y(), pimpl_->threshold_gradient_, pimpl_->threshold_fixed_, out);
		
		const size_t width(image.width()), height(image.height());
		QImage temp(pimpl_->render_area_->alphaMask());
		for (size_t i = 0; i < width; ++i)
		{
			for (size_t j = 0; j < height; ++j)
			{
				if (out.pixelIndex(i, j) > 0)
					temp.setPixel(i, j, QColor(Qt::black).rgba());
			}
		}
		//temp.save("temp.jpg","jpg");
		pimpl_->render_area_->setAlphaMask(temp);
	}
};
