/***************************** 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++;
    }
}
