/******************************************************************************
 * QSkinny - Copyright (C) The authors
 *           SPDX-License-Identifier: BSD-3-Clause
 *****************************************************************************/

#include "QskSwitchButtonSkinlet.h"
#include "QskSwitchButton.h"

static inline qreal qskEffectivePosition( const QskSwitchButton* switchButton )
{
    auto pos = switchButton->positionHint( QskSwitchButton::Handle );
    pos = qBound( 0.0, pos, 1.0 );

    if( switchButton->isInverted() )
        pos = 1.0 - pos;

    if ( switchButton->orientation() == Qt::Horizontal )
    {
        if( switchButton->layoutMirroring() )
            pos = 1.0 - pos;
    }

    return pos;
}

QskSwitchButtonSkinlet::QskSwitchButtonSkinlet( QskSkin* skin )
    : Inherited( skin )
{
    setNodeRoles( { GrooveRole, HandleRole, RippleRole } );
}

QskSwitchButtonSkinlet::~QskSwitchButtonSkinlet()
{
}

QRectF QskSwitchButtonSkinlet::subControlRect( const QskSkinnable* skinnable,
    const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
{
    using Q = QskSwitchButton;

    if ( subControl == Q::Handle )
    {
        return handleRect( skinnable, contentsRect );
    }

    if ( subControl == Q::Groove )
    {
        return grooveRect( skinnable, contentsRect );
    }

    if ( subControl == Q::Ripple )
    {
        return rippleRect( skinnable, contentsRect );
    }


    return Inherited::subControlRect( skinnable, contentsRect, subControl );
}

QSizeF QskSwitchButtonSkinlet::sizeHint( const QskSkinnable* skinnable,
    Qt::SizeHint which, const QSizeF& ) const
{
    if ( which != Qt::PreferredSize )
        return QSizeF();

    auto grooveHint = skinnable->strutSizeHint( QskSwitchButton::Groove );
    auto handleHint = skinnable->strutSizeHint( QskSwitchButton::Handle );
    auto rippleHint = skinnable->strutSizeHint( QskSwitchButton::Ripple );

    auto hint = grooveHint;
    hint = hint.expandedTo( rippleHint );
    hint = hint.expandedTo( handleHint );

    return hint;
}

QSGNode* QskSwitchButtonSkinlet::updateSubNode( const QskSkinnable* skinnable,
    quint8 nodeRole, QSGNode* node ) const
{
    using Q = QskSwitchButton;

    switch ( nodeRole )
    {
        case RippleRole:
            return updateBoxNode( skinnable, node, Q::Ripple );

        case HandleRole:
            return updateBoxNode( skinnable, node, Q::Handle );

        case GrooveRole:
            return updateBoxNode( skinnable, node, Q::Groove );
    }

    return Inherited::updateSubNode( skinnable, nodeRole, node );
}

QRectF QskSwitchButtonSkinlet::grooveRect(
    const QskSkinnable* skinnable, const QRectF& contentsRect ) const
{
    using Q = QskSwitchButton;

    const auto switchButton = static_cast< const Q* >( skinnable );

    auto size = skinnable->strutSizeHint( Q::Groove );

    if ( switchButton->orientation() == Qt::Vertical )
    {
        if ( size.height() < 0.0 )
        {
            const auto handleSize = skinnable->strutSizeHint( Q::Handle );
            size.setHeight( 2 * handleSize.height() );
        }
    }
    else
    {
        if ( size.width() < 0.0 )
        {
            const auto handleSize = skinnable->strutSizeHint( Q::Handle );
            size.setWidth( 2 * handleSize.width() );
        }
    }

    size = size.expandedTo( QSize( 0.0, 0.0 ) );

    QRectF r;
    r.setSize( size );
    r.moveCenter( contentsRect.center() );

    return r;
}

QRectF QskSwitchButtonSkinlet::handleRect(
    const QskSkinnable* skinnable, const QRectF& contentsRect ) const
{
    using Q = QskSwitchButton;

    const auto switchButton = static_cast< const Q* >( skinnable );

    const auto grooveRect = subControlRect( skinnable, contentsRect, Q::Groove );
    const auto pos = qskEffectivePosition( switchButton );
    const auto size = skinnable->strutSizeHint( Q::Handle );

    qreal cx, cy;

    if( switchButton->orientation() == Qt::Vertical )
    {
        const qreal y0 = grooveRect.y() + 0.5 * size.height();
        const qreal h = grooveRect.height() - size.height();

        cx = grooveRect.x() + 0.5 * grooveRect.width();
        cy = y0 + pos * h;
    }
    else
    {
        const qreal x0 = grooveRect.x() + 0.5 * size.width();
        const qreal w = grooveRect.width() - size.width();

        cx = x0 + pos * w;
        cy = grooveRect.y() + 0.5 * grooveRect.height();
    }

    QRectF r;
    r.setSize( size );
    r.moveCenter( QPointF( cx, cy ) );

    return r;
}

QRectF QskSwitchButtonSkinlet::rippleRect(
    const QskSkinnable* skinnable, const QRectF& contentsRect ) const
{
    using Q = QskSwitchButton;

    const auto switchButton = static_cast< const Q* >( skinnable );

    const auto grooveRect = subControlRect( skinnable, contentsRect, Q::Groove );
    const auto pos = qskEffectivePosition( switchButton );
    const auto sizeHandle = skinnable->strutSizeHint( Q::Handle );
    const auto sizeRipple = skinnable->strutSizeHint( Q::Ripple );

    qreal cx, cy;

    if( switchButton->orientation() == Qt::Vertical )
    {
        const qreal y0 = grooveRect.y() + 0.5 * sizeHandle.height();
        const qreal h = grooveRect.height() - sizeHandle.height();

        cx = grooveRect.x() + 0.5 * grooveRect.width();
        cy = y0 + pos * h;
    }
    else
    {
        const qreal x0 = grooveRect.x() + 0.5 * sizeHandle.width();
        const qreal w = grooveRect.width() - sizeHandle.width();

        cx = x0 + pos * w;
        cy = grooveRect.y() + 0.5 * grooveRect.height();
    }

    QRectF r;
    r.setSize( sizeRipple );
    r.moveCenter( QPointF( cx, cy ) );

    return r;
}

#include "moc_QskSwitchButtonSkinlet.cpp"
