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

 Copyright 2013 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 "MvQEditor.h"

#include <QAction>
#include <QActionGroup>
#include <QCloseEvent>
#include <QDateTime>
#include <QDebug>
#include <QDialogButtonBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QPainter>
#include <QPlainTextEdit>
#include <QPushButton>
#include <QSettings>
#include <QStyle>
#include <QStyleOption>
#include <QStackedLayout>
#include <QStackedWidget>
#include <QToolBar>
#include <QToolButton>
#include <QVBoxLayout>

#include "EditorDrawerFactory.h"
#include "Folder.h"
#include "IconClass.h"
#include "MvRequest.h"
#include "Path.h"
#include "Request.h"

#include "MvQEditorDrawerPanel.h"
#include "MvQFolderModel.h"
#include "MvQTemplateDrawer.h"
#include "MvQIconProvider.h"
#include "MvQTheme.h"

//================================================
//
// MvQEditorHeader
//
//================================================

MvQEditorHeader::MvQEditorHeader(MvQEditor *owner,QPixmap pix) : 
        QWidget(owner),
        owner_(owner)
{
  	QHBoxLayout *hb=new QHBoxLayout(this);	
  	hb->setSpacing(0); 
	hb->setContentsMargins(0,0,0,0);	
	
	QFont font;
	font.setBold(true);
	QFontMetrics fm(font);
	int txth=3*fm.height()+2*fm.leading()+8; 
	txth=(txth > 32)?txth:32;
			
  	//Pixmap
	QLabel *pixLabel=new QLabel(this);
	pixLabel->setPixmap(pix);	
	pixLabel->setFrameShape(QFrame::Box);
        pixLabel->setFrameShape(QFrame::StyledPanel);
	pixLabel->setObjectName(QString::fromUtf8("editorHeaderIcon"));
	pixLabel->setMaximumHeight(txth);
	hb->addWidget(pixLabel);
	
	stackLayout_=new QStackedLayout();
	hb->addLayout(stackLayout_);
	
	//Info panel
	QWidget *w=new QWidget;
	QHBoxLayout *labelHb=new QHBoxLayout(w);	
	labelHb->setSpacing(0); 
	labelHb->setContentsMargins(2,1,2,1);		
	stackLayout_->addWidget(w);
	
	label_=new QLabel("",this);
	label_->setObjectName(QString::fromUtf8("editorHeaderLabel"));	
	label_->setAlignment(Qt::AlignLeft| Qt::AlignVCenter);
        label_->setAutoFillBackground(true);
        label_->setTextInteractionFlags(Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse);
	label_->setFrameShape(QFrame::Box);
        label_->setFrameShape(QFrame::StyledPanel);
	label_->setMargin(2);
	label_->setMaximumHeight(txth);
	labelHb->addWidget(label_);

	renameTb_=new QToolButton(this);
	renameTb_->setAutoRaise(true);
	//renameTb_->setText(tr("Rename"));
	renameTb_->setToolTip(tr("Click to rename icon"));
	renameTb_->setIcon(QPixmap(QString::fromUtf8(":/desktop/edit.svg")));
	labelHb->addWidget(renameTb_);
	
	connect(renameTb_,SIGNAL(clicked(bool)),
		this,SLOT(slotStartEdit(bool)));
	
	//Editor
	w=new QWidget;
	QHBoxLayout *editHb=new QHBoxLayout(w);
	editHb->setSpacing(0); 
	editHb->setContentsMargins(2,1,2,1);
	stackLayout_->addWidget(w);
	
	edit_=new QLineEdit(this);
	editHb->addWidget(edit_);
	
	//Rename buttonBox_
	
	QToolButton *acceptTb=new QToolButton(this);
	acceptTb->setAutoRaise(true);
	acceptTb->setIcon(QPixmap(QString::fromUtf8(":/desktop/accept.svg")));
	editHb->addWidget(acceptTb);
	
	QToolButton *cancelTb=new QToolButton(this);
	cancelTb->setAutoRaise(true);
	cancelTb->setIcon(QPixmap(QString::fromUtf8(":/desktop/cancel.svg")));
	editHb->addWidget(cancelTb);
	
	connect(acceptTb,SIGNAL(clicked(bool)),
		this,SLOT(slotAcceptEdit(bool)));	
	
	connect(cancelTb,SIGNAL(clicked(bool)),
		this,SLOT(slotCancelEdit(bool)));
		
	setProperty("editor","true");	
		
}  

void MvQEditorHeader::slotStartEdit(bool)
{
  	if(stackLayout_->currentIndex() == 0)
	{	
	  	stackLayout_->setCurrentIndex(1);
		if(owner_->current())
		  	edit_->setText(QString::fromStdString(owner_->current()->name()));
		else
		  	edit_->clear();
	}
}	

void MvQEditorHeader::slotAcceptEdit(bool)
{
  	if(stackLayout_->currentIndex() == 1)
	{
	  	owner_->rename(edit_->text());
		stackLayout_->setCurrentIndex(0);
	}		
}	

void MvQEditorHeader::slotCancelEdit(bool)
{
  	if(stackLayout_->currentIndex() == 1)
	{
		stackLayout_->setCurrentIndex(0);
		edit_->clear();
	
	}	
}

void MvQEditorHeader::updateLabel(IconObject* obj)
{	
  	if(!obj)
	  	return;
	
	QString str="<b><font color=" + MvQTheme::colour("editor","header_text").name() + tr("> Icon name: </font>");
	str+="<font color=" +  MvQTheme::colour("editor","header_icon_name").name() + ">" + QString::fromStdString(obj->name()) + "</font></b><br>";
	
	str+="<font color=" +  MvQTheme::colour("editor","header_text").name()   + ">";
	
	str+=tr("<b>Folder: </b>");
	str+=QString::fromStdString(obj->parent()->fullName()) + "<br>";
	
	str+=tr("<b>Type: </b> ");
	str+=QString::fromStdString(obj->className());
	
  	QDateTime dt=QDateTime::fromTime_t(obj->path().lastModified());
	str+=tr(" <b>Modified: </b>");	
	str+=dt.toString("yyyy-MM-dd hh:mm");
	
	if(obj->locked())
	  	str+=tr(" <b>Status: <font color=\'red\'>Read only</font></b>");	
	
	str+="</font>";
	
	label_->setText(str);

	lock(obj->locked());
}  

void MvQEditorHeader::lock(bool b)
{
  	renameTb_->setEnabled(!b);
}

void MvQEditorHeader::paintEvent(QPaintEvent *)
 {
     QStyleOption opt;
     opt.init(this);
     QPainter p(this);
     style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
 }
 
//================================================
//
// MvQEditorTextPanel
//
//================================================	

MvQEditorTextPanel::MvQEditorTextPanel(const IconClass& c,Editor* owner,QWidget* parent) : 
            QWidget(parent),
            class_(c),
            owner_(owner),
            current_(0),
            correct_(true),
            beingLoaded_(false)
{
  	QVBoxLayout *vb=new QVBoxLayout;
	setLayout(vb);
	
	vb->setSpacing(0); 
	vb->setContentsMargins(0,0,0,0);	
		
	//Warning label
	QHBoxLayout* warnLayout=new QHBoxLayout();
	vb->addLayout(warnLayout);
	
	warnLabel_=new QLabel("This is the contents of the file storing your icon settings. Please only edit it at <b> your own <b>risk</b>!",this);
	warnLabel_->setWordWrap(true);
	warnLabel_->setObjectName("editorWarningLabel");	
	warnLabel_->setAlignment(Qt::AlignCenter);
        warnLabel_->setAutoFillBackground(true);
	warnLayout->addWidget(warnLabel_,1);
	
	//Message label
	QHBoxLayout* msgLayout=new QHBoxLayout();
	vb->addLayout(msgLayout);
	
	msgLabel_=new QLabel("Hello",this);
	msgLabel_->setObjectName("editorMessageLabel");
	msgLabel_->setWordWrap(true);
	msgLayout->addWidget(msgLabel_,1);	
	msgLabel_->hide();

	//Text editor
	te_=new QPlainTextEdit(this);
	te_->setProperty("mvStyle","fileContents");
	vb->addWidget(te_,1); 
	
	connect(te_,SIGNAL(textChanged()),
		this,SLOT(slotTextChanged()));
}

void MvQEditorTextPanel::setObject(IconObject* obj)
{
  	current_=obj;
	
	QString str=tr("This is the contents of the file storing your icon settings.");
	
	if(obj->locked())
	{
	  	te_->setReadOnly(true);
		te_->setProperty("mvStyle","fileContentsReadOnly");
	}
	else	  	
	{
	  	te_->setReadOnly(false);
		te_->setProperty("mvStyle","fileContents");
		str+=tr("Please only edit it at <b> your own <b>risk</b>!");
	}
	
	te_->update();	
	warnLabel_->setText(str);
}


void MvQEditorTextPanel::loadRequestFile()
{
	beingLoaded_=true;
	QFile f(QString::fromStdString(current_->path().str()));
	f.open(QFile::ReadOnly | QFile::Text);	
	QTextStream rs(&f);
	te_->setPlainText(rs.readAll());
  	f.close();
	beingLoaded_=false;
}

/*bool MvQEditorTextPanel::checkVerb()
{
  	const char* v=current_->request().getVerb();
        const char* ic=current_->iconClass().name().c_str();
	
  	if(strcmp(v,ic) != 0)
	{
	  	QString s="<b><font color=\'red\'> Error: </b></font> Request type <b>" + QString(v) + "</b> in text does not match icon type <b>"  + QString(ic) + "</b>! ";
		s+="Please change request type to <b>" + QString(ic) + "</b> or revert the icon back to system default.";
		
		msgLabel_->setText(s);	
		
		return false;
	}
	
	return true;
}*/

bool MvQEditorTextPanel::check(bool fromSave)
{
 	QString s=te_->toPlainText(); 
	Request r=Request::fromText(s.toStdString());
	QString verb;
	
	qDebug() << "TEXT --->";
	r.print();
	qDebug() << "<--- TEXT";
	
	if(r)
	    verb=QString(r.getVerb());
	  	  
	QString ic=QString::fromStdString(current_->editorClass().name());
	//QString ic=QString::fromStdString(current_->iconClass().name());
	
	QString msg;	
	bool failed=false;
	if(verb.compare(ic,Qt::CaseInsensitive) != 0)
	{
	  	msg="Request type <b>" + verb + "</b> in text does not match icon type <b>"  + ic + "</b>! ";
		msg+="Please change the request type to <b>" +ic + "</b> or revert the icon back to system defaults.";		
		failed=true;
	}
	
	if(failed)
	{
	 	QString s="<table><tr><td><img src=\':/desktop/error.svg\' /></td><td width=\'4\'/><td><b>Error: </b>";
		if(fromSave)
	  		s+="<u>Cannot save file!</u>";
		else
		  	s+="<u>Broken request!</u>";
		
		s+="<br>" + msg + "</td></tr></table>"; 	
		msgLabel_->show();
		msgLabel_->setText(s);
	}
	else
	  	msgLabel_->hide();
 	
	correct_=(!failed);
	
	return correct_;
}  


void MvQEditorTextPanel::slotTextChanged()
{
  	if(!beingLoaded_)
	{
	  	owner_->changed();
		msgLabel_->hide();
	}	
}


void MvQEditorTextPanel::save()
{
	FILE *f = fopen(current_->path().str().c_str(),"w");
	if(f == NULL)
	{
		//marslog(LOG_EROR|LOG_PERR,"%s",tmp);
	}
		
	qDebug() << te_->toPlainText() << current_->path().str().c_str();

	fprintf(f,"%s\n",te_->toPlainText().toStdString().c_str());
	fclose(f);
}

void MvQEditorTextPanel::load()
{  	
	loadRequestFile();
	check(false);
}

//================================================
//
// MvQEditor
//
//================================================

MvQEditor::MvQEditor(const IconClass& name,const string& kind, QWidget *parent) : 
        QDialog(parent), 
        Editor(name,kind),
        drawerFilled_(false),
        ignoreChange_(false)
{
  	//Set Window icon
  	setWindowIcon(MvQIconProvider::pixmap(name,24));
  
  	QVBoxLayout *vb=new QVBoxLayout;
	vb->setSpacing(0); 
	vb->setContentsMargins(0,0,0,0);
	setLayout(vb);
	
	//Header		
	headerPanel_=new MvQEditorHeader(this,MvQIconProvider::pixmap(name,32));
	vb->addWidget(headerPanel_);
	
	//-------------------
	// Toolbar
	//-------------------
	
	toolBarLayout_=new QHBoxLayout();
	toolBarLayout_->setSpacing(0); 
	toolBarLayout_->setContentsMargins(0,0,0,0);
	
	//vb->addLayout(toolBarLayout_);
	
	//Widget to provide bg for the toolbars
	QWidget *w=new QWidget(this);
	w->setProperty("editor","true");	
	w->setLayout(toolBarLayout_);
	vb->addWidget(w);
	
	//toolbarLeft_=new QToolBar(this);
	//toolbarLeft_->setIconSize(QSize(20,20)); 
		
	toolbarRight_=new QToolBar(w);
	//toolbarRight_->setIconSize(QSize(18,18)); 
	
	//toolBarLayout_->addWidget(toolbarLeft_,1);
	//toolBarLayout_->addStretch(1);	
	toolBarLayout_->addWidget(toolbarRight_);
	
	QWidget *spacer = new QWidget(this);
	spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
	sepAc_=toolbarRight_->addWidget(spacer);
		
	//Gui edit
	guiAc_ =new QAction(this);
	guiAc_->setIcon(QPixmap(":/desktop/gui_edit.svg"));	
	guiAc_->setCheckable(true);
	guiAc_->setToolTip(tr("<b>GUI</b> edit mode.<br> To switch between edit modes changes have to be <u>saved<u>!"));
	toolbarRight_->addAction(guiAc_);
	
	//Text edit
	textAc_ =new QAction(this);
	textAc_->setIcon(QPixmap(":/desktop/text_edit.svg"));	
	textAc_->setCheckable(true);
	textAc_->setToolTip(tr("<b>Text</b> edit mode.<br> To switch between edit modes changes have to be <u>saved</u>!"));
	toolbarRight_->addAction(textAc_);
		
	viewAg_=new QActionGroup(this);
	viewAg_->setExclusive(true);
	viewAg_->addAction(guiAc_);
	viewAg_->addAction(textAc_);
	
	guiAc_->setChecked(true);
	
	connect(viewAg_,SIGNAL(triggered(QAction*)),
		this,SLOT(slotChangeView(QAction*)));
	
	//Search 
	//QSearchLine
	//QLabel
	//searchLine_=new QLineEdit(this);	
		
	//toolBarLayout_->addStretch(1);
	
	//---------------------
	// Central part
	//---------------------
		
	viewSw_=new QStackedWidget(this);
	vb->addWidget(viewSw_,1);
	
	//Panel layout
	w= new QWidget(this);
	centralLayout_=new QVBoxLayout;
	centralLayout_->setSpacing(0); 
	centralLayout_->setContentsMargins(1,1,1,1);	
	w->setLayout(centralLayout_);
	viewSw_->addWidget(w);
	
	//Text widget
	textPanel_ = new MvQEditorTextPanel(name,this,this);
	viewSw_->addWidget(textPanel_);
        viewSw_->addWidget(textPanel_);
	
	viewSw_->setCurrentIndex(0);		
	
	//--------------------
	// Drawer
	//--------------------
	
	drawerPanel_=new MvQEditorDrawerPanel(this);
	
	/*map<string, EditorDrawer*> drawers=EditorDrawerFactory::create(this);
	for(map<string, EditorDrawer*>::iterator j = drawers.begin();j != drawers.end(); ++j) 
	{
	  	drawerPanel_->addDrawer((*j).second->widget(),
						(*j).second->name());
	}*/
	
	vb->addWidget(drawerPanel_);
	
	drawerPanel_->shrink();
	
	//--------------------
	// Buttonbox
	//--------------------

	buttonBox_= new QDialogButtonBox(this);
	
	savePb_=buttonBox_->addButton(QDialogButtonBox::Save);
	okPb_=buttonBox_->addButton(QDialogButtonBox::Ok);
	cancelPb_=buttonBox_->addButton(QDialogButtonBox::Cancel);	
	resetPb_=buttonBox_->addButton(QDialogButtonBox::Reset);
	
	savePb_->setDefault(true);
	savePb_->setEnabled(false);
	resetPb_->setEnabled(false);
	
	/*QCheckBox* stayOpenCb= new QCheckBox(tr("Stay open"),this);
	stayOpenCb->setChecked(false);
	buttonBox_->addButton(stayOpenCb,QDialogButtonBox::YesRole);*/
	
	//buttonBox_->addButton(QDialogButtonBox::Close);

	connect(buttonBox_, SIGNAL(clicked(QAbstractButton*)), 
		this, SLOT(slotButtonClicked(QAbstractButton*)));
	
	vb->addWidget(buttonBox_);
}
	
	
MvQEditor::~MvQEditor()
{
}
void MvQEditor::showIt() 
{
	show();
	
	//Mimimize the drawer
	drawerPanel_->shrink();	
}

void MvQEditor::addToToolBar(QList<QAction*> lst)
{
  	toolbarRight_->insertActions(sepAc_,lst);
}

//Iconobserver method
void MvQEditor::iconChanged(IconObject* obj)
{
  	if(obj==current_)
	{
	  	headerPanel_->updateLabel(current_);
		updateWindowTitle();
	}	
}	


void MvQEditor::changed()
{
	if(!temporary_ && !ignoreChange_)
	{
		savePb_->setEnabled(true);
		resetPb_->setEnabled(true);		
		viewAg_->setEnabled(false);
	}
	
	//resetPb_->setEnabled(true);	
}  

void MvQEditor::edit()
{	  
  	if(!current_)
	  	return;
 		
	//Set default button status
	savePb_->setEnabled(false);
	resetPb_->setEnabled(false);
	
	//Populate drawers if needed
	if(!drawerFilled_)
	{
	  	map<string, EditorDrawer*> drawers=EditorDrawerFactory::create(this);
		for(map<string, EditorDrawer*>::iterator j = drawers.begin();j != drawers.end(); ++j) 
		{
	  		drawerPanel_->addDrawer((*j).second->widget(),
						(*j).second->name());
		}
		drawerFilled_=true;
	}	
		
	//Read the previous size
	readSettings();

	//Header
	headerPanel_->updateLabel(current_);	
	
	//Title
	updateWindowTitle();
	
	//Text
	textPanel_->setObject(current_);
	
	//If request is broken we switch to text mode
	if(!current_->checkRequest())
	{
	  	textPanel_->load();
	  	viewAg_->setEnabled(false);
		textAc_->setChecked(true);
		slotChangeView(textAc_);
		//textAc_->toggled();	
	}	
	//otherwise we start with gui mode
	else
	{  
		viewAg_->setEnabled(true);
		guiAc_->setChecked(true); 
		slotChangeView(guiAc_); // this will call reset
		viewAg_->setEnabled(true);
	}
	
	savePb_->setEnabled(false);
	resetPb_->setEnabled(false);
	
	okPb_->setEnabled(!current_->locked());	  	
}

bool MvQEditor::isTextMode()
{
  	return (viewSw_->currentIndex() == TextViewMode);
}	
   
void MvQEditor::slotTextChanged()
{
  	changed();
}	
   
void MvQEditor::raiseIt() 
{
	if(isMinimized()) 
		showNormal();
	
	raise();
}	

void MvQEditor::rename(QString text)
{
	if(!current_)
	  	return;
  		
	if(!text.isEmpty())
        { 	
	  	string n=text.toStdString();
	  	if( n != current_->name())
		{
			current_->editor(0);
			current_->rename(n);
			current_->editor(this);
		}
		headerPanel_->updateLabel(current_);
		updateWindowTitle();		
	}	  
}  

void MvQEditor::slotChangeView(QAction *ac)
{
  	if(savePb_->isEnabled())
	{
	  	viewAg_->setEnabled(false);
	  	return;
	}
	
  	//From gui to text mode
  	if(ac == textAc_)
	{
	 	viewSw_->setCurrentIndex(TextViewMode);		
		ignoreChange_=true;
		textPanel_->load();
		ignoreChange_=false;
	}
	//From text to gui mode
	else
	{	  	
	  	viewSw_->setCurrentIndex(LineViewMode);
		ignoreChange_=true;
		reset();
		ignoreChange_=false;
	}	
}

void MvQEditor::slotButtonClicked(QAbstractButton* button)
{
	if(!button) return;
	
	if(buttonBox_->standardButton(button) == QDialogButtonBox::Cancel)
	{
		writeSettings();
		
		savePb_->setEnabled(false);
		resetPb_->setEnabled(false);
		
		close();
		reject();  	
	}
	else if(buttonBox_->standardButton(button) == QDialogButtonBox::Ok)
	{
	  	writeSettings();
	  	
	  	//Text mode
		if(isTextMode())
		{
		  	if(textPanel_->check())  
			  	textPanel_->save();	
			else 	
			{
			  	viewAg_->setEnabled(false);
			  	return;
			}	
		}
		//GUI mode
		else  
		{
		  	ignoreChange_=true;
			apply();
			ignoreChange_=false;
		}	
		
		current_->modified();
		notifyObserverApply();
		
		savePb_->setEnabled(false);
		resetPb_->setEnabled(false);
		
		close();
		accept();
	}	
	else if(buttonBox_->standardButton(button) == QDialogButtonBox::Save)
	{	  	
		ignoreChange_=true;
		
		//Text mode
		if(isTextMode())
		{
		  	if(textPanel_->check())  
			  	textPanel_->save();	
			else 	
			{
			  	viewAg_->setEnabled(false);
			  	return;
			}	
		}
		//GUI mode
		else  
		{
		  	ignoreChange_=true;
			apply();
			ignoreChange_=false;
		}
		
		current_->modified();
		notifyObserverApply();
		
		savePb_->setEnabled(false);
		resetPb_->setEnabled(false);
		viewAg_->setEnabled(true);
		
	}
	else if(buttonBox_->standardButton(button) == QDialogButtonBox::Reset)
	{
		//Text mode
		if(isTextMode())
			textPanel_->load();
		else	  
		{
		  	ignoreChange_=true;
			reset();
			ignoreChange_=false;
		}
			
		savePb_->setEnabled(false);
		resetPb_->setEnabled(false);
		viewAg_->setEnabled(true);
	}	
}  

void MvQEditor::accept()
{
  	editDone();	
  	QDialog::accept();
}

void MvQEditor::reject()
{
  	notifyObserverClose();
	editDone();
	QDialog::reject();
}  

void MvQEditor::temporary()
{
	Editor::temporary();
	savePb_->setEnabled(false);
   viewAg_->setEnabled(false);
	headerPanel_->lock(true);
}

void MvQEditor::closeEvent(QCloseEvent* event)
{
	writeSettings();
	close();
	accept();
	event->accept();
}

void MvQEditor::updateWindowTitle()
{ 
	QString txt=QString::fromStdString(current_->name());
	if(current_->parent())
	 	txt+=" - " + QString::fromStdString(current_->parent()->fullName());
		
	txt+=" - Metview";
	setWindowTitle(txt);
}

void MvQEditor::writeSettings()
{
	QSettings settings("ECMWF","MV4-Desktop-Editor-" + QString::fromStdString(class_.name()));
	
	//We have to clear it so that should not remember all the previous values 
	settings.clear();
	
	settings.beginGroup("main");
	settings.setValue("size",size());
	settings.endGroup();
	
	writeSettings(settings);
}

void MvQEditor::readSettings()
{
	QSettings settings("ECMWF","MV4-Desktop-Editor-" + QString::fromStdString(class_.name()));
  	
	settings.beginGroup("main");
	if(settings.contains("size"))
	{  
		resize(settings.value("size").toSize());
	}
	else
	{
	  	resize(QSize(520,500));
	}
	
	settings.endGroup(); 
	
	readSettings(settings);	
}
