// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
#include "NodeView/nodegroup.h"
#include "NodeView/connection.h"
#include "NodeView/fulllinesocket.h"
#include "NodeView/node.h"
#include "NodeView/nodeview.h"
#include "NodeView/socket.h"
#include "NodeView/widgetsocket.h"

#include <qglobal.h>
#if QT_VERSION >= 0x050000
#ifndef Qt5
#define Qt5
#endif
#else
#ifndef Qt4
#define Qt4
#endif
#endif

#include <QDebug>
#include <QGraphicsDropShadowEffect>
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
#include <QPushButton>
#include <QStyleOption>

/***********************Public Members***********************/

NodeGroup::NodeGroup( NodeView* graphWidget, QGraphicsItem* parent, QGraphicsScene* scene )
#ifdef Qt5
    : QGraphicsPolygonItem( parent )
    , m_graphWidget( graphWidget ) {
    Q_UNUSED( scene );
#else
    : QGraphicsPolygonItem( parent, scene )
    , m_graphWidget( graphWidget ) {
#endif

    setFlag( ItemIsMovable );
    setFlag( ItemSendsGeometryChanges );
    setFlag( ItemIsSelectable );
    setFlag( ItemIsFocusable );
    // setCacheMode(DeviceCoordinateCache);

    setZValue( INT_MIN );
    m_outlinePen = QPen( Qt::black );
    m_outlinePen.setWidth( 3 );
    m_selectedPen = QPen( Qt::darkGray );
    m_selectedPen.setWidth( 3 );
    m_backgroundBrush = QBrush( QColor( 200, 255, 200 ) );
    m_selectedBrush = m_backgroundBrush;
    m_titleBarBrush = m_backgroundBrush;
    m_titleBarBrush.setColor( m_titleBarBrush.color().darker( 125 ) );
    m_titleBarSelectedBrush = m_titleBarBrush;

    m_topCorner = QPointF( 0, 0 );
    m_mainRect = QRectF( 0, 0, 100, 100 );
    m_curPos = this->pos();

    QFont titleFont;
    // titleFont.setFamily("Arial");
    m_titleTextItem.setParentItem( this );
    m_titleTextItem.setFont( titleFont );
    m_titleText = "Title";

    m_titleJustify = TitleCenter;
    m_elidedTitle = Qt::ElideNone;

    this->updateBoundingRect();
}

NodeGroup::~NodeGroup() {}

void NodeGroup::addNode( Node* node ) {
    this->m_containedNodes.append( node );
    this->updateGroup();
    QObject::connect( node, SIGNAL( deletingNode( Node* ) ), this, SLOT( removeNode( Node* ) ) );
    QObject::connect( node, SIGNAL( itemUpdated() ), this, SLOT( updateGroup() ) );
}

QBrush NodeGroup::backgroundBrush() const { return this->m_backgroundBrush; }

QList<Node*> NodeGroup::containedNodes() { return this->m_containedNodes; }

Qt::TextElideMode NodeGroup::elidedTitle() { return this->m_elidedTitle; }

void NodeGroup::updateBoundingRect() {
    QFontMetrics* fm = new QFontMetrics( m_titleTextItem.font() );
    int height = fm->height();
    if( this->m_mainRect.width() > 0 ) {
        QRectF boundingRect = this->m_mainRect;
        boundingRect.setY( boundingRect.y() - height - 6 );
        m_boundingRect = boundingRect;
    } else {
        QRectF boundingRect = QRectF( this->m_topCorner.x(), this->m_topCorner.y() - 25, 50, 50 + height + 6 );
        m_boundingRect = boundingRect;
    }
}

QRectF NodeGroup::boundingRect() const { return m_boundingRect; }

QPen NodeGroup::outlinePen() const { return this->m_outlinePen; }

void NodeGroup::paint( QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget ) {
    Q_UNUSED( widget );
    Q_UNUSED( option );

    QFontMetrics* fm = new QFontMetrics( m_titleTextItem.font() );

    int width = 0;
    QString elidedTitle;
    if( this->m_mainRect.width() > 0 ) {
        elidedTitle = fm->elidedText( m_titleText, m_elidedTitle, m_mainRect.width() - 6 );
    } else {
        elidedTitle = fm->elidedText( m_titleText, m_elidedTitle, 50 );
    }
    m_titleTextItem.setText( elidedTitle );
    width = fm->width( elidedTitle );
    int left = 3;
    if( m_titleJustify == TitleCenter )
        left = this->shape().boundingRect().width() / 2 + 3 - width / 2;
    else if( m_titleJustify == TitleRight )
        left = this->shape().boundingRect().width() - width - 3;
    m_titleTextItem.setPos( this->shape().boundingRect().left() + left, this->shape().boundingRect().top() + 3 );

    if( isSelected() ) {
        painter->setPen( this->m_selectedPen );
        painter->setBrush( this->m_titleBarSelectedBrush );
    } else {
        painter->setPen( this->m_outlinePen );
        painter->setBrush( this->m_titleBarBrush );
    }
    // Draws the node on the screen.
    painter->drawPath( this->shape() );

    if( isSelected() ) {
        painter->setPen( this->m_selectedPen );
        painter->setBrush( this->m_selectedBrush );
    } else {
        painter->setPen( this->m_outlinePen );
        painter->setBrush( this->m_backgroundBrush );
    }

    QPainterPath path = QPainterPath();
    if( this->m_mainRect.width() > 0 )
        path.addRect( this->m_mainRect.x(), this->shape().boundingRect().bottom(), this->m_mainRect.width(),
                      this->m_mainRect.height() );
    else
        path.addRect( this->m_topCorner.x(), this->m_topCorner.y(), 50, 50 );
    // Draws the node on the screen.
    painter->drawPath( path );
}

QBrush NodeGroup::selectedBrush() const { return this->m_selectedBrush; }

QPen NodeGroup::selectedPen() const { return this->m_selectedPen; }

void NodeGroup::setBackgroundBrush( const QBrush& brush ) {
    this->m_backgroundBrush = brush;
    this->update();
}

void NodeGroup::setElidedTitle( Qt::TextElideMode mode ) { this->m_elidedTitle = mode; }

void NodeGroup::setOutlinePen( const QPen& pen ) {
    this->m_outlinePen = pen;
    this->update();
}

void NodeGroup::setSelectedBrush( const QBrush& brush ) {
    this->m_selectedBrush = brush;
    this->update();
}

void NodeGroup::setSelectedPen( const QPen& pen ) {
    this->m_selectedPen = pen;
    this->update();
}

void NodeGroup::setTitleBarBrush( const QBrush& brush ) {
    this->m_titleBarBrush = brush;
    this->update();
}

void NodeGroup::setTitleBarSelectedBrush( const QBrush& brush ) { this->m_titleBarSelectedBrush = brush; }

void NodeGroup::setTitleJustify( TitleJustify justify ) { this->m_titleJustify = justify; }

void NodeGroup::setTitleText( const QString& text ) {
    this->m_titleText = text;
    this->updateGroup();
}

void NodeGroup::setTitleTextBrush( const QBrush& brush ) {
    this->m_titleTextItem.setBrush( brush );
    this->update();
}

void NodeGroup::setTitleTextFont( const QFont& font ) {
    this->m_titleTextItem.setFont( font );
    this->updateGroup();
}

void NodeGroup::setTitleTextPen( const QPen& pen ) {
    this->m_titleTextItem.setPen( pen );
    this->updateGroup();
}

QPainterPath NodeGroup::shape() const {
    QPainterPath path;
    QFontMetrics fm = QFontMetrics( this->m_titleTextItem.font() );

    if( this->m_mainRect.width() > 0 )
        path.addRect( this->m_mainRect.x(), this->m_mainRect.y() - ( fm.height() + 6 ), this->m_mainRect.width(),
                      fm.height() + 6 );
    else
        path.addRect( this->m_topCorner.x(), this->m_topCorner.y() - ( fm.height() + 6 ), 50, fm.height() + 6 );
    return path;
}

QBrush NodeGroup::titleBarBrush() const { return this->m_titleBarBrush; }

QBrush NodeGroup::titleBarSelectedBrush() const { return this->m_titleBarSelectedBrush; }

NodeGroup::TitleJustify NodeGroup::titleJustify() const { return this->m_titleJustify; }

QString NodeGroup::titleText() const { return this->m_titleText; }

QBrush NodeGroup::titleTextBrush() const { return this->m_titleTextItem.brush(); }

QFont NodeGroup::titleTextFont() const { return this->m_titleTextItem.font(); }

int NodeGroup::type() const { return Type; }

void NodeGroup::updateArea() {
    this->prepareGeometryChange();
    QRectF workingRect;
    foreach( Node* contained, this->m_containedNodes ) {
        if( workingRect.isEmpty() ) {
            workingRect = this->mapFromItem( contained, contained->boundingRect() ).boundingRect();
        } else {
            workingRect =
                workingRect.united( this->mapFromItem( contained, contained->boundingRect() ).boundingRect() );
        }
    }
    if( workingRect.width() > 0 )
        this->m_topCorner = workingRect.topLeft();

    if( m_elidedTitle == Qt::ElideNone ) {
        QFontMetrics* fm = new QFontMetrics( m_titleTextItem.font() );
        int textWidth = fm->width( m_titleText );
        int rectWidth = workingRect.width();
        if( textWidth > rectWidth ) {
            int widthDiff = textWidth - rectWidth;
            int mod = int( ( widthDiff / 2.0 ) + 0.5 );
            workingRect.adjust( -mod, 0, mod, 0 );
        }
    }
    workingRect.adjust( -3, -3, 3, 3 );
    this->m_mainRect = workingRect;
}

void NodeGroup::mouseReleaseEvent( QGraphicsSceneMouseEvent* event ) {
    update();

    foreach( Node* node, this->m_containedNodes ) {
        node->snapToGrid();
    }

    QGraphicsItem::mouseReleaseEvent( event );
}

QVariant NodeGroup::itemChange( GraphicsItemChange change, const QVariant& value ) {
    QPointF movement;
    switch( change ) {
    case ItemPositionHasChanged:
        movement = value.toPointF() - this->m_curPos;
        foreach( Node* inner, this->m_containedNodes ) {
            inner->setPos( inner->pos() + movement );
        }
        this->m_curPos = value.toPointF();
        break;
    default:
        break;
    }

    return QGraphicsItem::itemChange( change, value );
}

void NodeGroup::mouseDoubleClickEvent( QGraphicsSceneMouseEvent* event ) {
    emit doubleClicked( event );
    QGraphicsItem::mouseDoubleClickEvent( event );
}

void NodeGroup::updateGroup() {
    this->updateArea();
    this->updateBoundingRect();
    this->update();
}

void NodeGroup::removeNode( Node* node ) {
    this->m_containedNodes.removeAll( node );
    this->updateGroup();
}