/***************************** 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 <iostream>
#include <sstream>
#include <fstream>
#include <cstdio>

#include "BufrMetaData.h"
#include "MvException.h"
#include "MvKeyProfile.h"

#include <LogHandler.h>

std::map<string,string> BufrMetaData::rdbKey_;

BufrSection::~BufrSection()
{
	for(vector<BufrSectionItem*>::iterator it=item_.begin(); it != item_.end(); it++)
	{
		delete *it;
	}
}


//=====================================
//
// BufrSectionDump
//
//=====================================

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

void BufrSectionDump::clear()
{
	for(vector<BufrSection*>::iterator it=section_.begin(); it != section_.end(); it++)
	{
		if(*it !=0)
			delete *it;
	}
	section_.clear();

	text_.clear();
}


bool BufrSectionDump::read(const string& fname, int msgCnt)
{
	LogItem *log=new LogItem;
	LogHandler::instance()->add(log);

	stringstream sst;
	sst << "Generating BUFR Section (0,1,2,3) dump for message: <b>" << msgCnt << "</b>";
	log->description(sst.str());
	log->method("BUFREX subroutines: BUPRS0, BUPRS1, BUPRS2, BUPRS3");

	MvObsSet oset(fname.c_str());
	MvObsSetIterator iter(oset);
	MvObs obs;

	bool found=false;
	while( (obs = iter()) )
	{
		if(iter.msgNumber() == msgCnt)
		{
			found=true;
			break;
		}
	}

	if(!found)
	{
		log->error("Message not found!");
		return false;
	}

	BufrSection *asec=0;
	ostringstream oss[4];

	//------------------------
	//  Section 0
	//------------------------

	asec = new BufrSection("Section 0");
	section_.push_back(asec);

	if(obs.printSection0(oss[0])== false)
	{
		log->error("Section 0 dump failed!");
		//clear();
		return false;
	}

	asec->setText(oss[0].str());

	//------------------------
	//  Section 1
	//------------------------

	asec = new BufrSection("Section 1");
	section_.push_back(asec);

        if(obs.printSection1(oss[1]) == false)
	{
		log->error("Section 1 dump failed!");
		//clear();
		return false;
	}

        asec->setText(oss[1].str());

	//------------------------
	//  Section 2
	//------------------------

	if(obs.hasSection2())
	{
                asec = new BufrSection("Section 2");
		section_.push_back(asec);

		if(obs.printSection2(oss[2])== false)
		{
            		log->error("Section 2 dump failed!");
			//clear();
			return false;
		}

		asec->setText(oss[2].str());
	}

	//------------------------
	//  Section 3
	//------------------------

	try
	{
		asec = new BufrSection("Section 3");
		section_.push_back(asec);

		if(obs.printSection3(oss[3]) == false)
		{
			log->error("Section 3 dump failed!");
			//clear();
			return false;
		}
       		asec->setText(oss[3].str());
	}
	catch(MvException e)
	{
		log->error("Section 3 dump failed!\n");
		log->error(e.what());

		return false;
	}

	return true;
}


//=====================================
//
// BufrDataDump
//
//=====================================

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

void BufrDataDump::clear()
{
	for(vector<BufrDataItem*>::iterator it=item_.begin(); it != item_.end(); it++)
	{
		if(*it !=0)
			delete *it;
	}
	item_.clear();

	text_.clear();
}


void BufrDataDump::read(const string& fname, int msgCnt,int subsetCnt)
{
	LogItem *log=new LogItem;
	LogHandler::instance()->add(log);

	stringstream sst;
	sst << "Generating BUFR data dump for message: <b>" << subsetCnt << "</b> and for subset: <b>" <<
                subsetCnt << "</b>";

	log->description(sst.str());
	log->method("BUFRX");

	MvObsSet oset(fname.c_str());
	MvObsSetIterator iter(oset);
	MvObs obs;

	bool found=false;
	while( (obs = iter()) )
	{
		if(iter.msgNumber() == msgCnt)
		{
			found=true;
			break;
		}
	}

	if(!found)
	{
		log->error("Message not found!");
		return;
	}

	if(subsetCnt >= 1 && subsetCnt <= obs.msgSubsetCount())
	{
		int index=1;
		while(index != subsetCnt && obs.Advance())
		{
			index++;
		}
	}

	bool dataToRead=obs.setFirstDescriptor();

	while(dataToRead)
	{
		BufrDataItem *item = new BufrDataItem;

		stringstream in;
		in.width(5);
		in.fill('0');
		in << obs.currentDescriptor();

		item->descriptor(in.str());
		item->value(obs.stringValue());
		item->unit(obs.unit());
		item->name(obs.name());

		item_.push_back(item);

		dataToRead=obs.setNextDescriptor();
	}
}

//=====================================
//
// BufrBitmapDump
//
//=====================================

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

void BufrBitmapDump::clear()
{
	/*for(vector<BufrDataItem*>::iterator it=item_.begin(); it != item_.end(); it++)
	{
		if(*it !=0)
			delete *it;
	}*/
	item_.clear();
}


void BufrBitmapDump::read(const string& fname, int msgCnt,int subsetCnt)
{
	LogItem *log=new LogItem;
	LogHandler::instance()->add(log);

	stringstream sst;
	sst << "Generating BUFR bitmap dump for message: <b>" << subsetCnt << "</b> and for subset: <b>" <<
                subsetCnt << "</b>";
	log->description(sst.str());
	log->method("BUFRX");

	MvObsSet oset(fname.c_str());
	MvObsSetIterator iter(oset);
	MvObs obs;

  	bool found=false;
	while( (obs = iter()) )
	{
		if(iter.msgNumber() == msgCnt)
		{
			found=true;
			break;
		}
	}

	if(!found)
	{
		log->error("Message not found!");
		return;
	}

	if(subsetCnt >= 1 && subsetCnt <= obs.msgSubsetCount())
	{
		int index=1;
		while(index != subsetCnt && obs.Advance())
		{
			index++;
		}
	}

	int rowNum;
	int dataColNum;
	obs.getBufrBoxSize(rowNum,dataColNum);

	colNum_=dataColNum+2;

	double val;
	string sval;
    	for(int i=0; i < rowNum; i++)
	{
		vector<string> txt(colNum_);

		txt[0]=obs.feedbackItemName(i+1);
		txt[1]=obs.feedbackItemUnit(i+1);

		for(int j=0; j < dataColNum; j++)
		{
			val=obs.feedbackValue(i+1,j+1);
			stringstream out;
			out << val;
			txt[j+2]=out.str();
		}
		item_.push_back(txt);
	}
}

//=====================================
//
// BufrMetaData
//
//=====================================

BufrMetaData::BufrMetaData()
{
	messageNum_=0;	
}

BufrMetaData::~BufrMetaData()
{

}
bool BufrMetaData::useMessageSizeForProgressIndicator()
{
	return true;;
}

int BufrMetaData::getEstimatedMessageNum()
{	
  	return messageNum_;
}

void BufrMetaData::getKeyList(string section,MvKeyProfile* prof)
{
	for(vector<MvKey*>::iterator it=allKey_->begin(); it != allKey_->end(); it++)
	{
		if((*it)->metaData("section") == section)
		{
			prof->addKey((*it)->clone());
		}
	}
}

void BufrMetaData::setFileName(string fname)
{
	fileName_=fname;
	messageNum_=0;
}


void BufrMetaData::loadKeyProfile(MvKeyProfile *prof)
{
        prof->clearKeyData();
        readMessages(prof);
}

int BufrMetaData::subsetNum(int msgCnt)
{
	MvObsSet oset(fileName_.c_str());
	MvObsSetIterator iter(oset);
	MvObs obs;

	bool found=false;
	while( (obs = iter()) )
	{
		if(iter.msgNumber() == msgCnt)
		{
			found=true;
			break;
		}
	}

	if(!found)
		return 0;

	return obs.msgSubsetCount();
}

void BufrMetaData::readMessages(MvKeyProfile *prof)
{
	TDynamicTime otime(19980922,0);
	MvObsSet oset(fileName_.c_str());
	MvObsSetIterator iter(oset);

	//iter.setTime(otime);
	//iter.setWmoStation(11520);

	MvObs obs;

	//int index=1;

	messageNum_=0;
	totalMessageNum_=0;
	int bytesRead=0;

   	while( (obs = iter()) )
	{
 		if(iter.subsetNumber() != 1)
		{
			continue;
		}
	
		map<string,string> section2;	
		bool section2Decoded=false;
		bool section2Available=false;

	       	for(unsigned int i=0; i< prof->size(); i++)
        	{

			string name = prof->at(i)->name();
            		string stmp;
			//name="originatingCentre";

			if(name == "MV_Index")
			{
				stringstream in;
				in << messageNum_+1;
				prof->at(i)->addValue(in.str().c_str());
			}

			else
			{
				int section;
				std::istringstream iss(prof->at(i)->metaData("section"));
  				iss >> section;

            			switch(section)
            			{

		        	case 0:
                			if(name == "totalLength")          
						prof->at(i)->addValue(intToString(obs.messageTotalLen()));
                			else if(name == "editionNumber")
						prof->at(i)->addValue(intToString(obs.editionNumber()));
					else
						prof->at(i)->addValue("N/A");
					break;

            			case 1:
                 		{
					if(name == "editionNumber")
						prof->at(i)->addValue(intToString(obs.editionNumber()));
					else if(name == "masterTable")
						prof->at(i)->addValue(intToString(obs.masterTable()));
					else if(name == "masterTableVersion")
						prof->at(i)->addValue(intToString(obs.masterTableVersion()));
					else if(name == "localTableVersion")
						prof->at(i)->addValue(intToString(obs.localTableVersion()));
					else if(name == "messageType")
						prof->at(i)->addValue(intToString(obs.messageType()));
					else if(name == "messageSubType")
						prof->at(i)->addValue(intToString(obs.messageSubtype()));
					else if(name == "originatingCentre")
						prof->at(i)->addValue(intToString(obs.originatingCentre()));
					else if(name == "hasSection2")
						prof->at(i)->addValue((obs.hasSection2()==true)?"y":"n");

					else if(name == "date")
					{
						TDynamicTime t=obs.msgTime(); 
						prof->at(i)->addValue(t.CharDate());		
					}
					else if(name == "time")
					{
						TDynamicTime t=obs.msgTime(); 
						prof->at(i)->addValue(t.CharHhMm());		
					}
					else if(name == "year")
					{
						TDynamicTime t=obs.msgTime();  
						string s=intToString(t.GetYear());
						prof->at(i)->addValue(s);		
					}
					else if(name == "month")
					{
						TDynamicTime t=obs.msgTime();  
						string s=intToString(t.GetMonth());
						prof->at(i)->addValue(s);		
					}
					else if(name == "day")
					{
						TDynamicTime t=obs.msgTime();  
						string s=intToString(t.GetDay());
						prof->at(i)->addValue(s);		
					}
					else if(name == "hour")
					{
						TDynamicTime t=obs.msgTime();  
						string s=intToString(t.GetHour());
						prof->at(i)->addValue(s);		
					}
					else if(name == "minute")
					{
						TDynamicTime t=obs.msgTime();  
						string s=intToString(t.GetMin());
						prof->at(i)->addValue(s);		
					}
					else
						prof->at(i)->addValue("N/A");
					break;
				}
            			case 2:
                 		{
					if(obs.originatingCentre() != 98)
					{
						prof->at(i)->addValue("N/A");
						break;
					}

					if(section2Decoded==false && obs.hasSection2())
					{       	      						
               					section2Available=obs.getDataFromSection2(section2);		
						section2Decoded=true;

					}

					if(!section2Available)
					{
						prof->at(i)->addValue("N/A");
						break;
					}

					if(name == "date:98")
					{
						if(section2.find("YEAR") != section2.end() &&
						   section2.find("MONTH") != section2.end() &&
						   section2.find("DAY") != section2.end())
						{
							string s=formatDate(section2["YEAR"],
								section2["MONTH"],
								section2["DAY"]);
							prof->at(i)->addValue(s);
						}
						else
						{	
							prof->at(i)->addValue("N/A");
						}	
		
					}
					else if(name == "time:98")
					{						
						if(section2.find("HOUR") != section2.end() &&
						   section2.find("MINUTE") != section2.end() &&
						   section2.find("SECOND") != section2.end())

						{
							string s=formatTime(section2["HOUR"],
								section2["MINUTE"],
								section2["SECOND"]);
							prof->at(i)->addValue(s);
						}
						else
						{	
							prof->at(i)->addValue("N/A");
						}
					}
					else
					{
						string s=prof->at(i)->metaData("dumpId");
						if(section2.find(s) != section2.end())
						{
							prof->at(i)->addValue(section2[s]);
						}
						else
						{	
							prof->at(i)->addValue("N/A");
						}
					}
					break;
				}
				case 3:
                 		{
					if(name == "msgSubsetCount")
						prof->at(i)->addValue(intToString(obs.msgSubsetCount()));

					else
						prof->at(i)->addValue("N/A");
					break;
				}
               			default:
                  			prof->at(i)->addValue("N/A");
			 		break;

                		}
            		}
		}
		messageNum_++;

		//observer_->messageScanStepChanged(messageNum_);
		bytesRead+=obs.messageTotalLen();
		observer_->messageScanStepChanged(bytesRead);
	}
	totalMessageNum_=messageNum_;
}

string BufrSection::itemValue(string itemName)
{
	for(int i=0; i < static_cast<int>(item_.size()); i++)
	{
		if(item_[i]->name() ==  itemName)
		{
			return item_[i]->value();
		}
	}
	return "N/A";
}


void BufrSection::setText(const string& txt)
{
	text_=txt;

	istringstream in(text_);
	string c;
	while(getline(in,c))
	{
		stringstream ss(c);
		string s, name;
		vector<string> sv;

		s=c;
		if(s.size() > 1 && s.find("BUFR SECTION") == string::npos )
		{
			while(ss >> s )
			{
				sv.push_back(s);
			}

			BufrSectionItem *item = new BufrSectionItem;

			for(unsigned int i=0; i< sv.size()-1; i++)
			{
				name.append(sv[i]);
				if(i != sv.size()-2)
					name.append(" ");
			}
			item->name(name);
			item->value(sv[sv.size()-1]);

			//cout << c << endl ;
                        addItem(item);
		}
	}
}

string BufrMetaData::intToString(int i)
{
	stringstream out;
	out << i;
	return out.str();
}

string BufrMetaData::formatDate(string y, string m, string d)
{
	string res=y;
	res.append((m.size() == 1)?"0" + m : m);
	res.append((d.size() == 1)?"0" + d : d);

	if(res.find("N/A") != string::npos)
		res="N/A";

	return res;
}

string BufrMetaData::formatTime(string h, string m, string s)
{
	string res=(h.size() == 1)?"0" + h : h;
	res.append((m.size() == 1)?"0" + m : m);
	res.append((s.size() == 1)?"0" + s : s);

	if(res.find("N/A") != string::npos)
		res="N/A";

	return res;
}
