/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include <QAction>
#include <QApplication>
#include <QDebug>
#include <QHeaderView>
#include <QHBoxLayout>  
#include <QInputDialog>
#include <QLabel>
#include <QMenu>
#include <QPushButton>
#include <QSettings>
#include <QToolButton>
#include <QTableView>
#include <QVBoxLayout>

#include "MvScm.h"
#include "MvQScmDataWidget.h"
#include "MvQScmModel.h"

#include <algorithm>

//===============================================================
//
// MvQScmDataPanel
//
//===============================================================

MvQScmDataPanel::MvQScmDataPanel(QWidget *parent) : 
	QWidget(parent), 
	model_(0), 
	master_(false)
{
	QVBoxLayout *layout= new QVBoxLayout;
	layout->setContentsMargins(0,0,0,0);
	setLayout(layout);
	
	filterModel_= new MvQScmProfileFilterModel(this);
	filterModel_->setDynamicSortFilter(true);
	
	view_=new QTableView;
	view_->setObjectName("paramMlView");
	view_->setProperty("mvStyle",3);	
	view_->setAlternatingRowColors(true);
	view_->setModel(filterModel_);	
	layout->addWidget(view_);
	
	MvQScmTableViewDelegate *mlDelegate=new MvQScmTableViewDelegate(this);
	view_->setItemDelegate(mlDelegate);
		
	connect(view_,SIGNAL(clicked(const QModelIndex&)),
		this,SLOT(slotParamSelected(const QModelIndex&)));	
	
	connect(view_,SIGNAL(pressed(const QModelIndex&)),
		this,SLOT(slotParamSelected(const QModelIndex&)));
}	


void MvQScmDataPanel::setMasterParam(MvScmVar *var,MvScmVar *var1)
{
  	masterParams_[var]=var1;
}

void MvQScmDataPanel::setStep(int step)
{
	prevIndex_=filterModel_->mapToSource(view_->currentIndex());	
	model_->setStep(step);
}	
	
void MvQScmDataPanel::reselectIndex(bool select)
{
	QModelIndex actIndex=model_->index(prevIndex_.row(),prevIndex_.column());	
	view_->setCurrentIndex(filterModel_->mapFromSource(actIndex));			
	if(select)
	{
		if(masterParams_.size() == 0)
		{
		  	 slotParamSelected(actIndex);	
		}
		else 
		{
			if(actIndex.isValid())
			{		  	
	  			std::map<MvScmVar*,MvScmVar*>::iterator it=masterParams_.find(vars_.at(actIndex.column())); 
				if(it != masterParams_.end())
				{
		  			slotParamSelected(actIndex);	
				}	
				else	
				{	
					emit selectParamFromMasterPanel();
				}
			}
			else emit selectParamFromMasterPanel();			  	
		}	
	}
}

void MvQScmDataPanel::slotParamSelected(int index)
{
	if(index >=0 && index < static_cast<int>(vars_.size()))
	{
		MvScmVar *var=vars_.at(index);
		int step=model_->step(); 
		
		std::map<MvScmVar*,MvScmVar*>::iterator it=masterParams_.find(var); 
		if(it != masterParams_.end())
		{
		  	emit paramSelected(it->second,step);
		}
		else if(masterParams_.size() ==0)
		{  	
			emit paramSelected(var,step);
		}  
	}
}	

void MvQScmDataPanel::slotParamSelected(const QModelIndex& index)
{
	if(!index.isValid())
	{
	  	emit stepChanged(model_->step());
	}
	else
	{
	  	slotParamSelected(index.column());
	}	
}

bool MvQScmDataPanel::selectParam(MvScmVar* var)
{
  	if(!var)
	  	return false;
	
	int row=0;
	QModelIndex current=view_->currentIndex();
	if(current.isValid())
		row=current.row();
	
	QModelIndex index=filterModel_->mapFromSource(model_->indexForVar(var,model_->step(),row));
	if(index.isValid())
	{
		view_->setCurrentIndex(index);
		view_->scrollTo(index,QAbstractItemView::PositionAtCenter);
		return true;
	}
	
	return false;
}	

void MvQScmDataPanel::showEditableOnly(bool b)
{
	filterModel_->setShowEditableOnly(b);
}	

bool MvQScmDataPanel::update(const MvScmProfileChange& item)
{ 
  	QModelIndex index=filterModel_->mapFromSource(model_->indexForChange(item));
	if(index.isValid())
	{  	
		view_->update(index);
		view_->scrollTo(index,QAbstractItemView::PositionAtCenter);
			
		if(item.dependantVar())
		{
			index=model_->indexForVar(item.dependantVar(),item.step(),item.level());
			view_->update(filterModel_->mapFromSource(index));
		}
		return true;
	}
	
	return false;
}

void MvQScmDataPanel::reload()
{
  	model_->reload();
}	

//===============================================================
//
// MvQScmProfileDataPanel
//
//===============================================================

MvQScmProfileDataPanel::MvQScmProfileDataPanel(QWidget *parent) : 
   MvQScmDataPanel(parent),
   header_(0)
{
	//Column tree
	model_= new MvQScmProfileModel();
	filterModel_->setSourceModel(model_);
	
	connect(model_,SIGNAL(dataEdited(const MvScmProfileChange&)),
		this,SIGNAL(dataEdited(const MvScmProfileChange&)));
	
	header_=view_->horizontalHeader();
	header_->setContextMenuPolicy(Qt::CustomContextMenu);
	
	connect(header_,SIGNAL(sectionClicked(int)),
		this,SLOT(slotParamSelected(int)));
		
	connect(header_,SIGNAL(customContextMenuRequested(const QPoint &)),
                this, SLOT(slotHeaderContextMenu(const QPoint &)));
		
	//Dymanic filter did not work somehow, so we need to update the filter model in this way	
	filterModel_->setShowEditableOnly(filterModel_->showEditableOnly());	
}  

void MvQScmProfileDataPanel::init(const vector<MvScmVar*>& vars,int step,const vector<float>& levels,const string& levelName,
				  const vector<MvScmVar*>& editableVars)
{
  	vars_=vars;
	editableVars_=editableVars;
	model_->setData(vars,step,levels,levelName);
	model_->setEditableVars(editableVars);
}


void MvQScmProfileDataPanel::slotHeaderContextMenu(const QPoint &position)
{
	int section=header_->logicalIndexAt(position); 
	
	if( section< 0 || section >= header_->count())
		return;
	
	bool editable=(std::find(editableVars_.begin(),editableVars_.end(),vars_.at(section)) != editableVars_.end())?true:false;
	
	if(!editable)
	  	return;
	
	QList<QAction*> actions;
	QAction *action;
	
	action = new QAction(this);
        action->setObjectName(QString::fromUtf8("actionAssign"));
	action->setText(tr("Set values"));
	action->setData("assign");	
	actions << action;
	
	
	action = new QAction(this);
        action->setObjectName(QString::fromUtf8("actionAssign"));
	action->setText(tr("Set values to 0"));
	action->setData("assignToZero");	
	actions << action;
		
    	if(actions.count() > 0 && 
	   (action=QMenu::exec(actions, header_->mapToGlobal(position))) != 0)
	{
		QString key=action->data().toString();
		MvScmVar *var=vars_.at(section);
		if(key == "assign")
		{			  
			assignValue(var);		  
		}
		
		if(key == "assignToZero")
		{			  
			assignValue(var,0.);		  
		}
	}	
			
	foreach(QAction *action,actions)
	{
		delete action;
	}
}

void MvQScmProfileDataPanel::assignValue(MvScmVar *var)
{
	bool ok;
     	double val=QInputDialog::getDouble(0, 
				tr("Assign value"),
                        	tr("New value:"),
                                0.,-10000000,10000000,10,&ok);
				
	if(ok)
        { 			
		assignValue(var,val);	
	}	
}

void MvQScmProfileDataPanel::assignValue(MvScmVar *var,double val)
{
  	vector<MvScmProfileChange> ch;  	
	var->setValues(model_->step(),val,ch);
	for(unsigned int  i=0; i < ch.size(); i++)
	  	emit dataEdited(ch[i]);	 
}  
  
  
//===============================================================
//
// MvQScmProfileDataPanel
//
//===============================================================


MvQScmSurfaceDataPanel::MvQScmSurfaceDataPanel(QWidget *parent) : MvQScmDataPanel(parent)
{
	//Column tree
	model_= new MvQScmSurfaceModel();
	filterModel_->setSourceModel(model_);
	
	connect(model_,SIGNAL(dataEdited(const MvScmProfileChange&)),
		this,SIGNAL(dataEdited(const MvScmProfileChange&)));
		
	QHeaderView *header=view_->verticalHeader();
	
	connect(header,SIGNAL(sectionClicked(int)),
		this,SLOT(slotParamSelected(int)));
		
	//Dymanic filter did not work somehow, so we need to update the filter model in this way	
	filterModel_->setShowEditableOnly(filterModel_->showEditableOnly());		
}  

void MvQScmSurfaceDataPanel::init(const vector<MvScmVar*>& vars,int step,
				  const vector<MvScmVar*>& editableVars)
{
  	vars_=vars;
	model_->setData(vars,step);
	model_->setEditableVars(editableVars);
}

//===============================================================
//
// MvQScmDataWidget
//
//===============================================================

MvQScmTab::MvQScmTab(QWidget* parent) :
  QTabWidget(parent)
{ 	
	tabBar()->setProperty("mvStyle","folder");
}

MvQScmDataWidget::MvQScmDataWidget(QWidget *parent) : 
         QWidget(parent),
         data_(0),
         masterPanel_(0)
{	
	//-----------------------
	// The main layout 
	//-----------------------
  
	QVBoxLayout *mainLayout = new QVBoxLayout;
        mainLayout->setObjectName(QString::fromUtf8("vboxLayout"));
	mainLayout->setContentsMargins(0,0,0,1);
	mainLayout->setSpacing(1);
	setLayout(mainLayout);
	
	//-------------------
	// Central tab	
	//-------------------
		
	tab_= new MvQScmTab(this); 	
	
	mainLayout->addWidget(tab_,1);
	
	connect(tab_,SIGNAL(currentChanged(int)),
		this,SLOT(slotTabIndexChanged(int)));
}
 
MvQScmDataWidget::~MvQScmDataWidget()
{
	writeSettings();
}

void MvQScmDataWidget::clearPanels()
{
  	tab_->clear();
	for(std::map<PanelType,MvQScmDataPanel*>::iterator it=panels_.begin(); it != panels_.end(); it++)
	{
  		delete it->second;
	}
	
	masterPanel_=0;
	tabToPanel_.clear();
	panelToTab_.clear(); 
}  

void MvQScmDataWidget::addPanelToTab(MvQScmDataPanel *panel,PanelType type,QString title)
{
	tab_->addTab(panel,title);
	int tabIndex=tab_->count()-1;
	panelToTab_[type]=tabIndex;
	tabToPanel_[tabIndex]=type;		
}

void MvQScmDataWidget::init(MvScm *data,QList<MvScmVar*> editVars)
{	
  	//Delete all the panels
  	clearPanels();
  	
  	if(data->stepNum() <= 0)
	{
	  	data_=0;
		return;
	}	
	
	data_=data;

	//Get editable variables	     
	std::vector<MvScmVar*> editVec=editVars.toVector().toStdVector();
		
	int step=0;
	MvQScmDataPanel *panel;

	//-------------------------
	// Model levels
	//-------------------------
	
	//At the moment masterPanel_ has to be the model level panel !!!
	
	if(!data->modelLevelDim().isEmpty()) 
	{		
		panel=new MvQScmProfileDataPanel(this);
		panels_[ModelLevPanel]=panel;
	
		//This will be the master panel
		masterPanel_=panel;
		
		//Add to tab
		addPanelToTab(panel,ModelLevPanel,tr("Model levels"));
		
		//init
		panel->init(data_->modelLevel(),step,data_->modelLevelDim().values(),data_->modelLevelDim().longName(),editVec);
	}
		
	//-------------------------
	// Pressure levels
	//-------------------------		
	
	//if(!data->pressureLevelDim().isEmpty())
	if(0)
	{		
	  	panel=new MvQScmProfileDataPanel(this);
		panels_[PressureLevPanel]=panel;
	
		//Add to tab
		addPanelToTab(panel,PressureLevPanel,tr("Pressure levels"));
			 
		//init
		panel->init(data_->pressureLevel(),step,data_->pressureLevelDim().values(),data_->pressureLevelDim().longName(),editVec);
	}
		
	//-------------------------
	//Soil levels
	//-------------------------
	
	if(!data->soilLevelDim().isEmpty())
	{		
	  	panel=new MvQScmProfileDataPanel(this);
		panels_[SoilLevPanel]=panel;
	
		//Add to tab
		addPanelToTab(panel,SoilLevPanel,tr("Soil levels"));
		
		panel->init(data_->soilLevel(),step,data_->soilLevelDim().values(),data_->soilLevelDim().longName(),editVec);
		
		if(masterPanel_)
		{
			panel->setMasterParam(data_->soilVar(MvScm::TempSoil),data_->mlVar(MvScm::TempML));
				
			connect(panel,SIGNAL(selectMasterPanel()),
				masterPanel_,SLOT(slotSelectCurrent()));
		}		
	}
		
	//-------------------------
	//Surface - always exists
	//------------------------
	
	panel=new MvQScmSurfaceDataPanel(this);
	panels_[SurfaceLevPanel]=panel;
	
	//Add to tab
	addPanelToTab(panel,SurfaceLevPanel,tr("Surface and other 2D"));
	
	panel->init(data_->surfaceLevel(),step,editVec);
	
	if(masterPanel_)
	{	
		panel->setMasterParam(data_->surfVar(MvScm::TempSurf),data_->mlVar(MvScm::TempML));
		
		connect(panel,SIGNAL(selectMasterPanel()),
			masterPanel_,SLOT(slotSelectCurrent()));
	}
	
	//-----------------------------------
	//Relaying signals from the panels
	//-----------------------------------
	
	for(std::map<PanelType,MvQScmDataPanel*>::iterator it=panels_.begin(); it != panels_.end(); it++)
	{
		connect(it->second,SIGNAL(dataEdited(const MvScmProfileChange&)),
			this,SIGNAL(dataEdited(const MvScmProfileChange&))); 
			
		connect(it->second,SIGNAL(paramSelected(MvScmVar*,int)),
			this,SIGNAL(paramSelected(MvScmVar*,int)));
			
		connect(it->second,SIGNAL(stepChanged(int)),	
			this,SIGNAL(stepChanged(int)));
	}

	//Initial profile	
	if(masterPanel_)
	{	
	  	//!!!!!!!!!!!!!!!!!!
		masterPanel_->slotParamSelected(data_->mlVarIndex(MvScm::TempML));
	}			
}

void MvQScmDataWidget::slotStepChanged(int value)
{
	int step=value;
	
	//Save and set steps	
	for(std::map<PanelType,MvQScmDataPanel*>::iterator it=panels_.begin(); it != panels_.end(); it++)
	{
		it->second->setStep(step);
	}	
	
	//Update profile view
	
	int actTab=tab_->currentIndex();	
	
	//Model levels
	if(panels_.find(ModelLevPanel) != panels_.end())
	{
		panels_[ModelLevPanel]->reselectIndex(tabToPanel_[actTab] == ModelLevPanel || tabToPanel_[actTab] == PressureLevPanel);
	}
	
	//Pressure
	if(panels_.find(PressureLevPanel) != panels_.end())
	{  
	  	panels_[PressureLevPanel]->reselectIndex(tabToPanel_[actTab] == PressureLevPanel);
	
	}
	
	//Soil
	if(panels_.find(SoilLevPanel) != panels_.end())
	{  
	  	panels_[SoilLevPanel]->reselectIndex(tabToPanel_[actTab] == SoilLevPanel);
	
	}
	
	//Surf
	panels_[SurfaceLevPanel]->reselectIndex(tabToPanel_[actTab] == SurfaceLevPanel);
}	


void MvQScmDataWidget::slotTabIndexChanged(int /*index*/)
{
}

void MvQScmDataWidget::showEditableParams(bool b)
{
	for(std::map<PanelType,MvQScmDataPanel*>::iterator it=panels_.begin(); it != panels_.end(); it++)
	{
		it->second->showEditableOnly(b);
	}	   
}  

void MvQScmDataWidget::selectMlParam(MvScmVar* var)	
{ 
  	if(panels_.find(ModelLevPanel) != panels_.end())
	{			
	  	if(panels_[ModelLevPanel]->selectParam(var))
		{
		  	tab_->setCurrentIndex(panelToTab_[ModelLevPanel]);		
		}
	}	
}  

void MvQScmDataWidget::update(const MvScmProfileChange& item)
{
	if(!item.var())
	  	return;
	
  	for(std::map<PanelType,MvQScmDataPanel*>::iterator it=panels_.begin(); it != panels_.end(); it++)
	{
		if(it->second->update(item))
		{		  
			tab_->setCurrentIndex(panelToTab_[it->first]);
			break;
		}	
	}
}

void MvQScmDataWidget::reload()
{
	for(std::map<PanelType,MvQScmDataPanel*>::iterator it=panels_.begin(); it != panels_.end(); it++)
	{
		it->second->reload();
	}	
}  

void MvQScmDataWidget::writeSettings()
{
}

void MvQScmDataWidget::readSettings()
{
}
