/******************************************************************************
 * QSkinny - Copyright (C) 2016 Uwe Rathmann
 * This file may be used under the terms of the QSkinny License, Version 1.0
 *****************************************************************************/

#ifndef QSK_ASPECT_H
#define QSK_ASPECT_H

#include "QskFlags.h"
#include <QMetaObject>
#include <functional>

#ifdef Q_MOC_RUN

#define QSK_NAMESPACE( name ) struct name
#define QSK_ENUM( name ) Q_GADGET Q_ENUM( name )

#else

#define QSK_NAMESPACE( name ) namespace name
#define QSK_ENUM( name )

#endif

QSK_NAMESPACE( QskAspect )
{
    enum Subcontrol : quint16
    {
        Control =           0,
        LastSubcontrol =    ( 1 << 12 ) - 1
    };
    QSK_ENUM( Subcontrol )

    enum Type : quint8
    {
        Flag   = 0,
        Metric = 1,
        Color  = 2,
    };
    QSK_ENUM( Type )

    enum Edge : quint8
    {
        LeftEdge        = 1 << 0,
        TopEdge         = 1 << 1,
        RightEdge       = 1 << 2,
        BottomEdge      = 1 << 3,

        HorizontalEdges = TopEdge | BottomEdge,
        VerticalEdges   = LeftEdge | RightEdge,

        AllEdges        = HorizontalEdges | VerticalEdges
    };
    QSK_ENUM( Edge )

    enum Corner : quint8
    {
        TopLeftCorner     = 1 << 0,
        TopRightCorner    = 1 << 1,
        BottomRightCorner = 1 << 2,
        BottomLeftCorner  = 1 << 3,

        LeftCorners       = TopLeftCorner | BottomLeftCorner,
        RightCorners      = TopRightCorner | BottomRightCorner,
        TopCorners        = TopLeftCorner | TopRightCorner,
        BottomCorners     = BottomLeftCorner | BottomRightCorner,

        AllCorners        = LeftCorners | RightCorners
    };
    QSK_ENUM( Corner )

    enum BoxPrimitive : quint8
    {
        Background =     0,

        Margin =         1,
        Padding =        2,
        RadiusX =        3,
        RadiusY =        4,
        Border =         5,
        Shadow =         6,

        Radius  =        7, // RadiusX | RadiusY
    };
    QSK_ENUM( BoxPrimitive )

    enum FlagPrimitive : quint8
    {
        NoFlag,

        Alignment,
        Style,
        SizeMode,
        Decoration,
        GraphicRole,
        FontRole
    };
    QSK_ENUM( FlagPrimitive )

    enum MetricPrimitive : quint8
    {
        NoMetric,

        Size,
        Position,
        MinimumWidth,
        MinimumHeight,
        MaximumWidth,
        MaximumHeight,
        Spacing
    };
    QSK_ENUM( MetricPrimitive )

    enum ColorPrimitive : quint8
    {
        NoColor,

        TextColor,
        StyleColor,
        LinkColor
    };
    QSK_ENUM( ColorPrimitive )

    enum State : quint16
    {
        NoState =         0,

        FirstSystemState =  1 << 0,
        FirstUserState =    1 << 4,
        LastUserState =     1 << 11,
        LastSystemState =   1 << 15,

        AllStates =   0xFFFF
    };
    QSK_ENUM ( State )

    extern const QMetaObject staticMetaObject;
}

QSK_DECLARE_OPERATORS_FOR_FLAGS( QskAspect::Edge )
QSK_DECLARE_OPERATORS_FOR_FLAGS( QskAspect::Corner )
QSK_DECLARE_OPERATORS_FOR_FLAGS( QskAspect::State )

#undef QSK_NAMESPACE
#undef QSK_ENUM

namespace QskAspect
{
    class QSK_EXPORT Aspect
    {
    public:
        constexpr Aspect();
        constexpr Aspect( Subcontrol );
        constexpr Aspect( Type );
        constexpr Aspect( BoxPrimitive );

        constexpr Aspect( const Aspect& ) = default;
        constexpr Aspect( Aspect&& ) = default;

        Aspect& operator=( const QskAspect::Aspect& ) = default;

        bool operator==( const Aspect& ) const;
        bool operator!=( const Aspect& ) const;

        bool operator<( const Aspect& ) const;

        constexpr Aspect operator|( Subcontrol ) const;
        constexpr Aspect operator|( Type ) const;
        constexpr Aspect operator|( BoxPrimitive ) const;
        constexpr Aspect operator|( Edge ) const;
        constexpr Aspect operator|( Corner ) const;
        constexpr Aspect operator|( FlagPrimitive ) const;
        constexpr Aspect operator|( MetricPrimitive ) const;
        constexpr Aspect operator|( ColorPrimitive ) const;
        constexpr Aspect operator|( State ) const;

        constexpr quint64 value() const;

        bool isAnimator() const;
        void setAnimator( bool on );

        Subcontrol subControl() const;
        void setSubControl( Subcontrol );

        Type type() const;
        void setType( Type );

        State state() const;
        State topState() const;

        void addState( State state );
        void clearState( State state );
        void clearStates();

        bool isBoxPrimitive() const;
        BoxPrimitive boxPrimitive() const;
        void setBoxPrimitive( BoxPrimitive primitive );

        Edge edge() const;
        void setEdge( Edge edge );
        void clearEdge();

        Corner corner() const;
        void setCorner( Corner corner );

        FlagPrimitive flagPrimitive() const;
        ColorPrimitive colorPrimitive() const;
        MetricPrimitive metricPrimitive() const;

        const char* toPrintable() const;

    private:
        constexpr Aspect( Subcontrol, Type, BoxPrimitive );
        constexpr Aspect( uint subControl, uint type, bool isAnimator,
            uint primitive, bool isBoxPrimitive, uint states );

        uint m_subControl : 12;

        uint m_type : 3;
        bool m_isAnimator : 1;

        uint m_reserved1 : 8;

        uint m_primitive : 7;
        bool m_isBoxPrimitive : 1;

        uint m_states : 16;

        uint m_reserved2 : 16;

    } Q_PACKED;

    inline constexpr Aspect::Aspect():
        Aspect( Control, Flag, Background )
    {
    }

    inline constexpr Aspect::Aspect( Subcontrol subControl ):
        Aspect( subControl, Flag, Background )
    {
    }

    inline constexpr Aspect::Aspect( Type type ):
        Aspect( Control, type, Background )
    {
    }

    inline constexpr Aspect::Aspect( BoxPrimitive primitive ):
        Aspect( Control, Flag, primitive )
    {
    }

    inline constexpr Aspect::Aspect( Subcontrol subControl, Type type, BoxPrimitive primitive ):
        Aspect( subControl, type, false, primitive, true, NoState )
    {
    }

    inline constexpr Aspect::Aspect( uint subControl, uint type, bool isAnimator,
            uint primitive, bool isBoxPrimitive, uint states ):
        m_subControl( subControl ),
        m_type( type ),
        m_isAnimator( isAnimator ),
        m_reserved1( 0 ),
        m_primitive( primitive ),
        m_isBoxPrimitive( isBoxPrimitive ),
        m_states( states ),
        m_reserved2( 0 )
    {
    }

    inline bool Aspect::operator==( const Aspect& other ) const
    {
        return value() == other.value();
    }

    inline bool Aspect::operator!=( const Aspect& other ) const
    {
        return value() != other.value();
    }

    inline bool Aspect::operator<( const Aspect& other ) const
    {
        return value() < other.value();
    }

    inline constexpr Aspect Aspect::operator|( Subcontrol subControl ) const
    {
        return Aspect( subControl, m_type, m_isAnimator,
            m_primitive, m_isBoxPrimitive, m_states );
    }

    inline constexpr Aspect Aspect::operator|( Type type ) const
    {
        return Aspect( m_subControl, type, m_isAnimator,
            m_primitive, m_isBoxPrimitive, m_states );
    }

    inline constexpr Aspect Aspect::operator|( BoxPrimitive primitive ) const
    {
        return Aspect( m_subControl, m_type, m_isAnimator,
            ( m_primitive & ~0x7 ) | primitive, true, m_states );
    }

    inline constexpr Aspect Aspect::operator|( Edge edge ) const
    {
        return Aspect( m_subControl, m_type, m_isAnimator,
            m_primitive | ( edge << 3 ), true, m_states );
    }

    inline constexpr Aspect Aspect::operator|( Corner corner ) const
    {
        return operator|( static_cast< Edge >( corner ) );
    }

    inline constexpr Aspect Aspect::operator|( FlagPrimitive primitive ) const
    {
        return Aspect( m_subControl, m_type, m_isAnimator,
            primitive, false, m_states );
    }

    inline constexpr Aspect Aspect::operator|( MetricPrimitive primitive ) const
    {
        return operator|( static_cast< FlagPrimitive >( primitive ) );
    }

    inline constexpr Aspect Aspect::operator|( ColorPrimitive primitive ) const
    {
        return operator|( static_cast< FlagPrimitive >( primitive ) );
    }

    inline constexpr Aspect Aspect::operator|( State state ) const
    {
        return Aspect( m_subControl, m_type, m_isAnimator,
            m_primitive, m_isBoxPrimitive, m_states | state );
    }

    inline constexpr quint64 Aspect::value() const
    {
        return *reinterpret_cast< const quint64* >( this );
    }

    inline bool Aspect::isAnimator() const
    {
        return m_isAnimator;
    }

    inline void Aspect::setAnimator( bool on )
    {
        m_isAnimator = on;
    }

    inline Subcontrol Aspect::subControl() const
    {
        return static_cast< Subcontrol >( m_subControl );
    }

    inline void Aspect::setSubControl( Subcontrol subControl )
    {
        m_subControl = subControl;
    }

    inline Type Aspect::type() const
    {
        return static_cast< Type >( m_type );
    }

    inline void Aspect::setType( Type type )
    {
        m_type = type;
    }

    inline State Aspect::state() const
    {
        return static_cast< State >( m_states );
    }

    inline void Aspect::addState( State state )
    {
        m_states |= state;
    }

    inline void Aspect::clearState( State state )
    {
        m_states &= ~state;
    }

    inline void Aspect::clearStates()
    {
        m_states = 0;
    }

    inline bool Aspect::isBoxPrimitive() const
    {
        return m_isBoxPrimitive;
    }

    inline BoxPrimitive Aspect::boxPrimitive() const
    {
        return static_cast< BoxPrimitive >( m_primitive & 0x7 );
    }

    inline void Aspect::setBoxPrimitive( BoxPrimitive primitive )
    {
        m_isBoxPrimitive = true;
        m_primitive = primitive;
    }

    inline Edge Aspect::edge() const
    {
        if ( m_isBoxPrimitive )
            return static_cast< Edge >( m_primitive >> 3 );

        return static_cast< Edge >( 0 );
    }

    inline void Aspect::setEdge( Edge edge )
    {
        m_isBoxPrimitive = true;
        m_primitive |= ( edge << 3 );
    }

    inline void Aspect::clearEdge()
    {
        if ( m_isBoxPrimitive )
            m_primitive &= ~( AllEdges << 3 );
    }

    inline Corner Aspect::corner() const
    {
        return static_cast< Corner >( edge() );
    }

    inline void Aspect::setCorner( Corner corner )
    {
        setEdge( static_cast< Edge >( corner ) );
    }

    inline FlagPrimitive Aspect::flagPrimitive() const
    {
        if ( m_isBoxPrimitive || ( m_type != Flag ) )
            return NoFlag;

        return static_cast< FlagPrimitive >( m_primitive );
    }

    inline ColorPrimitive Aspect::colorPrimitive() const
    {
        if ( m_isBoxPrimitive || ( m_type != Color ) )
            return NoColor;

        return static_cast< ColorPrimitive >( m_primitive );
    }

    inline MetricPrimitive Aspect::metricPrimitive() const
    {
        if ( m_isBoxPrimitive || ( m_type != Metric ) )
            return NoMetric;

        return static_cast< MetricPrimitive >( m_primitive );
    }

    inline constexpr Aspect operator|( State state, const Aspect& aspect )
    {
        return aspect | state;
    }

    inline constexpr Aspect operator|( Edge edge, const Aspect& aspect )
    {
        return aspect | edge;
    }

    inline constexpr Aspect operator|( Corner corner, const Aspect& aspect )
    {
        return aspect | corner;
    }

    inline constexpr Aspect operator|( Subcontrol subControl, const Aspect& aspect )
    {
        return subControl | aspect;
    }

    inline constexpr Aspect operator|( Type type, const Aspect& aspect )
    {
        return aspect | type;
    }

    inline constexpr Aspect operator|( BoxPrimitive primitive, const Aspect& aspect )
    {
        return aspect | primitive;
    }

    inline constexpr Aspect operator|( Subcontrol subControl, Type type )
    {
        return Aspect( subControl ) | type;
    }

    inline constexpr Aspect operator|( Type type, Subcontrol subControl )
    {
        return subControl | type;
    }

    inline constexpr Aspect operator|( Subcontrol subControl, State state )
    {
        return Aspect( subControl ) | state;
    }

    inline constexpr Aspect operator|( State state, Subcontrol subControl )
    {
        return subControl | state;
    }

    inline constexpr Aspect operator|( Type type, BoxPrimitive primitive )
    {
        return Aspect( type ) | primitive;
    }

    inline constexpr Aspect operator|( BoxPrimitive primitive, Type type )
    {
        return type | primitive;
    }

    inline constexpr Aspect operator|( BoxPrimitive primitive, Edge edge )
    {
        return Aspect( primitive ) | edge;
    }

    inline constexpr Aspect operator|( Edge edge, BoxPrimitive primitive )
    {
        return primitive | edge;
    }

    inline constexpr Aspect operator|( BoxPrimitive primitive, Corner corner )
    {
        return primitive | static_cast< Edge >( corner );
    }

    inline constexpr Aspect operator|( Corner corner, BoxPrimitive primitive )
    {
        return primitive | corner;
    }

    inline constexpr Aspect operator|( Subcontrol subControl, FlagPrimitive primitive )
    {
        return Aspect( subControl ) | primitive;
    }

    inline constexpr Aspect operator|( FlagPrimitive primitive, Subcontrol subControl )
    {
        return subControl | primitive;
    }

    inline constexpr Aspect operator|( Subcontrol subControl, ColorPrimitive primitive )
    {
        return Aspect( subControl ) | primitive;
    }

    inline constexpr Aspect operator|( ColorPrimitive primitive, Subcontrol subControl )
    {
        return subControl | primitive;
    }

    inline constexpr Aspect operator|( Subcontrol subControl, MetricPrimitive primitive )
    {
        return Aspect( subControl ) | primitive;
    }

    inline constexpr Aspect operator|( MetricPrimitive primitive, Subcontrol subControl )
    {
        return subControl | primitive;
    }

    inline constexpr Aspect operator|( Subcontrol subControl, BoxPrimitive primitive )
    {
        return Aspect( subControl ) | primitive;
    }

    inline constexpr Aspect operator|( BoxPrimitive primitive, Subcontrol subControl )
    {
        return subControl | primitive;
    }

    inline constexpr Aspect operator|( Subcontrol subControl, Edge edge )
    {
        return Aspect( subControl ) | edge;
    }

    inline constexpr Aspect operator|( Edge edge, Subcontrol subControl )
    {
        return subControl | edge;
    }

    inline constexpr Aspect operator|( Subcontrol subControl, Corner corner )
    {
        return Aspect( subControl ) | corner;
    }

    inline constexpr Aspect operator|( Corner corner, Subcontrol subControl )
    {
        return subControl | corner;
    }

    QSK_EXPORT State registerState( const QMetaObject*, State, const char* );
    QSK_EXPORT Subcontrol nextSubcontrol( const QMetaObject*, const char* );

    QSK_EXPORT QByteArray subControlName( Subcontrol );
    QSK_EXPORT QVector< QByteArray > subControlNames( const QMetaObject* = nullptr );
    QSK_EXPORT QVector< Subcontrol > subControls( const QMetaObject* );
}

namespace std
{
    template< > struct hash< QskAspect::Aspect >
    {
        constexpr size_t operator()( const QskAspect::Aspect& aspect ) const noexcept
        {
            return aspect.value();
        }
    };
}

Q_DECLARE_TYPEINFO( QskAspect::Aspect, Q_MOVABLE_TYPE );

#ifndef QT_NO_DEBUG_STREAM

class QDebug;

QSK_EXPORT QDebug operator<<( QDebug, const QskAspect::Aspect& );
QSK_EXPORT QDebug operator<<( QDebug, const QskAspect::Type& );
QSK_EXPORT QDebug operator<<( QDebug, const QskAspect::Edge& );
QSK_EXPORT QDebug operator<<( QDebug, const QskAspect::Corner& );
QSK_EXPORT QDebug operator<<( QDebug, const QskAspect::BoxPrimitive& );
QSK_EXPORT QDebug operator<<( QDebug, const QskAspect::FlagPrimitive& );
QSK_EXPORT QDebug operator<<( QDebug, const QskAspect::ColorPrimitive& );
QSK_EXPORT QDebug operator<<( QDebug, const QskAspect::MetricPrimitive& );
QSK_EXPORT QDebug operator<<( QDebug, const QskAspect::Subcontrol& );
QSK_EXPORT QDebug operator<<( QDebug, const QskAspect::State& );

QSK_EXPORT void qskDebugState( QDebug, const QMetaObject*, QskAspect::State );
QSK_EXPORT void qskDebugAspect( QDebug, const QMetaObject*, QskAspect::Aspect );

#endif

#define QSK_SUBCONTROLS( ... ) static const QskAspect::Subcontrol __VA_ARGS__;
#define QSK_STATES( ... ) static const QskAspect::State __VA_ARGS__;

#define QSK_SUBCONTROL( type, name ) \
    const QskAspect::Subcontrol type::name = \
        QskAspect::nextSubcontrol( &type::staticMetaObject, #type "::" #name );

#define QSK_STATE( type, name, value ) \
    const QskAspect::State type::name = \
        QskAspect::registerState( &type::staticMetaObject, value, #type "::" #name );
#endif
