/***************************** 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 <QCloseEvent>
#include <QComboBox>
#include <QCursor>
#include <QDebug>
#include <QLabel>
#include <QSpinBox>
#include <QSplitter>
#include <QStatusBar>
#include <QString>
#include <QStringList>
#include <QToolButton>
#include <QVBoxLayout>

#include "BufrExaminer.h"

#include "BufrMetaData.h"
#include "MvKeyProfile.h"
#include "MvQFileInfoLabel.h"
#include "MvQAbout.h"
#include "MvQBufrDumpModel.h"

#include "MvQKeyManager.h"
#include "MvQKeyProfileModel.h"
#include "MvQKeyProfileTree.h" 
#include "MvQTreeViewSearchLine.h"
#include "MvQTreeView.h"

BufrExaminer::BufrExaminer(QWidget *parent) : MvQAbstractMessageExaminer("BufrExaminer",parent)
{
	setAttribute(Qt::WA_DeleteOnClose);
	
	setWindowTitle("Metview - Bufr Examiner");

	//Initial size
	setInitialSize(1100,800);
		
	//Init
	messageType_="BUFR";

	ignoreMessageSpinChangeSignal_=false;
	ignoreSubsetSpinChangeSignal_=false;
	messageCanBeDecoded_=false;

	sectionDumpModel_=0;
	dataDumpModel_=0;
	bitmapDumpModel_=0;

	//Set up goto panel
	setupGotoPanel();
		
	//Set up bufr dump boxes
	setupDumpBox();
	
	//Build ui
	
	QWidget *w;

	//----------------------
	// Main splitter
	//----------------------

	mainSplitter_= new QSplitter;
	mainSplitter_->setOrientation(Qt::Vertical);

        mainSplitter_->setOpaqueResize(false);
	setCentralWidget(mainSplitter_);
	
	//-----------------------------------------------------
	// 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);
	w=new QWidget;
	w->setLayout(mainLayout);
	mainSplitter_->addWidget(w);

	//------------------
	// File info label
	//------------------
      
 	//Add to layout 
	mainLayout->addWidget(fileInfoLabel_);
	mainLayout->addWidget(gotoPanel_);
	
	//---------------------
	// Central splitter
	//---------------------

	centralSplitter_ = new QSplitter(this); 
	centralSplitter_->setOpaqueResize(false);	
	mainLayout->addWidget(centralSplitter_,1);

	//-------------------------
	// Populate central splitter
	//-------------------------
	
	centralSplitter_->addWidget(messagePanel_);
	centralSplitter_->addWidget(dumpPanel_);
			
	//--------------------------
	// Find widget
	//--------------------------
	
	mainLayout->addWidget(findPanel_);

	//--------------------------
	// Log Area
	//--------------------------

	mainSplitter_->addWidget(logPanel_);
	mainSplitter_->setCollapsible(1,false);
	
	//----------------------------
	// Setup menus and toolbars
	//----------------------------

	setupMenus(menuItems_);
	
	//Status bar	
 	statusMessageLabel_ = new QLabel("");
    	statusMessageLabel_->setFrameShape(QFrame::NoFrame);
    	statusBar()->addPermanentWidget(statusMessageLabel_,1);  // '1' means 'please stretch me when resized'

	connect(this,SIGNAL(statusMessage(QString)),
		this,SLOT(slotStatusMessage(QString)));

	//-------------------------
	// Settings
	//-------------------------

	readSettings();
	
}
 
BufrExaminer::~BufrExaminer()
{	
	writeSettings();
	
	if(sectionDumpModel_) delete sectionDumpModel_;
	if(dataDumpModel_) delete dataDumpModel_;
	if(bitmapDumpModel_) delete bitmapDumpModel_;
}

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

void BufrExaminer::setupGotoPanel()
{
	QHBoxLayout *msgSpinLayout=new  QHBoxLayout;

	//Combo box for key profile selection
	QLabel *spinLabel = new QLabel(tr("Go to &message:"));
	messageSpin_ = new QSpinBox;
	spinLabel->setBuddy(messageSpin_);

	msgSpinLayout->addWidget(spinLabel);
	msgSpinLayout->addWidget(messageSpin_);
	msgSpinLayout->addSpacing(5);
	
	spinLabel = new QLabel(tr("Go to &subset:"));
	subsetSpin_ = new QSpinBox;
	spinLabel->setBuddy(subsetSpin_);

	msgSpinLayout->addWidget(spinLabel);
	msgSpinLayout->addWidget(subsetSpin_);
	
	subsetLabel_=new QLabel("");
	msgSpinLayout->addWidget(subsetLabel_);
	msgSpinLayout->addStretch(1);
	
	gotoPanel_=new QWidget;
	gotoPanel_->setLayout(msgSpinLayout);
}

void BufrExaminer::setupDumpBox()
{
	QWidget* w;
	QVBoxLayout *vb;
  
  	dumpPanel_=new QWidget;
	QVBoxLayout *dumpLayout = new QVBoxLayout;
	dumpLayout->setContentsMargins(0,0,0,0);
	dumpPanel_->setLayout(dumpLayout);	

	//Tab
	dumpTab_ = new QTabWidget;
	dumpLayout->addWidget(dumpTab_);

 	//--------------------------------
	// Section 0-3 dump
	//--------------------------------

	w=new QWidget;
	vb=new QVBoxLayout;
	vb->setContentsMargins(0,0,0,0);
	w->setLayout(vb);
	
	sectionDumpTree_= new MvQTreeView;
	sectionDumpModel_= new MvQBufrSectionDumpModel;
	sectionDumpTree_->setModel(sectionDumpModel_);
	sectionDumpTree_->setObjectName("sectionDumpTree");
	sectionDumpTree_->setProperty("mvStyle",1);
	sectionDumpTree_->setAlternatingRowColors(true);
	sectionDumpTree_->setAllColumnsShowFocus(true);
	sectionDumpTree_->setDragDropMode(QAbstractItemView::DragOnly);
	vb->addWidget(sectionDumpTree_);

	sectionDumpSearch_= new MvQTreeViewSearchLine(sectionDumpTree_,1,"");
	findPanel_->addSearchLineToRight(sectionDumpSearch_,w);
	
	dumpTab_->addTab(w,tr("Section 0-3"));

	//--------------------------------
	// Data dump
	//--------------------------------
	
	w=new QWidget;
	vb=new QVBoxLayout;
	vb->setContentsMargins(0,0,0,0);
	w->setLayout(vb);
	
	dataDumpTree_= new MvQTreeView;
	dataDumpModel_= new MvQBufrDataDumpModel;
	dataDumpTree_->setModel(dataDumpModel_);
	dataDumpTree_->setObjectName("dataDumpTree");
	dataDumpTree_->setProperty("mvStyle",1);
	dataDumpTree_->setAlternatingRowColors(true);
	dataDumpTree_->setAllColumnsShowFocus(true);
	dataDumpTree_->setRootIsDecorated(false);
	vb->addWidget(dataDumpTree_);

	dataDumpSearch_= new MvQTreeViewSearchLine(dataDumpTree_,1,"");
	findPanel_->addSearchLineToRight(dataDumpSearch_,w);

	//dataDumpTree_->setDragDropMode(QAbstractItemView::DragOnly);

	dumpTab_->addTab(w,tr("Data"));	

	//--------------------------------
	// Bitmap dump
	//--------------------------------

	w=new QWidget;
	vb=new QVBoxLayout;
	w->setLayout(vb);
	
	bitmapDumpTree_= new MvQTreeView;
	bitmapDumpModel_= new MvQBufrBitmapDumpModel;
	bitmapDumpTree_->setModel(bitmapDumpModel_);
	bitmapDumpTree_->setObjectName("bitmapDumpTree");
	bitmapDumpTree_->setProperty("mvStyle",1);
	bitmapDumpTree_->setAlternatingRowColors(true);
	bitmapDumpTree_->setAllColumnsShowFocus(true);
	bitmapDumpTree_->setRootIsDecorated(false);
	vb->addWidget(bitmapDumpTree_);
	
	bitmapDumpSearch_ = new MvQTreeViewSearchLine(bitmapDumpTree_,1,"");
	findPanel_->addSearchLineToRight(bitmapDumpSearch_,w);
	
	//dataDumpTree_->setDragDropMode(QAbstractItemView::DragOnly);

	dumpTab_->addTab(w,tr("Data, bitmaps expanded"));
	

	connect(dumpTab_,SIGNAL(currentChanged(int)),
		this,SLOT(slotCurrentDumpChanged(int)));
	
}
void BufrExaminer::initDumps()
{
	sectionDumpTree_->setFocus(Qt::OtherFocusReason);

	connect(messageSpin_, SIGNAL(valueChanged(int)),
                this, SLOT(slotMessageSpinChanged(int)));

	connect(subsetSpin_, SIGNAL(valueChanged(int)),
                this, SLOT(slotSubsetSpinChanged(int)));
}

void BufrExaminer::initAllKeys()
{
	//Get all keys
	MvKeyProfile *prof=new MvKeyProfile("Metview keys");
	prof->addKey(new MvKey("MV_Index","Index","Message index"));
	allKeys_ << prof;

	vector<MvKeyProfile*> allKeyProf;
	keyManager_->loadAllKeys(allKeyProf);
	for(vector<MvKeyProfile*>::iterator it=allKeyProf.begin(); it != allKeyProf.end(); it++)
	{
		allKeys_ << *it;
	}

	sectionDumpModel_->setAllKeys(allKeys_);
}

void BufrExaminer::loadKeyProfile(MvKeyProfile *prof)
{	
	MvQAbstractMessageExaminer::loadKeyProfile(prof);

	ignoreMessageSpinChangeSignal_=true;
	messageSpin_->setMinimum(1);
	messageSpin_->setMaximum(data_->messageNum());
	ignoreMessageSpinChangeSignal_=false;
}

void BufrExaminer::loadDumps()
{
	BufrMetaData *bufr=static_cast<BufrMetaData*>(data_);


	bool expandSectionDumpTree=false;
	QModelIndexList indexLst;
        QModelIndexList expandLst;

	//If the dumpData is not yet loaded 
	if(!sectionDumpModel_->dumpData())
	{
		expandSectionDumpTree=true;
	}
	else
	{
		//Save the expand state of the current sectionDumpTree
		indexLst=sectionDumpModel_->match(sectionDumpModel_->index(0,0),
			    Qt::DisplayRole,"*",-1,
		            Qt::MatchWildcard | Qt::MatchRecursive);
	
	
		foreach(QModelIndex index,indexLst)
		{
			if(sectionDumpTree_->isExpanded(index))
				expandLst << index;
		}	
	}

	ignoreMessageSpinChangeSignal_=true;
	messageSpin_->setValue(currentMessageNo_+1);	
	ignoreMessageSpinChangeSignal_=false;

	ignoreSubsetSpinChangeSignal_=true;
	int subsetNum=bufr->subsetNum(currentMessageNo_+1);
	int currentSubset=subsetSpin_->value();
	subsetSpin_->setMinimum(1);
	subsetSpin_->setMaximum(subsetNum);
	if(currentSubset >=  subsetNum)
	{
		subsetSpin_->setValue(1);
	}	
	ignoreSubsetSpinChangeSignal_=false;

	subsetLabel_->setText(tr("  (Number of subsets: ") + QString::number(subsetNum) + ")");

	messageCanBeDecoded_=false;

	//Generate and read bufr dumps
	loadSectionDump();
	loadDataDump();
	loadBitmapDump();	

	if(expandSectionDumpTree==true)
	{
		expandLst=sectionDumpModel_->match(sectionDumpModel_->index(0,0),
			    Qt::DisplayRole,"*",-1,
		            Qt::MatchWildcard | Qt::MatchRecursive);
	}
	
	//Try to restore the expand state of the sectionDumpTree
	foreach(QModelIndex index,expandLst)
	{
		if(sectionDumpModel_->hasChildren(index))
		{
			sectionDumpTree_->setExpanded(index,true);
		}
	}	

	for(int i=0;i < sectionDumpModel_->columnCount()-1; i++)
	{ 
		sectionDumpTree_->resizeColumnToContents(i);
	}
}

void BufrExaminer::loadSectionDump()
{	
	int msgCnt=currentMessageNo_+1;

	BufrSectionDump* currentDump=sectionDumpModel_->dumpData();

	BufrSectionDump* dump = new BufrSectionDump;
	
	messageCanBeDecoded_=dump->read(data_->fileName(),msgCnt);
	
	sectionDumpModel_->dataIsAboutToChange();
	sectionDumpModel_->setDumpData(dump);

	for(int i=0;i < sectionDumpModel_->columnCount()-1; i++)
	{ 
		sectionDumpTree_->resizeColumnToContents(i);
	}

	if(currentDump != 0)
		delete currentDump;

       	//sectionDumpBrowser_->setPlainText(QString(msg->text().c_str()));*/
}


void BufrExaminer::loadDataDump()
{	
	int msgCnt=currentMessageNo_+1;
	int subsetCnt=subsetSpin_->value();

	BufrDataDump* currentDump=dataDumpModel_->dumpData();

	BufrDataDump* dump = new BufrDataDump;

	if(messageCanBeDecoded_)
	{
		dump->read(data_->fileName(),msgCnt,subsetCnt);	
	}

	dataDumpModel_->dataIsAboutToChange();
	dataDumpModel_->setDumpData(dump);

	for(int i=0;i < dataDumpModel_->columnCount()-1; i++)
	{ 
		dataDumpTree_->resizeColumnToContents(i);
	}

	if(currentDump != 0)
		delete currentDump;


       	//sectionDumpBrowser_->setPlainText(QString(msg->text().c_str()));*/
}

void BufrExaminer::loadBitmapDump()
{	
	int msgCnt=currentMessageNo_+1;
	int subsetCnt=subsetSpin_->value();

	BufrBitmapDump* currentDump=bitmapDumpModel_->dumpData();

	BufrBitmapDump* dump = new BufrBitmapDump;
	if(messageCanBeDecoded_)
	{
		dump->read(data_->fileName(),msgCnt,subsetCnt);	
	}

	bitmapDumpModel_->dataIsAboutToChange();
	bitmapDumpModel_->setDumpData(dump);

	for(int i=0;i < bitmapDumpModel_->columnCount()-1; i++)
	{ 
		bitmapDumpTree_->resizeColumnToContents(i);
	}

	if(currentDump != 0)
		delete currentDump;

	bitmapDumpSearch_->slotUpdateColumns();

	//sectionDumpBrowser_->setPlainText(QString(msg->text().c_str()));*/
}

void BufrExaminer::slotMessageSpinChanged(int value)
{
	if(ignoreMessageSpinChangeSignal_)
		return;

	//Override cursor
	QApplication::setOverrideCursor(QCursor(Qt::BusyCursor));

	slotSelectMessage(value-1);
	
	QApplication::restoreOverrideCursor();

}

void BufrExaminer::slotSubsetSpinChanged(int /*value*/)
{
	if(ignoreSubsetSpinChangeSignal_)
		return;

	//Override cursor
	QApplication::setOverrideCursor(QCursor(Qt::BusyCursor));

	//Generate and read bufr dumps
	loadDataDump();
	loadBitmapDump();

	QApplication::restoreOverrideCursor();

}

void BufrExaminer::slotCurrentDumpChanged(int index)
{
	findPanel_->setCurrentSearchLineById(dumpTab_->widget(index));
} 
  
void BufrExaminer::slotShowAboutBox()
{
	MvQAbout about("BufrExaminer","", MvQAbout::MetviewVersion );
	about.exec();
}

void BufrExaminer::slotStatusMessage(QString s)
{
	statusMessageLabel_->setText(s);
}

void BufrExaminer::updateFileInfoLabel()
{
	fileInfoLabel_->setBufrTextLabel(QString(data_->fileName().c_str()),data_->totalMessageNum());
}

void BufrExaminer::writeSettings()
{
	QSettings settings("ECMWF","MV4-BufrExaminer");
	
	settings.beginGroup("mainWindow");
	settings.setValue("geometry",saveGeometry());
	settings.setValue("centralSplitter",centralSplitter_->saveState());
	settings.setValue("mainSplitter",mainSplitter_->saveState());
	settings.endGroup();
  
  	settings.beginGroup("BufrExaminer");
	settings.setValue("actionFileInfoStatus",actionFileInfo_->isChecked());
	settings.setValue("keyProfileName",keyCombo_->currentText());
	settings.setValue("dumpTabIndex",dumpTab_->currentIndex());
	settings.setValue("actionLogStatus",actionLog_->isChecked());
	settings.endGroup();
}

void BufrExaminer::readSettings()
{
	QVariant value;

	QSettings settings("ECMWF","MV4-BufrExaminer");
	
	settings.beginGroup("mainWindow");
	restoreGeometry(settings.value("geometry").toByteArray());
	centralSplitter_->restoreState(settings.value("centralSplitter").toByteArray());
	mainSplitter_->restoreState(settings.value("mainSplitter").toByteArray());
	settings.endGroup();

  	settings.beginGroup("BufrExaminer");

	QString txt; 
	int ival;

	if(settings.value("actionFileInfoStatus").isNull())
	{
		actionFileInfo_->setChecked(true);
	}
	else
	{
		actionFileInfo_->setChecked(settings.value("actionFileInfoStatus").toBool());
	}

	if(settings.value("actionLogStatus").isNull())
	{
		//Hidden by default
		actionLog_->setChecked(false);
	}
	else
	{
		actionLog_->setChecked(settings.value("actionLogStatus").toBool());
	}

	//Init key combo
	//It is empty at this stage!!
	savedKeyProfileName_=settings.value("keyProfileName").toString();
	/*for(int i=0; i < keyCombo_->count(); i++)
	{
		if(keyCombo_->itemText(i) == txt)
		{
			keyCombo_->setCurrentIndex(i);
			break;
		}
	}*/


	//Init tabs
	ival=settings.value("dumpTabIndex").toInt();
	if(ival == dumpTab_->currentIndex())
	{
		slotCurrentDumpChanged(ival);
	}	
	else
	{  
		dumpTab_->setCurrentIndex(ival);
	}
	
	settings.endGroup();
}
