/****************************************************************************
* VCGLib                                                            o o     *
* Visual and Computer Graphics Library                            o     o   *
*                                                                _   O  _   *
* Copyright(C) 2004                                                \/)\/    *
* 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 "holeListModel.h"
#include <QMessageBox>

using namespace vcg;


HoleListModel::HoleListModel(MeshModel *m, QObject *parent)
	: QAbstractItemModel(parent)
{
	state = HoleListModel::Selection;
	mesh = m;
	pickedAbutment.SetNull();
	mesh->updateDataMask(MeshModel::MM_FACEFACETOPO);
	//tri::UpdateTopology<CMeshO>::FaceFace(mesh->cm);
	holesManager.Init(&m->cm);
}


void HoleListModel::drawHoles() const
{
	glLineWidth(2.0f);
	glDepthMask(GL_TRUE);
	glDepthFunc(GL_GREATER);
	glEnable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);

	HoleVector::const_iterator it = holesManager.holes.begin();
	for( ; it != holesManager.holes.end(); ++it)
	{
		if( it->IsSelected())
		{
			if(it->IsAccepted())
				glColor(Color4b::DarkGreen);
			else
				glColor(Color4b::DarkRed);
		}
		else
			glColor(Color4b::DarkBlue);
		it->Draw();
	}

	// draw the edge selected as a bridge abutment in manual-bridging
	if(!pickedAbutment.IsNull())
	{
		glDepthFunc(GL_ALWAYS);
		glLineWidth(2.0f);
		glColor(Color4b::Yellow);
		glBegin(GL_LINES);
			glVertex( pickedAbutment.f->V0(pickedAbutment.z)->P() );
			glVertex( pickedAbutment.f->V1(pickedAbutment.z)->P() );
		glEnd();
	}

	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LESS);
	glLineWidth(2.0f);
	for(it = holesManager.holes.begin(); it != holesManager.holes.end(); ++it)
	{
		if(it->IsSelected())
		{
			if(it->IsAccepted())
				glColor(Color4b::Green);
			else
				glColor(Color4b::Red);
		}
		else
			glColor(Color4b::Blue);

		it->Draw();
	}
}

void HoleListModel::drawCompenetratingFaces() const
{
	glDisable(GL_DEPTH_TEST);
	glDepthFunc(GL_ALWAYS);
	glDisable(GL_LIGHTING);
	glColor3f(0.8f, 0.8f, 0.f);

	// draw face border also behind other face
	HoleVector::const_iterator it;
	for(it = holesManager.holes.begin(); it != holesManager.holes.end(); ++it)
		if(it->IsCompenetrating())
			it->DrawCompenetratingFace(GL_LINE_LOOP);

	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LESS);

	// draw compenetrating face, only the visible part
	for(it = holesManager.holes.begin(); it != holesManager.holes.end(); ++it)
		if(it->IsCompenetrating())
			it->DrawCompenetratingFace(GL_TRIANGLES);

	// draw face border only visible part
	glLineWidth(4.0f);
	glColor3f(1.0f, 1.0f, 0.f);
	for(it = holesManager.holes.begin(); it != holesManager.holes.end(); ++it)
		if(it->IsCompenetrating())
			it->DrawCompenetratingFace(GL_LINE_LOOP);
}

void HoleListModel::toggleSelectionHoleFromFace(CFaceO *bface)
{
	assert(holesManager.IsHoleBorderFace(bface));
	HoleVector::iterator h;
	
	int ind = holesManager.FindHoleFromFace(bface, h);
	if( ind == -1)
		return;
	h->SetSelect( !h->IsSelected() );
  
	emit dataChanged( index(ind, 4), index(ind, 4) );
	emit SGN_needUpdateGLA();
}

void HoleListModel::toggleAcceptanceHole(CFaceO *bface)
{
	assert(state == HoleListModel::Filled);
	HoleVector::iterator h;
	int ind = holesManager.FindHoleFromFace(bface, h);
	
	if(ind == -1)
		return;
	h->SetAccepted( !h->IsAccepted() );
  
	emit dataChanged( index(ind, 6), index(ind, 6) );
	emit SGN_needUpdateGLA();
}

void HoleListModel::addBridgeFace(CFaceO *pickedFace, int pickedX, int pickedY)
{
	BridgeAbutment<CMeshO> picked;
	if (!holesManager.FindBridgeAbutmentFromPick(pickedFace, pickedX, pickedY, picked))
		return;

	// reselect an already selected edge... deselect
	if(pickedAbutment.f == picked.f && pickedAbutment.z == picked.z )
	{
		pickedAbutment.SetNull();
		return;
	}

	if(pickedAbutment.IsNull() || pickedAbutment.f == picked.f)
		pickedAbutment = picked;
	else
	{
		// 2 edge selected... bridge building
		std::vector<CMeshO::FacePointer *> local_facePointer;
		local_facePointer.push_back(&pickedAbutment.f);
		local_facePointer.push_back(&picked.f);
		QString errLog;
		if(FgtBridge<CMeshO>::CreateBridge(pickedAbutment, picked, &holesManager, errLog))
		{
			emit SGN_ExistBridge(true);
			emit layoutChanged();
		}
		else
			QMessageBox::warning(0, tr("Bridge error"), errLog);

		pickedAbutment.SetNull();
	}
}

void HoleListModel::fill(FgtHole<CMeshO>::FillerMode mode)
{
	mesh->updateDataMask(MeshModel::MM_FACEFACETOPO);
  //tri::UpdateTopology<CMeshO>::FaceFace(mesh->cm);
    if(holesManager.Fill(mode))
	{
		state = HoleListModel::Filled;
		emit layoutChanged();
	}
}

void HoleListModel::acceptFilling(bool accept)
{
/*	if(!accept)
	{
		mesh->clearDataMask(MeshModel::MM_FACEFLAGBORDER | MeshModel::MM_FACEFACETOPO );
		mesh->updateDataMask(MeshModel::MM_FACEFACETOPO | MeshModel::MM_FACEFLAGBORDER );
	}
*/
	holesManager.ConfirmFilling(accept);
	
	state = HoleListModel::Selection;
	emit dataChanged( index(0, 0), index(holesManager.HolesCount(), 2) );
	if(holesManager.holes.size()==0)
	{
		QMessageBox::information(0, tr("No holes"), QString("Mesh have no hole to edit."));
		emit SGN_Closing();	
	}
	else
	{
		//emit layoutAboutToBeChanged();
		emit SGN_ExistBridge( holesManager.bridges.size() > 0 );
		emit SGN_needUpdateGLA();
		emit layoutChanged();
	}
}

void HoleListModel::autoBridge(bool singleHole, double distCoeff)
{
	holesManager.DiscardBridges();
	tri::UpdateTopology<CMeshO>::FaceFace(mesh->cm);

	if(singleHole)
		holesManager.AutoSelfBridging(distCoeff, 0);
	else
		holesManager.AutoMultiBridging(0);	

	emit SGN_ExistBridge( holesManager.bridges.size() > 0 );
	emit layoutChanged();
}

void HoleListModel::removeBridges()
{
	holesManager.DiscardBridges();
	emit SGN_ExistBridge(false);
	emit layoutChanged();
}

void HoleListModel::acceptBridges()
{
	holesManager.ConfirmBridges();
	emit SGN_ExistBridge(false);
}

void HoleListModel::closeNonManifolds()
{
	holesManager.CloseNonManifoldHoles();
	emit SGN_ExistBridge( holesManager.bridges.size()>0 );
	
	emit layoutChanged();
}


/************* Implementazione QAbstractItemModel class *****************/


QVariant HoleListModel::data(const QModelIndex &index, int role) const
{
	if(!index.isValid() )
		return QVariant();

	if(role == Qt::DisplayRole)
	{
		switch(index.column())
		{
		case 0:
			return holesManager.holes[index.row()].name;
		case 1:
			return holesManager.holes.at(index.row()).Size();
		case 2:
			return QString("%1").arg(holesManager.holes.at(index.row()).Perimeter(), 0, 'f', 5);
		}
	}
	else if (role == Qt::TextAlignmentRole)
	{
		if(index.column() == 0)   return Qt::AlignLeft;
		if(index.column() == 1 ||
			 index.column() == 2)   return Qt::AlignRight;
		return Qt::AlignCenter;
	}
	else if (role == Qt::CheckStateRole)
	{
		bool checked;
		if(index.column() == 3)
			checked = holesManager.holes[index.row()].IsNonManifold();
		else if(index.column() == 4)
			checked = holesManager.holes[index.row()].IsSelected();
		else if(state == HoleListModel::Filled && holesManager.holes[index.row()].IsSelected())
		{
			if(index.column() == 5)
				checked = holesManager.holes[index.row()].IsCompenetrating();
			else if(index.column() == 6)
				checked = holesManager.holes[index.row()].IsAccepted();
			else
				return QVariant();
		}
		else
			return QVariant();

		if(checked)
			return Qt::Checked;
		else
			return Qt::Unchecked;
	}

	return QVariant();
}


QVariant HoleListModel::headerData(int section, Qt::Orientation orientation, int role) const
{
  if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
	{
		switch(section)
		{
		case 0:
			return tr("Hole");
		case 1:
			return tr("Edges");
		case 2:
			return tr("Perimeter");
		case 3:
			return tr("Non Manif.");
		case 4:
			if(state == HoleListModel::Filled)
				return tr("Fill");
			else
				return tr("Select");
		case 5:
			if(state == HoleListModel::Filled)
				return tr("Comp.");
		case 6:
			if(state == HoleListModel::Filled)
				return tr("Accept");
		}
	}
	else if (orientation == Qt::Horizontal && role == Qt::SizeHintRole)
	{
		switch(section)
		{
		case 0:
			return QSize(63, 20);
		case 1:
			return QSize(38, 20);
		case 2:
			return QSize(55, 20);
		case 3:
			return QSize(60, 20);
		case 4:
			if(state == HoleListModel::Filled)
				return QSize(20, 20);
			else
				return QSize(50, 20);
		case 5:
			return QSize(38, 20);
		case 6:
			return QSize(42, 20);
		}
	}
	else if (orientation == Qt::Horizontal && role == Qt::ToolTip && state==HoleListModel::Filled && section == 4)
		return tr("Compenetration");

	return QVariant();
}


QModelIndex HoleListModel::index(int row, int column, const QModelIndex &/*parent*/) const
{
	if(row >= (int)holesManager.holes.size())
		return QModelIndex();
	return createIndex(row,column/*,0*/);
}


Qt::ItemFlags HoleListModel::flags(const QModelIndex &index) const
{
	Qt::ItemFlags ret = QAbstractItemModel::flags(index);

	if (!index.isValid())
		return Qt::ItemIsEnabled;

	if(index.column() == 0)
		ret = ret | Qt::ItemIsEditable;
	else if( (index.column() == 4 && state == HoleListModel::Selection) ||
					 (index.column() == 6 && state == HoleListModel::Filled) )
		return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled;
		//return ret = ret | Qt::ItemIsUserCheckable ;

	return ret;
}

bool HoleListModel::setData( const QModelIndex & index, const QVariant & value, int role )
{
	if(!index.isValid())
		return false;

	bool ret = false;
	if(role == Qt::EditRole && index.column() == 0)
	{
		QString newName = value.toString().trimmed();
		if(newName != "")
		{
			holesManager.holes[index.row()].name = newName;
			ret = true;
		}
	}
	else if(role == Qt::CheckStateRole)
	{
		if(state == HoleListModel::Selection)
		{
			if(index.column() == 4 && state == HoleListModel::Selection)
			{
				holesManager.holes[index.row()].SetSelect( !holesManager.holes[index.row()].IsSelected() );
				ret = true;
			}
		}
		else if(index.column() == 6)
		{	// check accept
			holesManager.holes[index.row()].SetAccepted( !holesManager.holes[index.row()].IsAccepted() );
			ret = true;
		}
	}
	if(ret)
	{
		emit dataChanged(index, index);
		emit SGN_needUpdateGLA();
	}

	return ret;
}

void HoleListModel::emitPostConstructionSignals()
{
	emit dataChanged( index(0, 0), index(holesManager.HolesCount(), 2) );
	emit SGN_needUpdateGLA();
}
