/****************************************************************************
* 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 <common/interfaces.h>
#include <meshlab/glarea.h>
#include "edittexture.h"
#include "renderarea.h"
#include <wrap/gl/pick.h>
#include <wrap/gl/picking.h>

#include <vcg/complex/algorithms/clean.h>
#include <QDockWidget>

using namespace vcg;

EditTexturePlugin::EditTexturePlugin()
{
	isDragging = false;
	widget = 0;
	qFont.setFamily("Helvetica");
	qFont.setPixelSize(14);
}

EditTexturePlugin::~EditTexturePlugin()
{
	// Delete the tool widget
	if (widget != 0)
	{
		delete widget;
		widget = 0;
	}
}

const QString EditTexturePlugin::Info()
{
	return tr("Edit texture coordinates of the selected area");
}

void EditTexturePlugin::mousePressEvent(QMouseEvent * event, MeshModel &m, GLArea * gla)
{
	isDragging = true;

    if(event->modifiers() == Qt::ControlModifier) selMode = SMAdd;
    else if(event->modifiers() == Qt::ShiftModifier) selMode = SMSub;
    else selMode = SMClear;
    // Change the appearance of the cursor
    switch(selMode)
    {
        case SMAdd:	// CTRL + Mouse
            gla->setCursor(QCursor(QPixmap(":/images/sel_rect_plus.png"),1,1));
            break;
        case SMSub: // SHIFT + Mouse
            gla->setCursor(QCursor(QPixmap(":/images/sel_rect_minus.png"),1,1));
            break;
        case SMClear: // Mouse
            gla->setCursor(QCursor(QPixmap(":/images/sel_rect.png"),1,1));
            for (unsigned i = 0; i < FaceSel.size(); i++) FaceSel[i]->ClearS();
            FaceSel.clear();
            break;
    }
    if(event->modifiers() == Qt::ControlModifier || event->modifiers() == Qt::ShiftModifier )
    {
        CMeshO::FaceIterator fi;
        for(fi = m.cm.face.begin(); fi != m.cm.face.end(); ++fi)
            if(!(*fi).IsD() && (*fi).IsS()) FaceSel.push_back(&*fi);
    }

    start = event->pos();
    cur = start;
    gla->update();
    return;
}

void EditTexturePlugin::mouseMoveEvent(QMouseEvent * event, MeshModel &/*m*/, GLArea * gla)
{
    prev = cur;
    cur = event->pos();
    /*int curT;
    static int lastRendering;
    if (isDragging)
    {
        // The user is selecting an area: management of the update
        lastRendering = clock();
        curT = clock();
        if(gla->lastRenderingTime() < 50 || (curT - lastRendering) > 1000 )
        {
            lastRendering = curT;
            gla->update();
        }
        else
        {
            gla->makeCurrent();
            glDrawBuffer(GL_FRONT);
            DrawXORRect(gla);
            glDrawBuffer(GL_BACK);
            glFlush();
        }
    }*/
    if(gla->lastRenderingTime() < 200 )
    {
        gla->update();
    }
    else
    {
        gla->makeCurrent();
        glDrawBuffer(GL_FRONT);
        DrawXORRect(gla);
        glDrawBuffer(GL_BACK);
        glFlush();
    }
}

void EditTexturePlugin::mouseReleaseEvent(QMouseEvent * event, MeshModel &/*m*/, GLArea * gla)
{
    prev = cur;
    cur = event->pos();
    gla->setCursor(QCursor(QPixmap(":/images/sel_rect.png"),1,1));
    if (isDragging)
    {
        widget->SelectFromModel();
        isDragging = false;
    }
    gla->update();
}

void EditTexturePlugin::Decorate(MeshModel &m, GLArea *gla)
{
	if (isDragging)
	{
		QPoint mid, wid;
		vector<CMeshO::FacePointer> NewFaceSel;
		vector<CMeshO::FacePointer>::iterator fpi;
		CMeshO::FaceIterator fi;

		DrawXORRect(gla);
		mid = (start + cur)/2;
		mid.setY(gla->height() - mid.y());
		wid = (start - cur);
		if(wid.x()<0) wid.setX(-wid.x());
		if(wid.y()<0) wid.setY(-wid.y());

		for(fi = m.cm.face.begin(); fi != m.cm.face.end(); ++fi)
			if(!(*fi).IsD()) (*fi).ClearS();

		glPushMatrix();
		glMultMatrix(m.cm.Tr);
		GLPickTri<CMeshO>::PickFace(mid.x(), mid.y(), m.cm, NewFaceSel, wid.x(), wid.y());
		glPopMatrix();

		switch(selMode)
		{
			case SMSub:
				for(fpi = FaceSel.begin(); fpi != FaceSel.end(); ++fpi)
					(*fpi)->SetS();
				for(fpi = NewFaceSel.begin(); fpi != NewFaceSel.end(); ++fpi)
					(*fpi)->ClearS();
				break;
			case SMAdd:
				for(fpi = FaceSel.begin(); fpi != FaceSel.end(); ++fpi)
					(*fpi)->SetS();
			case SMClear:
				for(fpi = NewFaceSel.begin(); fpi != NewFaceSel.end(); ++fpi)
					(*fpi)->SetS();
				break;
		}
	}
}

bool EditTexturePlugin::StartEdit(MeshModel &m, GLArea *gla )
{
	// Set up the model
	//m.cm.face.EnableFFAdjacency();
  m.updateDataMask(MeshModel::MM_FACEFACETOPO);

	if (m.cm.textures.size() == 0)
	{
		QMessageBox::warning(gla->window(), "Texture Parametrization Tool",
			"Sorry, this mesh has no texture.",
			QMessageBox::Ok, QMessageBox::Ok);
		return false;
	}

	if (vcg::tri::HasPerWedgeTexCoord(m.cm))
	{
		vcg::tri::UpdateTopology<CMeshO>::FaceFaceFromTexCoord(m.cm);
		if(vcg::tri::Clean<CMeshO>::HasConsistentPerWedgeTexCoord(m.cm) && !HasCollapsedTextCoords(m)) degenerate = false;
		else
		{
			this->Log(GLLogStream::WARNING,"This mesh has a degenerated texture parametrization!");
			degenerate = true;
		}
	}

	FaceSel.clear();
	CMeshO::FaceIterator ff;
	for(ff = m.cm.face.begin(); ff != m.cm.face.end(); ++ff)
		if(!(*ff).IsD() && (*ff).IsS()) FaceSel.push_back(&*ff);

	CMeshO::FaceIterator fi;
	for(fi = m.cm.face.begin(); fi != m.cm.face.end(); ++fi) (*fi).ClearS();

	gla->setCursor(QCursor(QPixmap(":/images/sel_rect.png"),1,1));

	connect(this, SIGNAL(setSelectionRendering(bool)),gla,SLOT(setSelectionRendering(bool)));
	setSelectionRendering(true);

	// Create an instance of the interface
	if (widget == 0)
	{
		widget = new TextureEditor(gla->window(), &m, gla, degenerate);
		dock = new QDockWidget(gla->window());
		dock->setAllowedAreas(Qt::NoDockWidgetArea);
		dock->setWidget(widget);
		QPoint p = gla->window()->mapToGlobal(QPoint(0,0));
		dock->setGeometry(-5+p.x()+gla->window()->width()-widget->width(),p.y(),widget->width(),widget->height());
		dock->setFloating(true);
	}
	dock->setVisible(true);
	dock->layout()->update();

	// Initialize the texture using the intere model
	InitTexture(m);

	gla->update();
	return true;
}

void EditTexturePlugin::EndEdit(MeshModel &m , GLArea * )
{
	// Delete the widget
	for (unsigned i = 0; i < m.cm.face.size(); i++) m.cm.face[i].ClearS();
	if (widget != 0)
	{
		delete widget;
		delete dock;
		widget = 0;
		dock = 0;
	 }
}

void EditTexturePlugin::DrawXORRect(GLArea *gla)
{
    //	Draw the rectangle of the selection area
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    glOrtho(0, gla->width(), gla->height(),0,-1,1);
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();
    glPushAttrib(GL_ENABLE_BIT);
    glDisable(GL_DEPTH_TEST);
    glDisable(GL_LIGHTING);
    glDisable(GL_TEXTURE_2D);
    glEnable(GL_COLOR_LOGIC_OP);
    glLogicOp(GL_XOR);
    glColor3f(1,1,1);

    glBegin(GL_LINE_LOOP);
      glVertex2f(start.x(),start.y());
      glVertex2f(cur.x(),start.y());
      glVertex2f(cur.x(),cur.y());
      glVertex2f(start.x(),cur.y());
    glEnd();

    glDisable(GL_LOGIC_OP);
    glPopAttrib();
    glPopMatrix();
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
}

void EditTexturePlugin::InitTexture(MeshModel &m)
{
	// Get the textures name and add the tab
	if (m.cm.textures.size() > 0)
	{
		for(unsigned i = 0; i < m.cm.textures.size(); i++)
			widget->AddRenderArea(m.cm.textures[i].c_str(), &m, i);
	}
	else widget->AddEmptyRenderArea();
}


// DA METTERE IN Clean.h
/**
Check if the parametrization is valid
*/
bool EditTexturePlugin::HasCollapsedTextCoords(MeshModel &m)
{
	if(!HasPerWedgeTexCoord(m.cm)) return true;
	for (CMeshO::FaceIterator fi = m.cm.face.begin(); fi != m.cm.face.end(); ++fi)
	{
		if(!(*fi).IsD())
		{
			if( (*fi).WT(0).P() == (*fi).WT(1).P() || (*fi).WT(0).P() == (*fi).WT(2).P() || (*fi).WT(1).P() == (*fi).WT(2).P())
				return true;
		}
	}
	return false;
}
