/***************************** 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 <QDebug>
#include <QBuffer>
#include <QFile>
#ifdef METVIEW_QT5
 #include <QtXmlPatterns/QXmlQuery>
 #include <QtXmlPatterns/QXmlResultItems>
#else
 #include <QXmlQuery>
 #include <QXmlResultItems>
#endif

#include "MvQKeyManager.h"
#include "MvQXmlQuery.h"
#include "MvKeyProfile.h"

#include "mars.h"

QMap<MvQKeyManager::KeyType,QString> MvQKeyManager::keyTypeName_;

MvQKeyManager::MvQKeyManager(KeyType t,string fProfUser) : 
	fProfUser_(fProfUser), keyType_(t), systemProfOnly_(false), predefinedKeysOnly_(false)
{
	if(keyType_ != GribType  && keyType_ != BufrType && 
	   keyType_ != FrameType  && keyType_ != PlaceMarkType && 
           keyType_ != LayerType && keyType_ != BookmarkType)
	{
		marslog(LOG_WARN,"MvQKeyManager::MvQKeyManager --> No keyType specified!"); 
		return;
	}
	
	if(keyType_ == LayerType)
	{
		systemProfOnly_=true;
	}

	if(keyType_ == BufrType)
	{
		predefinedKeysOnly_=true;
	}
	if(keyTypeName_.isEmpty() == true)
	{
		keyTypeName_[GribType]="GRIB";
		keyTypeName_[BufrType]="BUFR";
		keyTypeName_[FrameType]="Frame";
		keyTypeName_[PlaceMarkType]="PlaceMark";
		keyTypeName_[LayerType]="Layer";
		keyTypeName_[BookmarkType]="Bookmark";
	}

	if(fProfUser_.empty())
	{
		QMap<KeyType,QString>  keyFileName;
		QString s;
		
		QString mvLinkDir;
		const char *mvLinkC=getenv("METVIEW_LINK_DIR");
		if (mvLinkC == 0)  
		{	
			marslog(LOG_WARN,"MvQKeyManager::MvQKeyManager --> No METVIEW_LINK_DIR found!");
		}
		else
		{
			mvLinkDir=QString(mvLinkC);
		}
		
		//System defaults
		keyFileName[GribType]="GribKeyProfile_default.xml";
		keyFileName[BufrType]="BufrKeyProfile_default.xml";
		keyFileName[FrameType]="FrameKeyProfile_default.xml";
		keyFileName[LayerType]="LayerKeyProfile_default.xml";
		//keyFileName[PlaceMarkType]="PlaceMarkKeyProfile_default.xml";

		if(keyFileName.contains(keyType_))
		{  				
			s=mvLinkDir;
			s+="/" + keyFileName[keyType_];		
			fProfDefault_=s.toStdString();
		}

		
		if(!systemProfOnly_)
		{
			//All keys
			keyFileName.clear();
			keyFileName[GribType]="GribKeyProfile_all.xml";
			keyFileName[BufrType]="BufrKeyProfile_all.xml";
			keyFileName[FrameType]="FrameKeyProfile_all.xml";

			if(keyFileName.contains(keyType_))
			{				
			  	s=mvLinkDir; 
				s+="/" + keyFileName[keyType_];		
				fProfAll_=s.toStdString();
			}
			
			//User settings
			keyFileName.clear();
			keyFileName[GribType]="GribKeyProfile.xml";
			keyFileName[BufrType]="BufrKeyProfile.xml";
			keyFileName[FrameType]="FrameKeyProfile.xml";
			keyFileName[PlaceMarkType]="PlaceMarkKeyProfile.xml";
			keyFileName[BookmarkType]="Bookmarks.xml";			
	
			if(keyFileName.contains(keyType_))
			{
				QString mvUserDir;
				const char *mvUserC=getenv("METVIEW_USER_DIRECTORY");
				if (mvUserC == 0)  
				{	
					marslog(LOG_WARN,"MvQKeyManager::MvQKeyManager --> No METVIEW_USER_DIRECTORY found!");
				}
				else
				{
					mvUserDir=QString(mvUserC);
				}	
			
				s=mvUserDir;  
				s+="/System/Preferences/" + keyFileName[keyType_];		
				fProfUser_=s.toStdString();
			}	
		}
	}
}	

MvQKeyManager::MvQKeyManager(const MvQKeyManager &copy) 
{
	fProfUser_=copy.fProfUser_;
	fProfDefault_=copy.fProfDefault_;
	fProfAll_=copy.fProfAll_;
	keyType_=copy.keyType_;
	systemProfOnly_=copy.systemProfOnly_;
	predefinedKeysOnly_=copy.predefinedKeysOnly_;
	for(vector<MvKeyProfile*>::const_iterator it=copy.data().begin(); it != copy.data().end(); it++)
	{
		data_.push_back((*it)->clone());
	}	
}

MvQKeyManager::~MvQKeyManager()
{
	clear();
}

MvQKeyManager* MvQKeyManager::clone()
{
	MvQKeyManager* cp=new MvQKeyManager(*this);
	return cp;
}

void MvQKeyManager::clear()
{
	for(vector<MvKeyProfile*>::iterator it=data_.begin(); it != data_.end(); it++)
	{
		delete (*it);
		(*it)=0;
	}

	data_.clear();
}

void MvQKeyManager::update(MvQKeyManager* m)
{
	clear();
	for(vector<MvKeyProfile*>::const_iterator it=m->data().begin(); it != m->data().end(); it++)
	{
		data_.push_back((*it)->clone());
	}	
}

MvKeyProfile* MvQKeyManager::addProfile(string name)
{
	MvKeyProfile *profile=new MvKeyProfile(name);
	createDefaultProfile(profile);
	data_.push_back(profile);
	return profile;

}

void MvQKeyManager::addProfile(MvKeyProfile *profile)
{
	data_.push_back(profile);
}

MvKeyProfile* MvQKeyManager::addProfile(string name,MvKeyProfile* oriProf)
{
	MvKeyProfile *newProf=new MvKeyProfile(name);
	data_.push_back(newProf);

	for(unsigned int i=0; i < oriProf->size(); i++)
	{
		MvKey *key=oriProf->at(i);
		MvKey *newKey=new MvKey(key->name(),key->shortName(),key->description());
		newKey->setReadIntAsString(key->readIntAsString());
		
		for(map<string,string>::const_iterator it=key->metaData().begin(); it != key->metaData().end(); it++)
		{		
			newKey->setMetaData(it->first,it->second);
		}
		newProf->addKey(newKey);
	}

	return newProf;

}


MvKeyProfile* MvQKeyManager::addProfile(string name,MvKeyProfile* oriProf,KeyType /*oriKeyType*/)
{
  
  	MvKeyProfile *newProf=new MvKeyProfile(name);
	data_.push_back(newProf);

	for(unsigned int i=0; i < oriProf->size(); i++)
	{
		MvKey *key=oriProf->at(i);
		
		string keyName=key->name();
		string desc=key->description();
		if(keyType_ == FrameType)
		{
		  	//Need a better solution!!!
			if(keyName == "MV_Index")
			{
			  	keyName="MV_Frame";
				desc="Frame index";
			}
		}		
		
		MvKey *newKey=new MvKey(keyName,key->shortName(),desc);
		newKey->setReadIntAsString(key->readIntAsString());
		
		for(map<string,string>::const_iterator it=key->metaData().begin(); it != key->metaData().end(); it++)
		{		
			newKey->setMetaData(it->first,it->second);
		}
		newProf->addKey(newKey);
	}

	return newProf;

}  

void MvQKeyManager::changeProfile(string name,MvKeyProfile* prof)
{
	if(!prof || prof->systemProfile())
		return;

	for(vector<MvKeyProfile*>::iterator it=data_.begin(); it != data_.end(); it++)
	{
		if((*it)->name() == name)
		{
			delete *it;
			*it = prof->clone();
		}
	}	
}

void MvQKeyManager::deleteProfile(int index)
{
	vector<MvKeyProfile*>::iterator it=data_.begin()+index;

	if((*it)->systemProfile())
		return;

	delete (*it);
	data_.erase(it);	
}	

MvKeyProfile* MvQKeyManager::findProfile(string name)
{	
	for(vector<MvKeyProfile*>::iterator it=data_.begin(); it != data_.end(); it++)
	{
		if((*it)->name() == name)
		{
			return *it;
		}
	}	

	return 0;
}


void MvQKeyManager::loadProfiles()
{
	MvKeyProfile *firstSysProf=0;
	loadProfiles(fProfDefault_,data_);
	for(vector<MvKeyProfile*>::iterator it=data_.begin(); it != data_.end(); it++)
	{
		(*it)->setSystemProfile(true);
		if(!firstSysProf)
		{
			firstSysProf=*it;
		}	
	}	

	//If there are no user profiles
	if(!systemProfOnly_ && loadProfiles(fProfUser_,data_) == false)
	{
		//If there are no system profiles
		if(data_.size() == 0)
		{
			addProfile("Default");
			//createDefaultProfile(prof);
		}
		//If there is at least one system profile we create a 
		//copy of it and add it to the profile list as "Default" and
		//make it editable
		else if(firstSysProf)
		{
			MvKeyProfile* p=firstSysProf->clone();
			p->setSystemProfile(false);
			p->setName("Default");
			data_.push_back(p);
		}	
		saveProfiles();	
	}
}	
		
void MvQKeyManager::loadAllKeys(vector<MvKeyProfile*> &target)
{
	if(systemProfOnly_)
		return; 
	
	loadProfiles(fProfAll_,target);
}

bool MvQKeyManager::loadProfiles(string fileName,vector<MvKeyProfile*> &target)
{	
  	if(fileName.empty())
	  	return false;
  
  	QFile file(QString::fromStdString(fileName));

	//For bufr we corrently do not load/save profiles!!!
	if(file.open(QFile::ReadOnly | QFile::Text) == false)
	{
		marslog(LOG_WARN,"MvQKeyManager::loadProfiles --> Cannot find profile file: %s ",fileName.c_str());
		return false;
	}

	QString xmlConf(file.readAll());
	file.close();

	//qDebug() << xmlConf;

	QXmlResultItems result;

	QByteArray ba=xmlConf.toUtf8();
	QBuffer buffer(&ba) ; // This is a QIODevice.
    	buffer.open(QIODevice::ReadOnly);

	MvQXmlQuery query;
     	query.bindVariable("myDoc", &buffer);
	query.setQuery("doc($myDoc)//Profile");
	query.evaluateTo(&result);
	
	//Profile nodes
	QXmlItem item(result.next());
     	while (!item.isNull()) 
	{
         	if (item.isNode()) 
		{		
			QXmlResultItems keyResult;
			QString name;

			//QXmlQuery query;
			query.setFocus(item);

			//Name of profile node
			query.setQuery("string(./@name)");
			query.evaluateTo(&name);
			name=name.simplified();

			//Create new profile
			MvKeyProfile *prof=new MvKeyProfile(name.toStdString());	
			target.push_back(prof);

			query.setQuery("./Key");
			query.evaluateTo(&keyResult);

			//Key nodes
			QXmlItem keyItem(keyResult.next());
     			while (!keyItem.isNull()) 
			{
        			if (keyItem.isNode()) 
				{		
					QString keyName, keyShortName, keyDesc, keyEditable="true";

					QMap<QString,QString> att;
					query.getAttributes(keyItem,att);
					
					if(att.contains("name"))
						keyName=att["name"];

					if(att.contains("shortName"))
						keyShortName=att["shortName"];

					if(att.contains("desc"))
						keyDesc=att["desc"];

					if(att.contains("editable"))
						keyEditable=att["editable"];
					
					MvKey *key=new MvKey(keyName.toStdString(),
							keyShortName.toStdString(),
							keyDesc.toStdString());

					if(keyEditable == "false" || keyEditable == "0" || 
					   predefinedKeysOnly_)
					{
						key->setEditable(false);
					}
					
					prof->push_back(key);
				
					//Parse data tags!
					QXmlResultItems dataResult;
					query.setFocus(keyItem);
					query.setQuery("./Data");
					query.evaluateTo(&dataResult);

					QXmlItem dataItem(dataResult.next());
     					while (!dataItem.isNull()) 
					{
        					if(dataItem.isNode()) 
						{		
							QMap<QString,QString> dataAtt;
							query.getAttributes(dataItem,dataAtt);				

							QMapIterator<QString,QString> it(dataAtt);
							while(it.hasNext())
							{
								it.next();
				
								key->setMetaData(it.key().toStdString(),
									it.value().toStdString());
							}
						}
						dataItem = dataResult.next();
					}
				}

				keyItem = keyResult.next();
			}
                 }

		item = result.next();
        }

	buffer.close();

	return true;
}

void MvQKeyManager::saveProfiles()	
{
	//Open file for writing
	QFile out(fProfUser_.c_str());
	if(out.open(QFile::WriteOnly | QFile::Text) == false)
	{
		marslog(LOG_WARN,"MvQKeyManager::saveProfiles --> Cannot create profile file: %s ",fProfUser_.c_str());
		return;
	}

	QString s;

	s="<MetviewKeyProfile type=\"" + keyTypeName_[keyType_] + "\">\n";
	out.write(s.toUtf8());

	for(vector<MvKeyProfile*>::iterator it=data_.begin(); it != data_.end(); it++)
	{
		MvKeyProfile *prof = *it;


		//We do not save system profiles into the user profile file
		if(prof->systemProfile())
			continue;


		s="\t<Profile name=\"" + QString(prof->name().c_str())  + "\">\n";
		out.write(s.toUtf8());

		for(vector<MvKey*>::iterator it1=prof->begin(); it1 != prof->end(); it1++)
		{
			MvKey *key= *it1;
			s="\t\t<Key name=\"" + QString::fromStdString(key->name()) + "\"" + 
			    " shortName=\"" + QString::fromStdString(key->shortName()) + "\"" +
			    " desc=\"" + QString::fromStdString(key->description()) + "\"";
			   
			out.write(s.toUtf8());	

			if(key->metaData().size() >0)
			{
				s=">\n";
				out.write(s.toUtf8());
				
				for(map<string,string>::const_iterator it2=key->metaData().begin(); 
						it2 != key->metaData().end(); it2++)
				{	
 					s="\t\t\t<Data " +  QString::fromStdString(it2->first) + "=\"" 
						+ QString::fromStdString(it2->second) + "\"/>\n";
					out.write(s.toUtf8());	
				}

				s="\t\t</Key>\n";
				out.write(s.toUtf8());	
			}
			else
			{
				s="/>\n";
				out.write(s.toUtf8());
			}
			
		}
		s="\t</Profile>\n";
		out.write(s.toUtf8());	
	}

	s="</MetviewKeyProfile>";	
	out.write(s.toUtf8());

	out.close();
}

void  MvQKeyManager::createDefaultProfile(MvKeyProfile *prof)
{
	if(keyType_ == BookmarkType)
		return;
  
  	if(keyType_ == FrameType)
	{
		prof->addKey(new MvKey("MV_Frame","Frame","Frame index"));
	}
	else
	{	
 		prof->addKey(new MvKey("MV_Index","Index","Message index"));
	}	
} 


QString MvQKeyManager::findUniqueProfileName(QString profName)
{
	int cnt=0;
	for(vector<MvKeyProfile*>::const_iterator it=data_.begin(); it != data_.end(); it++)
	{
		QString qsName((*it)->name().c_str());

		if(qsName == profName)
		{
			if(cnt==0)
				cnt=1;	
		}
		else if(qsName.startsWith(profName + " ("))
		{
			QStringList lst=qsName.split("(");
			if(lst.count()==2)
			{
				lst=lst[1].split(")");
				if(lst.count()==2)
				{
					int n=lst[0].toInt();
					if(n >= cnt)
						cnt=n+1;
				}								
			}
		}
	}

	if(cnt > 0)
		return profName + " (" + QString::number(cnt) + ")";
	else
		return profName;
	
}
