/***************************** 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 <QAction>
#include <QCloseEvent>
#include <QDebug>
#include <QDialogButtonBox>
#include <QFile>
#include <QMenu> 
#include <QPushButton>
#include <QSplitter>
#include <QSettings>
#include <QString>
#include <QStringList>
#include <QTabWidget>
#include <QTextEdit>
#include <QTimer>
#include <QToolButton>
#include <QVBoxLayout>

#include "ProcMonitor.h"

#include <iostream>
 
#include "MvQPixmapCache.h"
#include "MvProcNode.h"
#include "MvQProcModel.h"
#include "MvQTreeView.h"

using namespace std;


QSize ProcViewDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
	QSize size=QStyledItemDelegate::sizeHint(option,index);
	return size+QSize(0,2);
}


ProcMonitor::ProcMonitor(svc *service,QWidget*parent) : 
    MvQMainWindow(parent),
    firstUpdate_(true)
{
	service_=service;
	
	setAttribute(Qt::WA_DeleteOnClose);
	
	setWindowTitle("Process Monitor - Metview");
	setWindowIcon(QPixmap(":/debug/monitor.svg"));
	//Initial size
	setInitialSize(460,400);
	
	//---------------------------------------------------
	// The main layout (the upper part of mainSplitter)
	//---------------------------------------------------
  
	QVBoxLayout *mainLayout = new QVBoxLayout;
        mainLayout->setObjectName(QString::fromUtf8("vboxLayout"));
	mainLayout->setContentsMargins(0,0,0,0);
	mainLayout->setSpacing(0);
	
	QWidget *w=new QWidget(this);
	w->setLayout(mainLayout);
	setCentralWidget(w);
	
	mainSplitter_=new QSplitter(this);
	mainSplitter_->setOpaqueResize(false);
	mainSplitter_->setOrientation(Qt::Vertical);
	
	mainLayout->addWidget(mainSplitter_);
	
	//------------------
	// File info label
	//------------------
      
 	//Add to layout 
	//mainLayout->addWidget(examinerBase_->fileInfoLabel());
	
	//---------------------------
	// Process tree to the left
	//---------------------------
	
	procModel_= new MvQProcModel(this);	
	loadIcons(procModel_->pixmapCache());
	
	filterModel_= new MvQProcFilterModel(this);	
	filterModel_->setSourceModel(procModel_);	
	filterModel_->setDynamicSortFilter(true);
	
	/*messageSortModel_= new MvQBufrDumpSortFilterModel;
        messageSortModel_->setSourceModel(messageModel_);
	messageSortModel_->setDynamicSortFilter(true);
	messageSortModel_->setFilterRole(Qt::UserRole);
	//messageSortModel_->setFilterRegExp(QRegExp("[1]"));
	messageSortModel_->setFilterFixedString("1");
	messageSortModel_->setFilterKeyColumn(0);*/

	procTree_=new MvQTreeView;
	procTree_->setObjectName("messageTree");
	procTree_->setProperty("mvStyle","desktop");
	procTree_->setSortingEnabled(true); 
	procTree_->sortByColumn (0,Qt::AscendingOrder); 
	//procTree_->setAlternatingRowColors(true);
        procTree_->setAllColumnsShowFocus(true);
	procTree_->setModel(filterModel_);	
	procTree_->setActvatedByKeyNavigation(true);
	//procTree_->setUniformRowHeights(true);
	procTree_->setItemsExpandable(false);
	procTree_->setRootIsDecorated(false);
	//procTree_->setIconSize(QSize(40,40));

	ProcViewDelegate *delegate=new ProcViewDelegate;
	procTree_->setItemDelegate(delegate);
	
	mainSplitter_->addWidget(procTree_);
	
	//Add context menu
	procTree_->setContextMenuPolicy(Qt::CustomContextMenu);
	
	//Context menu 
    	connect(procTree_, SIGNAL(customContextMenuRequested(const QPoint &)),
                this, SLOT(slotContextMenu(const QPoint &)));
			
	connect(procTree_,SIGNAL(clicked(const QModelIndex&)),
                this, SLOT(slotProcInfo(const QModelIndex&)));
		
	//Init model	
	procModel_->setData(MvProcNode::root());	
		
	//---------------------------------
	// Process info at the bottom
	//---------------------------------
	
	//Read css for the text formatting
	QString cssDoc;
	QFile f(":/debug/procInfo.css");
	QTextStream in(&f);
	if(f.open(QIODevice::ReadOnly | QIODevice::Text))
	{
       		cssDoc=QString(f.readAll());
	}
	f.close();

	infoTab_=new QTabWidget(this);
	mainSplitter_->addWidget(infoTab_);
	
	//Info 
	procInfo_=new QTextEdit(this);
	procInfo_->setReadOnly(true);
	procInfo_->setLineWrapMode(QTextEdit::NoWrap);	
	procInfo_->document()->setDefaultStyleSheet(cssDoc);		
	infoTab_->addTab(procInfo_,tr(" Information"));
	
	//Request
	procReq_=new QTextEdit(this);
	procReq_->setReadOnly(true);
	procReq_->setLineWrapMode(QTextEdit::NoWrap);		
	procReq_->document()->setDefaultStyleSheet(cssDoc);	
	infoTab_->addTab(procReq_,tr("Request"));
	
	QToolButton *tbCloseInfo=new QToolButton(this);
	tbCloseInfo->setIcon(QPixmap(":/debug/close.svg"));
	tbCloseInfo->setAutoRaise(true);
	tbCloseInfo->setToolTip(tr("Hide information panel"));
	
	connect(tbCloseInfo,SIGNAL(clicked()),
		infoTab_,SLOT(hide()));
		
	infoTab_->setCornerWidget(tbCloseInfo);
	infoTab_->hide();
	
	//procInfo_=new QTextEdit(this);	
	//procInfo_->setReadOnly(true);
	//procInfo_->document()->setDefaultStyleSheet(cssDoc);	
	//procInfo_->setLineWrapMode(QTextEdit::NoWrap);
	
	//procInfo_->setWordWrapMode(QTextOption::WordWrap);
	
	
	//mainSplitter_->addWidget(procInfo_);
		
	//--------------------------------------
	// Buttons
	//--------------------------------------
	
	buttonBox_= new QDialogButtonBox(this);
	
	buttonBox_->addButton(QDialogButtonBox::Close);
	
	connect(buttonBox_, SIGNAL(clicked(QAbstractButton*)), 
		this, SLOT(slotButtonClicked(QAbstractButton*)));
	
	mainLayout->addWidget(buttonBox_);
	
	//Starts stats update
	startService();
	
	//Read settings
	readSettings();
		
}

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

void ProcMonitor::slotButtonClicked(QAbstractButton* button)
{
	if(!button) return;
	
	if(buttonBox_->standardButton(button) == QDialogButtonBox::Close)
	{
		writeSettings();
		close();
		//accept();  	
	}  
}  

void ProcMonitor::input(svcid *id,request *r)
{ 	
	//ref is not always present in tQModelIndex index=indexAt(position);	he request, so we get it from the id
  	long ref   = atol(get_value(id->r,"REF",0));
	QString name(get_value(id->r,"NAME",0));
	QString mode(get_value(id->r,"MODE",0));	

	//cout << ref << " " << name << " " << mode << endl;

	cout << "id->r ------------------------>" << endl;
	
	print_all_requests(id->r);
	
	cout << "r ----------------------->" << endl;
	
	print_all_requests(r);
		
	
	//cout << r << " " << id->r << endl;
	
	//print_all_requests(id->r);
	
	if(mode == "REPLY")
	{
		long reqid = atol(get_value(r,"REQ_ID",    0));
		
		qDebug() << "reply" << name << reqid ;
		
		//Info *p = Info::Find(reqid);
		//delete p;
	}
	else if(mode == "WAIT")
	{
		long reqid = atol(get_value(r,"REQ_ID",    0));
		long from  = atol(get_value(r,"SOURCE_REF",0));
		const char *to   = get_value(r,"TARGET",0);

		qDebug() << "wait" << name << reqid << from << to;
		
		//Info *p = Info::Find(reqid);
		//delete p;
		//new Wait(reqid,from,to,r->next);
	}
	else if(mode == "REGISTER")
	{
		qDebug() << "register" << name << ref; 
		
		procModel_->dataIsAboutToChange();
		MvProcNode::add(ref,r);		
	  	procModel_->updateData();
		procTree_->expandAll();
		adjustView();
	}
	else if(mode == "BUSY")
	{
		r = r->next;		
		long reqid = atol(get_value(r,"REQ_ID",    0));
		
		qDebug() << "busy" << name << reqid; 
		
		//Info *p = Info::Find(reqid);
		//delete p;
	}
	else if(mode == "SERVICE")
	{
		long reqid = atol(get_value(r,"REQ_ID",    0));
		
		MvProcNode::addProcRequest(reqid,ref,name.toStdString(),r);
	  
	  	//MvProcNode *proc=MvProcNode::find(ref,name.toStdString());
		//if(proc)
		//  	proc->setServiceRequest(r);
	  
	  	long reqId = atol(get_value(r,"REQ_ID",    0));
		long from  = atol(get_value(r,"SOURCE_REF",0));

		const char *x   = get_value(r,"WAITMODE",0);
		int sync  = x?atoi(x):0;

		qDebug() << "service" << name << ref << reqid << from << sync; 
		
		if(r->next)
		  	print_all_requests(r->next);
		
		//Info *p = Info::Find(reqid);
		//delete p;
		//if( r->next )  //-- Q&D, 000810/vk
		//    new Request(reqid,from,ref,r->next,sync);
	} 
	else if(mode == "EXIT")
	{
		qDebug() << "exit" << name << ref; 
		
		procModel_->dataIsAboutToChange();
		MvProcNode::remove(ref,name.toStdString());
	  	procModel_->updateData();
		procTree_->expandAll();
		adjustView();
		
		//Info *p = Info::Find(ref);
		//delete p;
	}
	else if(mode == "PROGRESS")
	{
		long reqid = atol(get_value(r,"REQ_ID",    0));
		
		//Info *p = Info::Find(reqid);
		qDebug() << "progress" << name << reqid; 
		
		//Info *p = Info::Find(reqid);
		//if(p) p->Update(r);
	}
	else if(mode == "FOLLOWUP")
	{
		long reqid = atol(get_value(r,"REQ_ID",    0));
		
		MvProcNode::assignProcRequest(reqid,ref,name.toStdString());
		
		qDebug() << "followup" << name << reqid; 
		
		//Info *p = Info::Find(reqid);
		//if(p) p->Update(ref);
	}
		
	cout << "<-----------------------" << endl;
	cout << endl;
	
	/*if(mode == _REPLY)
	{
		long reqid = atol(get_value(r,"REQ_ID",    0));
		Info *p = Info::Find(reqid);
		delete p;
	}
	else if(mode == _WAIT)
	{
		long reqid = atol(get_value(r,"REQ_ID",    0));
		long from  = atol(get_value(r,"SOURCE_REF",0));
		const char *to   = get_value(r,"TARGET",0);

		Info *p = Info::Find(reqid);
		delete p;
		new Wait(reqid,from,to,r->next);
	}
	else if(mode == _REGISTER)
	{
		Info *p = Info::Find(ref);
		delete p;
		new Service(ref,r);
	}
	else if(mode == _BUSY)
	{
		r = r->next;		
		long reqid = atol(get_value(r,"REQ_ID",    0));
		Info *p = Info::Find(reqid);
		delete p;
	}
	else if(mode == _SERVICE)
	{
		long reqid = atol(get_value(r,"REQ_ID",    0));
		long from  = atol(get_value(r,"SOURCE_REF",0));

		const char *x   = get_value(r,"WAITMODE",0);
		int sync  = x?atoi(x):0;

		Info *p = Info::Find(reqid);
		delete p;
		if( r->next )  //-- Q&D, 000810/vk
		    new Request(reqid,from,ref,r->next,sync);
	} 
	else if(mode == _EXIT)
	{
		Info *p = Info::Find(ref);
		delete p;
	}
	else if(mode == _PROGRESS)
	{
		long reqid = atol(get_value(r,"REQ_ID",    0));
		Info *p = Info::Find(reqid);
		if(p) p->Update(r);
	}
	else if(mode == _FOLLOWUP)
	{
		long reqid = atol(get_value(r,"REQ_ID",    0));
		Info *p = Info::Find(reqid);
		if(p) p->Update(ref);
	}*/

}

void ProcMonitor::adjustView()
{
 	if(firstUpdate_)
	{  	
	  	MvProcNode::updateInfo();
		
		//Adjust treeview column size to contents	
		for(int i=0; i < procModel_->columnCount()-1; i++)
	  		procTree_->resizeColumnToContents(i);	
		
		procTree_->setCurrentIndex(filterModel_->index(0,0));	
		
		firstUpdate_=false;
	}		 
}  

void ProcMonitor::startService()
{	
	QTimer *timer= new QTimer(this);
	timer->setInterval(3000);
	timer->start();

	connect(timer,SIGNAL(timeout()),
		this,SLOT(slotProcessService()));
		
}

void ProcMonitor::slotProcessService()
{
	MvProcNode::updateInfo();
}

void ProcMonitor::slotContextMenu(const QPoint& position)
{
	MvProcNode *proc=procModel_->itemForIndex(filterModel_->mapToSource(procTree_->indexAt(position)));
 	if(proc)
	{
	  	QList<QAction*> lst;
		
		QAction *infoAction=new QAction(tr("Information"),this);		
		lst << infoAction;
	  	
		QAction *reqAction=new QAction(tr("Request"),this);		
		lst << reqAction;
		MvProcRequest *r=proc->procRequest();
		if(!r)
		{
		  	reqAction->setEnabled(false);
		}
		
	  	QAction *abortAction=new QAction(tr("Abort"),this);
		abortAction->setShortcut(tr("Del"));
		abortAction->setIcon(QPixmap(":/debug/remove.svg"));
		
		lst << abortAction;
		
		QAction *ac=QMenu::exec(lst,procTree_->mapToGlobal(position),infoAction,this);
		if(ac == infoAction)
		{
			infoTab_->show();
			showInfo(proc);
			showRequest(proc);
			infoTab_->setCurrentIndex(0);
		}
		else if(ac == reqAction)
		{	
			infoTab_->show();
			showInfo(proc);
			showRequest(proc);
			infoTab_->setCurrentIndex(1);
		}
		else if(ac == abortAction)
		{
		  	proc->killProc();
		}  
	}
}  

void ProcMonitor::slotProcInfo(const QModelIndex& index)
{	
  	MvProcNode *proc=procModel_->itemForIndex(filterModel_->mapToSource(index));
  	showInfo(proc);
	showRequest(proc);
}  

void ProcMonitor::showInfo(MvProcNode *proc)
{
	if(!proc)
	  	procInfo_->clear();
	
	QString txt;
	txt+="<table>";
		
	//
	addInfoRow("Name",proc->name(),txt);
	addInfoRow("Full name",proc->niceName(),txt);
	addInfoRow("Host",proc->host(),txt);
	addInfoRow("User",proc->user(),txt);
	addInfoRow("Pid",proc->pidAsString(),txt);
	
	txt+="</tr></table>";
		
	procInfo_->setHtml(txt);	
}  

void ProcMonitor::showRequest(MvProcNode *proc)
{
	if(!proc)
	  	procReq_->clear();
	
	QString txt;
	txt+="<table>";
		
	//Request
	MvProcRequest *r=proc->procRequest();
	if(r)
	{
		//addTitleInfoRow(tr("Request"),txt,2);	
	  
	  	addInfoRow("Class",r->className(),txt);
		addInfoRow("Name",r->name(),txt);
		addInfoRow("Sender",r->source(),txt);
		addInfoRow("Receiver",r->target(),txt);
		addInfoRow("Request",r->reqStr(),txt);
	}
	
	txt+="</tr></table>";
		
	procReq_->setHtml(txt);	
}  


void ProcMonitor::addInfoRow(QString label,const string& val,QString &info)
{	
	info+="<tr><td class=\"first\">" + label + "</td><td>" + QString::fromStdString(val) + "</td></tr>";
}

void ProcMonitor::addTitleInfoRow(QString label,QString &info, int colSpan)
{
	info+="<tr><td class=\"title\" align=\"center\" colspan=\"" + QString::number(colSpan+1) + + "\">" + label + "</td></tr>";
}

void ProcMonitor::loadIcons(MvQPixmapCache *pm)
{
	request *r = mars.setup;

	while(r)
	{
		if(strcmp(r->name,"object") == 0)
		{
			const char *p = get_value(r,"class",0);
			const char *q = get_value(r,"pixmap",0);
			if(p && q)
			{
				pm->addMvIconPath(QString(p),QString(q));
			}
		}
		r = r->next;
	}
}


void ProcMonitor::writeSettings()
{
	QSettings settings("ECMWF","MV4-MvMonitor");
	
	//We have to clear it not to remember all the previous windows
	settings.clear();
	
	settings.beginGroup("main");
	settings.setValue("geometry",saveGeometry());
	settings.setValue("mainSplitter",mainSplitter_->saveState());
	settings.setValue("infoTab",infoTab_->isVisible());
	settings.endGroup();
}

void ProcMonitor::readSettings()
{
	QSettings settings("ECMWF","MV4-MvMonitor");
  	
	settings.beginGroup("main");
	restoreGeometry(settings.value("geometry").toByteArray());
	mainSplitter_->restoreState(settings.value("mainSplitter").toByteArray());
	if(settings.contains("infoTab"))	
		infoTab_->setVisible(settings.value("infoTab").toBool());
	else
	  	infoTab_->setVisible(false);
	
	settings.endGroup();	
}	