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

#include "MvOdb.h"

MvQOdbVarModel::MvQOdbVarModel()
{
}

void MvQOdbVarModel::setData(QList<MvQOdbVar*>& data)
{
    data_ = data;

    //Reset the model (views will be notified)
    beginResetModel();
    endResetModel();
}


int MvQOdbVarModel::columnCount(const QModelIndex& /* parent */) const
{
    return 2;
}

int MvQOdbVarModel::rowCount(const QModelIndex& parent) const
{
    //Non-root
    if (parent.isValid()) {
        return 0;
    }
    //Root
    else {
        return data_.count();
    }
}


QVariant MvQOdbVarModel::data(const QModelIndex& index, int role) const
{
    if (!index.isValid() || role != Qt::DisplayRole) {
        return QVariant();
    }

    MvQOdbVar* var = data_[index.row()];
    return label(var, index.column());
}


QVariant MvQOdbVarModel::headerData(const int section, const Qt::Orientation orient, const int role) const
{
    if (orient != Qt::Horizontal || role != Qt::DisplayRole)
        return QAbstractItemModel::headerData(section, orient, role);

    switch (section) {
        case 0:
            return "Name";
        case 1:
            return "Value";
    }

    return QVariant();
}


QString MvQOdbVarModel::label(MvQOdbVar* var, const int column) const
{
    switch (column) {
        case 0: {
            return var->name();
        }
        case 1: {
            return var->value();
        }

        default:
            return QString();
    }
}

QModelIndex MvQOdbVarModel::index(int row, int column, const QModelIndex&) const
{
    if (data_.count() == 0 || row < 0 || column < 0) {
        return QModelIndex();
    }

    return createIndex(row, column, static_cast<void*>(0));
}


QModelIndex MvQOdbVarModel::parent(const QModelIndex&) const
{
    return QModelIndex();
}

//=========================================
//
// MvQOdbColumnModel
//
//=========================================

MvQOdbColumnModel::MvQOdbColumnModel(bool showTableColumn) :
    showTableColumn_(showTableColumn)
{
    typeMap_[MvQOdbColumn::None]     = "none";
    typeMap_[MvQOdbColumn::Int]      = "int";
    typeMap_[MvQOdbColumn::Float]    = "float";
    typeMap_[MvQOdbColumn::Double]   = "double";
    typeMap_[MvQOdbColumn::String]   = "string";
    typeMap_[MvQOdbColumn::Bitfield] = "bitfield";
}

void MvQOdbColumnModel::setData(const QList<MvQOdbColumn*>& data, MvQOdbMetaData::OdbVersion version)
{
    beginResetModel();
    data_       = data;
    odbVersion_ = version;
    endResetModel();
}


int MvQOdbColumnModel::columnCount(const QModelIndex& /* parent */) const
{
    if (odbVersion_ == MvQOdbMetaData::Version1) {
        if (showTableColumn_)
            return 3;
        else
            return 2;
    }
    else if (odbVersion_ == MvQOdbMetaData::Version2) {
        if (showTableColumn_)
            return 6;
        else
            return 5;
    }
    else
        return 0;
}

int MvQOdbColumnModel::rowCount(const QModelIndex& parent) const
{
    //Non-root
    if (parent.isValid()) {
        int id = parent.internalId();
        if (idToLevel(id) == 0) {
            return data_.at(id)->bitfieldNum();
        }
        else {
            return 0;
        }
    }
    //Root
    else {
        return data_.count();
    }
}


QVariant MvQOdbColumnModel::data(const QModelIndex& index, int role) const
{
    if (!index.isValid() || role != Qt::DisplayRole) {
        return QVariant();
    }

    int id = index.internalId();
    if (idToLevel(id) == 0) {
        MvQOdbColumn* col = data_.at(id);
        return label(col, index.column());
    }
    else if (idToLevel(id) == 1) {
        int parentRow             = idToParentRow(id);
        MvQOdbColumn* col         = data_.at(parentRow);
        MvQOdbBitfieldMember* bit = col->bitfield().at(index.row());
        return label(bit, index.column());
    }

    return QVariant();
}


QVariant MvQOdbColumnModel::headerData(const int section, const Qt::Orientation orient, const int role) const
{
    if (orient != Qt::Horizontal || role != Qt::DisplayRole)
        return QAbstractItemModel::headerData(section, orient, role);

    if (odbVersion_ == MvQOdbMetaData::Version1) {
        switch (section) {
            case 0:
                return tr("Name");
            case 1:
                return tr("Type");
            case 2:
                return (showTableColumn_) ? tr("Table") : QVariant();
            default:
                return QVariant();
        }
    }
    else if (odbVersion_ == MvQOdbMetaData::Version2) {
        switch (section) {
            case 0:
                return tr("Name");
            case 1:
                return tr("Type");
            case 2:
                return tr("Constant");
            case 3:
                return tr("Min");
            case 4:
                return tr("Max");
            //case 5:
            //	return tr("Missing value");
            case 5:
                return (showTableColumn_) ? tr("Table") : QVariant();
            default:
                return QVariant();
        }
    }

    return QVariant();
}


QVariant MvQOdbColumnModel::label(MvQOdbColumn* col, const int column) const
{
    if (odbVersion_ == MvQOdbMetaData::Version1) {
        switch (column) {
            case 0: {
                return col->name();
            }
            case 1: {
                return col->type();
            }
            case 2: {
                return (showTableColumn_) ? col->table() : QString();
            }
            default:
                return QString();
        }
    }

    else if (odbVersion_ == MvQOdbMetaData::Version2) {
        switch (column) {
            case 0: {
                return col->name();
            }
            case 1: {
                return col->type();
            }
            case 2: {
                return (col->isConstant()) ? QString("y") : QString("n");
            }
            case 3: {
                if (col->type() == "float")
                    return QString::number(col->min());
                else if (col->type() == "int")
                    return QString::number(static_cast<int>(col->min()));
                else
                    return QString("N/A");
            }
            case 4: {
                if (col->type() == "float")
                    return QString::number(col->max());
                else if (col->type() == "int")
                    return QString::number(static_cast<int>(col->max()));
                else
                    return QString("N/A");
            }
            //case 5:
            //{
            //	return ((col->type() =="float" || col->type() == "int") && col->hasMissingValue())?QString::number(col->missingValue()):QString("N/A");
            //}
            case 5: {
                return (showTableColumn_) ? col->table() : QString();
            }

            default:
                return QString();
        }
    }

    return QString();
}

QString MvQOdbColumnModel::label(MvQOdbBitfieldMember* bitfield, const int column) const
{
    switch (column) {
        case 0: {
            return bitfield->name();
        }
        case 1: {
            QString s1;
            s1.sprintf("%02d", bitfield->pos());

            QString str = "Pos: ";
            str += s1;
            str += "  Width: ";
            str += QString::number(bitfield->size());
            str += " bit";
            return str;
        }
        default:
            return QString();
    }
}

QModelIndex MvQOdbColumnModel::index(int row, int column, const QModelIndex& parent) const
{
    if (data_.count() == 0 || row < 0 || column < 0 || parent.column() > 3) {
        return QModelIndex();
    }

    //Parent is non-root -> level-1 items: id is the parent row (number+1)*10000
    if (parent.isValid()) {
        int id = (parent.row() + 1) * 10000;
        return createIndex(row, column, id);
    }
    //Parent is root -> level-0 items: id is the row number
    else {
        return createIndex(row, column, row);
    }
}


QModelIndex MvQOdbColumnModel::parent(const QModelIndex& index) const
{
    if (!index.isValid()) {
        return QModelIndex();
    }

    int id = index.internalId();
    if (idToLevel(id) == 0) {
        return QModelIndex();
    }
    else {
        int parentRow = idToParentRow(id);
        return createIndex(parentRow, 0, parentRow);
    }

    return QModelIndex();
}

int MvQOdbColumnModel::idToLevel(int id) const
{
    if (id >= 0 && id < 10000)
        return 0;
    else
        return 1;
}

int MvQOdbColumnModel::idToParentRow(int id) const
{
    if (idToLevel(id) == 0)
        return -1;
    else
        return id / 10000 - 1;
}


//=========================================
//
// MvQOdbDataModel
//
//=========================================

MvQOdbDataModel::MvQOdbDataModel() :
    hasTmpFilter_(false)
{
    data_ = 0;
}

void MvQOdbDataModel::dataIsAboutToChange()
{
    beginResetModel();
}

void MvQOdbDataModel::setData(MvAbstractOdb* data)
{
    data_ = data;
    data_->loadAllColumns();

    //sorting
    columnOrder_.clear();
    QMap<QString, int> cOrd;
    for (unsigned int i = 0; i < data_->columns().size(); i++) {
        QString str = QString::fromStdString(data_->columns().at(i)->name());
        cOrd[str]   = i;
    }

    QMapIterator<QString, int> it(cOrd);
    while (it.hasNext()) {
        it.next();
        columnOrder_ << it.value();
    }

    hasTmpFilter_ = false;

    //qDebug() << "model size:" << data_.count();
    //Reset the model (views will be notified)
    endResetModel();
}

int MvQOdbDataModel::columnCount(const QModelIndex& /* parent */) const
{
    if (data_ != 0)
        return data_->columnNum() + 1;
    else
        return 0;
}

int MvQOdbDataModel::rowCount(const QModelIndex& parent) const
{
    //Non-root
    if (parent.isValid()) {
        return 0;
    }
    //Root

    if (data_ != 0) {
        if (data_->chunkSize() == 0) {
            if (hasTmpFilter_) {
                return tmpFilter_.size();
            }
            else if (hasFilter()) {
                return filter_.size();
            }
            else {
                return data_->rowNum();
            }
        }
        else {
            return data_->nbRowsInChunk();
        }
    }
    else
        return 0;
}

QVariant MvQOdbDataModel::data(const QModelIndex& index, int role) const
{
    if (role != Qt::DisplayRole) {
        return QVariant();
    }

    if (!index.isValid() || index.column() < 0 || index.column() >= data_->columnNum() + 1) {
        return QVariant();
    }

    if (index.column() == 0) {
        if (data_->chunkSize() == 0)
            return index.row() + 1;
        else
            return data_->chunkSize() * data_->currentChunk() + index.row() + 1;
    }

    int dataColumn = index.column() - 1;
    if (dataColumn >= 0 && dataColumn < columnOrder_.count()) {
        dataColumn = columnOrder_[dataColumn];
    }


    const MvOdbColumn* col = data_->loadColumn(dataColumn);
    if (!col || col->isLoaded() == false)
        return QVariant();


    int dataRow = index.row();
    if (hasTmpFilter_) {
        Q_ASSERT(dataRow <= static_cast<int>(tmpFilter_.size()));
        dataRow = tmpFilter_[dataRow];
    }
    else if (hasFilter()) {
        dataRow = filter_[dataRow];
    }

    if (col->type() == MvOdbColumn::Int) {
        return col->intData().at(dataRow);
    }
    else if (col->type() == MvOdbColumn::Bitfield) {
        return static_cast<unsigned int>(col->floatData().at(dataRow));
    }
    else if (col->type() == MvOdbColumn::Float) {
        return col->floatData().at(dataRow);
    }
    else if (col->type() == MvOdbColumn::Double) {
        return col->doubleData().at(dataRow);
    }
    else if (col->type() == MvOdbColumn::String) {
        return QString::fromStdString(col->stringData().at(dataRow));
    }
    else if (col->type() != MvOdbColumn::None) {
        return QString::number(col->floatData().at(dataRow));
    }
    else {
        return QVariant();
    }
}

QVariant MvQOdbDataModel::headerData(const int section, const Qt::Orientation orient, const int role) const
{
    if (orient != Qt::Horizontal || (role != Qt::DisplayRole && role != Qt::ToolTipRole))
        return QAbstractItemModel::headerData(section, orient, role);

    if (section < 0 || (data_ && section >= static_cast<int>(data_->columns().size()) + 1))
        return QVariant();

    if (section == 0)
        return QString(tr("Row"));


    int dataColumn = section - 1;
    if (dataColumn >= 0 && dataColumn < columnOrder_.count()) {
        dataColumn = columnOrder_[dataColumn];
    }

    QString str = QString::fromStdString(data_->columns().at(dataColumn)->name());

    if (role == Qt::DisplayRole)
        return str.split("@").first();
    else if (role == Qt::ToolTipRole)
        return str;

    return QString();
}

QModelIndex MvQOdbDataModel::index(int row, int column, const QModelIndex& /*parent*/) const
{
    if (data_ == 0 || data_->rowNum() <= 0 || column < 0) {
        return QModelIndex();
    }

    return createIndex(row, column, static_cast<void*>(0));
}


QModelIndex MvQOdbDataModel::parent(const QModelIndex& /*index */) const
{
    return QModelIndex();
}

void MvQOdbDataModel::setFilter(const vector<int>& filter)
{
    if (filter.size() == 0 && filter_.size() == 0)
        return;

    if (!data_) {
        filter_       = filter;
        hasTmpFilter_ = false;
        tmpFilter_.clear();
    }

    else if (data_->chunkSize() == 0 && data_->rowNum() >= static_cast<int>(filter.size())) {
        beginResetModel();
        filter_       = filter;
        hasTmpFilter_ = false;
        tmpFilter_.clear();
        endResetModel();
    }
}

void MvQOdbDataModel::setTmpFilter(const vector<int>& tmpFilter)
{
    if (filter_.size() == 0)
        return;

    if (!data_) {
        hasTmpFilter_ = true;
        tmpFilter_    = tmpFilter;
    }
    else if (data_->chunkSize() == 0 && data_->rowNum() >= static_cast<int>(tmpFilter_.size())) {
        beginResetModel();
        hasTmpFilter_ = true;
        tmpFilter_    = tmpFilter;
        endResetModel();
    }
}

void MvQOdbDataModel::clearTmpFilter()
{
    if (hasTmpFilter_) {
        if (!data_) {
            hasTmpFilter_ = false;
            tmpFilter_.clear();
        }

        else if (data_->chunkSize() == 0 && data_->rowNum() >= static_cast<int>(filter_.size())) {
            beginResetModel();
            hasTmpFilter_ = false;
            tmpFilter_.clear();
            endResetModel();
        }
    }
}

bool MvQOdbDataModel::filtered() const
{
    return (data_ && data_->chunkSize() == 0 && (filter_.size() > 0 || tmpFilter_.size() > 0));
}

bool MvQOdbDataModel::hasFilter() const
{
    return (data_ && data_->chunkSize() == 0 && filter_.size() > 0);
}
