/***************************** 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 "netcdfcpp.h"

#include <math.h>

#include <QDebug>
#include <QFile>
#include <QTextStream>

#include "MvQObstatScatterData.h"

#include "MvKeyProfile.h"

MvQScatterItem::MvQScatterItem()
{
	nX_=0;
	nY_=0;
	data_=0;
}

MvQScatterItem::~MvQScatterItem()
{
	if(!data_)
	{
		delete data_;
	}
}

void MvQScatterItem::computeMeanForRows(QVector<float> &meanX,QVector<float> &meanY)
{
	for(int j=0; j < nY_; j++)
	{
		float sumX=0;
		float sumXWeigh=0.;
		for(int i=0; i < nX_; i++)
		{
			sumX+=data_[j+i*nY_];
			sumXWeigh+=(startX_+i*dX_)*data_[j+i*nY_];	
		}

		if(fabs(sumX) > 0.0001)
		{
			meanX << startY_+j*dY_;
			meanY << sumXWeigh/sumX;
		}
	}	
}

void MvQScatterItem::computeMeanForColumns(QVector<float> &meanX,QVector<float> &meanY)
{
	for(int i=0; i < nX_; i++)
	{
		float sumY=0;
		float sumYWeigh=0.;
		for(int j=0; j < nY_; j++)
		{
			sumY+=data_[j+i*nY_];
			sumYWeigh+=(startY_+j*dY_)*data_[j+i*nY_];	
		}

		if(fabs(sumY) > 0.0001)
		{
			meanX << startX_+i*dX_;
			meanY << sumYWeigh/sumY;
		}
	}
}

void MvQScatterItem::writeDataMatrixToNc(QString outFile,bool swapAxes)
{
	vector<float> x,y,value;

	/*for(int j=0; j < nY_; j++)
	{
		for(int i=0; i < nX_; i++)
		{
			value.push_back(data_[i+j*nX_]);	
		}
	}*/

	if(swapAxes == false)
	{
		for(int j=0; j < nY_; j++)
		{
			for(int i=0; i < nX_; i++)
			{
				value.push_back(data_[j+i*nY_]);	
			}
		}

		for(int i=0; i < nX_; i++)
		{
			x.push_back(startX_+(i+0.5)*dX_);
		}

		for(int j=0; j < nY_; j++)
		{
			y.push_back(startY_+(j+0.5)*dY_);
		}
	}
	else
	{
		for(int i=0; i < nX_; i++)
		{
			for(int j=0; j < nY_; j++)
			{
				value.push_back(data_[j+i*nY_]);	
			}
		}

		for(int i=0; i < nX_; i++)
		{
			y.push_back(startX_+(i+0.5)*dX_);
		}

		for(int j=0; j < nY_; j++)
		{
			x.push_back(startY_+(j+0.5)*dY_);
		}
	}	

	writeDataMatrixToNc(outFile.toStdString(),x,y,value);
}

void MvQScatterItem::writeDataMatrixToNc(string outFile,vector<float> &x, vector<float> &y, vector<float> &value)
{
int num, nx, ny;

	//Checking
	if(x.size() * y.size() != value.size() )
	{
		return;
	}


        //-------------------------
        // Netcdf output
        //-------------------------

	nx=x.size();
	ny=y.size();
        num=value.size();  
	 

        cout << "num: " << num << endl;

        // Now we build the Netcdf file!
        NcFile netcdf = NcFile(outFile.c_str(), NcFile::Replace);

        //Adding global attributes
        netcdf.add_att("Conventions","Metview");
        netcdf.add_att("institution","ECMWF, Reading, U.K.");
        netcdf.add_att("source","Metview 4");

        //Define dimensions (each column has the same number of rows!)
        NcDim* dim_nx = netcdf.add_dim("nx",nx);
        NcDim* dim_ny = netcdf.add_dim("ny",ny);

        //Define the netcdf vars
        map<int,NcVar*> nc_vars;
        nc_vars[0]=netcdf.add_var("x",ncFloat,dim_nx);
        nc_vars[1]=netcdf.add_var("y",ncFloat,dim_ny);
        nc_vars[2]=netcdf.add_var("val",ncFloat,dim_nx,dim_ny);

        //Write data into the netcdf file
        nc_vars[0]->put(&(x[0]),nx);
        nc_vars[1]->put(&(y[0]),ny);
        nc_vars[2]->put(&(value[0]),nx,ny);

        netcdf.close();           
}

MvQScatterBlock::MvQScatterBlock()
{


}

MvQScatterBlock::~MvQScatterBlock()
{
	foreach(MvQScatterItem *item,items_)
	{
		delete item;
	}
}

MvQObstatScatterData::MvQObstatScatterData()
{
	itemNum_=0;
	//filePath_="/var/tmp/user/metview/Local/OBSTAT/scat_0001_DCDA_synop_pressure.200809";
}

MvQObstatScatterData::~MvQObstatScatterData()
{
	clearData();
}

void MvQObstatScatterData::clearData()
{
	foreach(MvQScatterBlock* block, blocks_)
	{
		delete block;
	}
	blocks_.clear();
	itemNum_=0;
}

MvQScatterItem* MvQObstatScatterData::item(int index)
{
	int cnt=0;
	foreach(MvQScatterBlock *block, blocks_)
	{
		if(cnt + block->itemNum() > index)
		{
			int itemIndex=index-cnt;
			return block->items().at(itemIndex);
		}

		cnt+=block->itemNum();
	}

	return 0;
}

MvQScatterBlock* MvQObstatScatterData::blockOfItem(int index)
{
	int cnt=0;
	foreach(MvQScatterBlock *block, blocks_)
	{
		if(cnt + block->itemNum() > index)
		{			
			return block;
		}

		cnt+=block->itemNum();
	}

	return 0;
}

MvQScatterBlock* MvQObstatScatterData::block(int index)
{
	if(index >=0 && index < blocks_.count())
		return blocks_[index];

	return 0;
}

void MvQObstatScatterData::setFileName(QString fileName)
{
	fileName_=fileName;
	clearData();
	loadData();

	//QString nc="/var/tmp/user/metview/Local/OBSTAT/scat.nc";
	//blocks_[0]->items().at(0)->writeMatrixToNc(nc);
}

void MvQObstatScatterData::loadData()
{	
	QFile file(fileName_);

	if(file.open(QFile::ReadOnly | QFile::Text) == false)
	{
		return;
	}

	QTextStream in(&file);

	itemNum_=0;

	while (!in.atEnd()) 
	{
        	QString line = in.readLine();

		if(line.contains("#obstat scatter"))
		{
			line = in.readLine();
		}

		QStringList lst=line.split(" ",QString::SkipEmptyParts);
		qDebug() << line;
		qDebug() << lst;


		if(!lst.contains("HEADER=") || lst.count() != 11)
		{
			return;
		}

		MvQScatterBlock *block= new MvQScatterBlock;
		
		//Start date
		QString dstr=lst[1];
		dstr+=lst[2];
		block->setStartDate(QDateTime::fromString(dstr,"yyyyMMddHHmm"));
		
		//End date
		dstr=lst[3];
		dstr+=lst[4];
		block->setEndDate(QDateTime::fromString(dstr,"yyyyMMddHHmm"));
		
		//
		block->setSatId(lst[5].toInt());
		block->setSensorId(lst[6].toInt());
		int itemNum=lst[7].toInt();
		int channelNum=lst[8].toInt();
		int flagNum=lst[9].toInt();
		int areaNum=lst[10].toInt();
		//block->setItemNum(lst[7].toInt());
		//block->setChannelNum(lst[8].toInt());
		//block->setFlagNum(lst[9].toInt());
		//block->setAreaNum(lst[10].toInt());


		line = in.readLine();
		if(!line.contains("title="))
		{
			return;
		}

		lst=line.split("title=",QString::SkipEmptyParts);
		qDebug() << "title:" << lst << lst.last();
		if(lst.count() > 0)
		{	
			block->setTitle(lst.last().simplified());
		}
		qDebug() << "title:" << block->title();

		line = in.readLine();
		lst=line.split(" ",QString::SkipEmptyParts);		
		if(!lst.contains("press="))
		{
			return;
		}

		if(lst[1] == "yes")
		{
		
		}
		
		//X params		
		line = in.readLine();
		lst=line.split(" ",QString::SkipEmptyParts);
		qDebug() << line;
		qDebug() << lst;
		
		if(lst.count() != itemNum)
		{
			return;
		}
	
		QStringList paramsX=lst;

		//Y params	
		line = in.readLine();
		lst=line.split(" ",QString::SkipEmptyParts);
		if(lst.count() != itemNum)
		{
			return;
		}
	
		QStringList paramsY=lst;
	
		//Flags	
		line = in.readLine();
		lst=line.split(" ",QString::SkipEmptyParts);
		if(lst.count() != flagNum)
		{
			return;
		}

		QStringList flags=lst;

		//Areas
		line = in.readLine();
		lst=line.split(" ",QString::SkipEmptyParts);
		if(lst.count() != areaNum)
		{
			return;
		}

		block->setAreaNames(lst);
		
		//read titles
		QMap<int,QString> titles;
	
		for(int i=0; i < itemNum; i++)
		{
			line = in.readLine();
			titles[paramsX[i].toInt()]=line.simplified();

			line = in.readLine();
			titles[paramsY[i].toInt()]=line.simplified();
		}

		block->setParams(titles);

		int totalItemNum=itemNum*flagNum*areaNum*channelNum;
		
		for(int i=0; i < totalItemNum; i++)
		{
			//BEGIN SCATITEM
			line = in.readLine();
			lst=line.split(" ",QString::SkipEmptyParts);
			if(lst[0] != "BEGIN" && lst[1] != "SCATITEM" && lst.count() != 13)
			{
				return;
			}
			
			MvQScatterItem *item=new MvQScatterItem;

			item->setArea(lst[2].toInt());
			item->setLevel(lst[3].toInt());	
			item->setFlag(lst[4].toInt());	
			item->setParamX(lst[5].toInt());	
			item->setParamY(lst[6].toInt());			
			item->setStartX(lst[7].toFloat());			
			item->setStartY(lst[8].toFloat());	
			item->setDX(lst[9].toFloat());			
			item->setDY(lst[10].toFloat());	
			item->setNX(lst[11].toInt());			
			item->setNY(lst[12].toInt());		

			item->setTitleX(titles[item->paramX()]);
			item->setTitleY(titles[item->paramY()]);
			

			//GLOBAL STATS
			line = in.readLine();
			lst=line.split(" ",QString::SkipEmptyParts);
			if(lst[0] != "GLOBAL" && lst[1] != "STATS" && lst.count() != 13)
			{
				return;
			}

			item->setMaxX(lst[2].toFloat());
			item->setMinX(lst[3].toFloat());	
			item->setMaxY(lst[4].toFloat());
			item->setMinY(lst[5].toFloat());
			item->setSumX(lst[6].toFloat());
			item->setSumY(lst[7].toFloat());
			item->setSumXX(lst[8].toFloat());
			item->setSumYY(lst[9].toFloat());
			item->setSumXY(lst[10].toFloat());
			item->setSumDiffSqr(lst[11].toFloat());
			item->setSourceDataNum(lst[12].toInt());

			int num=0;
			int totalNum=item->nX()*item->nY(); 
			float *data = new float[totalNum];

			while(num != totalNum)
			{
				line = in.readLine();
				lst=line.split(" ",QString::SkipEmptyParts);
				foreach(QString s,lst)
				{
					data[num]=s.toFloat();
					num++;
				}
			}

			item->setData(data);
	
			block->addItem(item);	

			line = in.readLine();
			lst=line.split(" ",QString::SkipEmptyParts);
			if(!line.contains("END SCATITEM"))
			{
				return;
			}
			itemNum_++;
		}

		blocks_ << block;  
	}

	file.close();

}

void MvQObstatScatterData::loadKeyProfile(MvKeyProfile *prof)
{
	//prof->addKey(new MvKey("Index","Index"));	
	//prof->addKey(new MvKey("Satellite","Satellite"));
	//prof->addKey(new MvKey("Sensor","Sensor"));
	//prof->addKey(new MvKey("Area","Area"));
	//prof->addKey(new MvKey("Level","Level"));
	//prof->addKey(new MvKey("Flag","Flag"));
	//prof->addKey(new MvKey("ParX","ParX"));
	//prof->addKey(new MvKey("ParY","ParY"));
	prof->addKey(new MvKey("Start date","Start date"));
	prof->addKey(new MvKey("End date","End date"));
	prof->addKey(new MvKey("Satellite","Satellite"));
	prof->addKey(new MvKey("Sensor","Sensor"));
	prof->addKey(new MvKey("Title","Title"));

	int i=0;
	foreach(MvQScatterBlock* block, blocks_)
	{
		//foreach(MvQScatterItem* item, block->items())
		//{
			//prof->key("Index")->addValue(QString::number(i).toStdString());
		//	prof->key("Area")->addValue(QString::number(item->area()).toStdString());
		//	prof->key("Level")->addValue(QString::number(item->level()).toStdString());
		//	prof->key("Flag")->addValue(QString::number(item->flag()).toStdString());
		//	prof->key("ParX")->addValue(QString::number(item->paramX()).toStdString());
		//	prof->key("ParY")->addValue(QString::number(item->paramY()).toStdString());
			prof->key("Start date")->addValue(block->startDate().toString("yyyy-MM-dd hh:mm ").toStdString());
			prof->key("End date")->addValue(block->endDate().toString("yyyy-MM-dd hh:mm ").toStdString());
			prof->key("Satellite")->addValue(QString::number(block->satId()).toStdString());
			prof->key("Sensor")->addValue(QString::number(block->sensorId()).toStdString());
			prof->key("Title")->addValue(block->title().toStdString());
			i++;
		//}
	}	
}

void MvQScatterBlock::loadKeyProfile(MvKeyProfile *prof)
{
	//prof->addKey(new MvKey("Index","Index"));	
	//prof->addKey(new MvKey("Satellite","Satellite"));
	//prof->addKey(new MvKey("Sensor","Sensor"));
	prof->addKey(new MvKey("Area","Area"));
	prof->addKey(new MvKey("Level"," Channel/Level"));
	prof->addKey(new MvKey("Flag","Flag"));
	prof->addKey(new MvKey("ParamX","ParamX"));
	prof->addKey(new MvKey("ParamY","ParamY"));
	//prof->addKey(new MvKey("Start date","Start date"));
	//prof->addKey(new MvKey("End date","End date"));

	int i=0;
	foreach(MvQScatterItem* item, items_)
	{
		//prof->key("Index")->addValue(QString::number(i).toStdString());
		prof->key("Area")->addValue(areaNames_[item->area()-1].toStdString());
		prof->key("Level")->addValue(QString::number(item->level()).toStdString());
		prof->key("Flag")->addValue(QString::number(item->flag()).toStdString());
		prof->key("ParamX")->addValue(params_[item->paramX()].toStdString());
		prof->key("ParamY")->addValue(params_[item->paramY()].toStdString());
			//prof->key("Start date")->addValue(block->startDate().toString("yyyyMMdd-hh:mm").toStdString());
			//prof->key("End date")->addValue(block->endDate().toString("yyyyMMdd-hh:mm").toStdString());
		//prof->key("Satellite")->addValue(QString::number(satId_).toStdString());
		//prof->key("Sensor")->addValue(QString::number(sensorId_).toStdString());
		i++;
	}	
}
