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

#include <algorithm>

#include <QApplication>
#include <QDebug>
#include <QGraphicsSceneMouseEvent>
#include <QImage>
#include <QJsonArray>
#include <QMouseEvent>
#include <QPainter>
#include <QStyle>

#include "ObjectList.h"
#include "MvQJson.h"
#include "MvQMethods.h"
#include "MvQPlotView.h"
#include "MvQFeatureFactory.h"
#include "WsType.h"
#include "MvQPalette.h"
#include "MgQLayoutItem.h"
#include "MvLog.h"
#include "FontMetrics.h"


#include <cassert>

//#define MVQFEATUREICON_DEBUG_


//========================================
//
// MvQFeatureIconItem
//
//========================================

MvQFeatureImageItem::MvQFeatureImageItem(WsType* feature) :
    MvQFeatureItem(feature)
{
    Q_ASSERT(feature_);
    keepAspectRatio_ = true;

    auto s = feature_->sizeInPx();

    // the default size
    if (s.isEmpty() || s.width() == s.height()) {
        updateRect(30, 30);
    }
    else {
        Q_ASSERT(s.width() >= 0);
        square_ = false;
        hwRatio_ = static_cast<float>(s.height()) / static_cast<float>(s.width());
        updateRect(30, 30. * hwRatio_);
    }
}

void MvQFeatureImageItem::init(const QJsonObject& obj)
{
    QJsonObject g = obj["geometry"].toObject();
    auto gcLst = MvQJson::toPointFList(g["coordinates"].toArray(), g["type"].toString());
    if (!gcLst.isEmpty()) {
        geoCoord_ = gcLst[0];
        hasValidGeoCoord_ = isValidPoint(geoCoord_);
    }
    else {
        hasValidGeoCoord_ = false;
    }
    MvQFeatureItem::init(obj);
}

void MvQFeatureImageItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*o*/, QWidget* /*w*/)
{
    painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
    painter->drawPixmap(itemRect_, pix_, pix_.rect());
#ifdef MVQ_FEATURE_DEV_MODE_
    if (outOfView_) {
        auto pen = QPen(devModeColor_);
        painter->setBrush(Qt::NoBrush);
        painter->drawRect(itemRect_.adjusted(1, 1, -1, -1));
    }
#endif
}

void MvQFeatureImageItem::makePixmap()
{
    if (!shapeCol_.isValid()) {
        pix_ = feature_->pixmap(2 * itemRect_.width(), 2 * itemRect_.height());
    }
    else {
        pix_ = feature_->pixmap(2 * itemRect_.width(), 2 * itemRect_.height(), shapeCol_);
    }
}

void MvQFeatureImageItem::resize(QRectF targetRectInParent)
{
#ifdef MVQFEATUREICON_DEBUG_
    qDebug() << "MvQFeatureIconItem::resize";
    qDebug() << " pos:" << pos() << "itemRect:" << itemRect_ << "targetRectInParent:" << targetRectInParent;
#endif
    prepareGeometryChange();

    // the input rect is in parent coordinates. It defines the desired
    // boundingRect size
    QRectF targetRect = mapFromParent(targetRectInParent).boundingRect();

    // reduce the targetRect with the halo. We want to fit the itemRect into it!!!
    targetRect.adjust(halo(), halo(), -halo(), -halo());

    auto w = targetRect.width();
    auto h = targetRect.height();
    itemRect_ = QRectF(-w / 2., -h / 2., w, h);
#ifdef MVQFEATUREICON_DEBUG_
    qDebug() << " -> itemRect:" << itemRect_ << "targetRect:" << targetRect;
#endif
    bRect_ = itemRect_;
    setPos(targetRectInParent.center());
    makePixmap();
    adjustBRect();
    adjustGeoCoord();
    adjustSizeInReq();
}

void MvQFeatureImageItem::resizeTo(const MvQFeatureGeometry& geom)
{
    prepareGeometryChange();
    setPos(geom.pos_);
    itemRect_ = geom.itemRect_;
    bRect_ = itemRect_;
    makePixmap();
    adjustBRect();
    adjustGeoCoord();
    adjustSizeInReq();
}

void MvQFeatureImageItem::adjustSizeInReq()
{
    req_("WIDTH") = itemRect_.width();
    broadcastRequestChanged();
}

void MvQFeatureImageItem::updateRect(double width, double height)
{
    prepareGeometryChange();
    itemRect_ = QRectF(-width / 2., -height / 2., width, height);
    bRect_ = itemRect_;
}

void MvQFeatureImageItem::adjustBRect()
{
    if (fabs(halo() > 1E-5)) {
        prepareGeometryChange();
        bRect_ = itemRect_.adjusted(-halo(), -halo(), halo(), halo());
    }
}

double MvQFeatureImageItem::halo() const
{
    auto w = boxPen_.width();
    return (w <= 1) ? 0 : 1 + w / 2;
}

//========================================
//
// MvQFeatureStandardIconItem
//
//========================================

bool MvQFeatureStandardImageItem::getRequestParameters()
{
    if (!req_)
        return false;

    expandStyleRequest();

    std::string val;
    shapeCol_ = QColor();
    if (req_.getValue("COLOUR", val, true) && !val.empty()) {
        shapeCol_ = MvQPalette::magics(val);
    }

    // size
    req_.getValue("WIDTH", val);
    int iv = 0;
    try {
        iv = std::stoi(val);
    }
    catch (...) {
        iv = 0;
    }
    if (iv > 0 && iv != static_cast<int>(itemRect_.width())) {
        if (square_) {
            updateRect(iv, iv);
        }
        else {
            updateRect(iv, iv * hwRatio_);
        }
    }

    return true;
}

//========================================
//
// MvQFeatureCharIconItem
//
//========================================

void MvQFeatureCharImageItem::makePixmap()
{
    if (!shapeCol_.isValid()) {
        pix_ = feature_->pixmap(2 * itemRect_.width(), 2 * itemRect_.height());
    }
    else {
        pix_ = feature_->pixmapWithChar(2 * itemRect_.width(), 2 * itemRect_.height(), shapeCol_, letter_);
    }
}

bool MvQFeatureCharImageItem::getRequestParameters()
{
    //    MvLog().dbg() << MV_FN_INFO;

    Q_ASSERT(square_);

    if (!req_)
        return false;

    expandStyleRequest();

    std::string val;
    shapeCol_ = QColor();
    if (req_.getValue("COLOUR", val, true) && !val.empty()) {
        shapeCol_ = MvQPalette::magics(val);
    }

    // letter
    req_.getValue("CHARACTER", val);
    //    MvLog().dbg() << "letter=" << val;
    if (!val.empty()) {
        auto letter = QString::fromStdString(val).simplified();
        if (letter.size() >= 0 && letter_ != letter[0]) {
            letter_ = letter[0];
            makePixmap();
        }
    }

    // size
    req_.getValue("WIDTH", val);
    int iv = 0;
    try {
        iv = std::stoi(val);
    }
    catch (...) {
        iv = 0;
    }
    if (iv > 0 && iv != static_cast<int>(itemRect_.width())) {
        updateRect(iv, iv);
    }

    return true;
}

//========================================
//
// MvQFeatureWmoSymbolItem
//
//========================================

void MvQFeatureWmoSymbolItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*o*/, QWidget* /*w*/)
{
    if (boxBrush_.style() != Qt::NoBrush) {
        painter->fillRect(itemRect_, boxBrush_);
    }
    painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
    painter->drawPixmap(itemRect_, pix_, pix_.rect());

    if (boxPen_.style() != Qt::NoPen) {
        painter->setPen(boxPen_);
        painter->drawRect(itemRect_);
    }

#ifdef MVQ_FEATURE_DEV_MODE_
    if (outOfView_) {
        auto pen = boxPen_;
        if (boxPen_.style() == Qt::NoPen) {
            pen = QPen(devModeColor_);
        }
        else {
            pen.setColor(devModeColor_);
        }
        painter->setBrush(Qt::NoBrush);
        painter->drawRect(itemRect_);
    }
#endif
}

bool MvQFeatureWmoSymbolItem::getRequestParameters()
{
    Q_ASSERT(square_);

    if (!req_)
        return false;

    expandStyleRequest();

    std::string val;
    shapeCol_ = QColor();
    if (req_.getValue("SHAPE_COLOUR", val, true) && !val.empty()) {
        shapeCol_ = MvQPalette::magics(val);
    }

    // Set line parameters
    req_.getValue("LINE", val);
    if (val == "OFF") {
        boxPen_ = QPen(Qt::NoPen);
    }
    else {
        boxPen_ = MvQ::makePen((const char*)req_("LINE_STYLE"),
                               (int)req_("LINE_THICKNESS"),
                               (const char*)req_("LINE_COLOUR"),
                               Qt::SquareCap, Qt::MiterJoin);
    }

    // Set fill parameters
    req_.getValue("FILL", val);
    if (val == "OFF") {
        boxBrush_ = QBrush(Qt::NoBrush);
    }
    else {
        boxBrush_ = MvQ::makeBrush("SOLID",
                                   (const char*)req_("FILL_COLOUR"));
    }

    // size
    req_.getValue("WIDTH", val);
    int iv = 0;
    try {
        iv = std::stoi(val);
    }
    catch (...) {
        iv = 0;
    }
    if (iv > 0 && iv != static_cast<int>(itemRect_.width())) {
        updateRect(iv, iv);
    }

    return true;
}

static MvQFeatureMaker<MvQFeatureStandardImageItem> imgMaker("image");
static MvQFeatureMaker<MvQFeatureCharImageItem> charImgMaker("char_image");
static MvQFeatureMaker<MvQFeatureWmoSymbolItem> wmoMaker("wmo");
