/*
 * Copyright Staffan Gimåker 2006-2009.
 * Copyright Anders Boberg 2006-2008.
 *
 * ---
 *
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or copy at
 * http://www.boost.org/LICENSE_1_0.txt)
 */

#ifndef PEEKABOT_TYPES_HH_INCLUDED
#define PEEKABOT_TYPES_HH_INCLUDED

/**
 * \file
 *
 * \brief Contains definitions for common project-wide types and constants.
 *
 * Including platform indepedent primitive types.
 * The naming conventions are that the number at the end of the type name
 * signify the number <em>value and data bits</em> in the type.
 */

#include <cassert>
#include <ostream>
#include <string>
#include <boost/cstdint.hpp>


namespace peekabot
{
    using boost::int8_t;
    using boost::uint8_t;
    using boost::int16_t;
    using boost::uint16_t;
    using boost::int32_t;
    using boost::uint32_t;
    using boost::int64_t;
    using boost::uint64_t;

    typedef boost::uint32_t SourceID;

    static const SourceID NOBODY_SOURCE_ID = 0;
    static const SourceID SERVER_SOURCE_ID = 1;
    static const SourceID GUI_SOURCE_ID = 2;

    extern const boost::uint32_t NUMBER_OF_LAYERS;



    /**
     * \internal
     *
     * \brief Type for uniquely identifying each object in the scene.
     */
    typedef boost::uint32_t ObjectID;

    /**
     * \internal
     *
     * \brief A dumb RGB color struct.
     *
     * Used to pass around RGB colors. The significance of the members should
     * be fairly obvious.
     *
     * \f$ r,g,b \in [0.0,1.0] \f$. Values outside this range cause
     * undefined behaviour.
     */
    struct RGBColor
    {
        RGBColor() throw() : r(0), g(0), b(0)
        {
        }

        RGBColor(const RGBColor &color) throw()
            : r(color.r), g(color.g), b(color.b)
        {
        }

        RGBColor(float _r, float _g, float _b) throw()
            : r(_r), g(_g), b(_b)
        {
        }

        bool operator==(const RGBColor &color) const throw()
        {
            return color.r == r && color.g == g && color.b == b;
        }

        bool operator!=(const RGBColor &color) const throw()
        {
            return !(color == *this);
        }

        /**
         * \brief The red component.
         */
        float r;

        /**
         * \brief The green component.r
         */
        float g;

        /**
         * \brief The blue component.
         */
        float b;
    };

    struct RGBAColor : public RGBColor
    {
        RGBAColor() throw() : RGBColor(), a(1)
        {
        }

        RGBAColor(const RGBAColor &color) throw()
            : RGBColor(color), a(color.a)
        {
        }

        RGBAColor(float _r, float _g, float _b, float _a) throw()
            : RGBColor(_r, _g, _b), a(_a)
        {
        }

        bool operator==(const RGBAColor &color) const throw()
        {
            return color.r == r && color.g == g && color.b == b && color.a == a;
        }

        bool operator!=(const RGBAColor &color) const throw()
        {
            return !(color == *this);
        }

        /**
         * \brief The alpha component.
         */
        float a;
    };

    inline std::ostream &operator<<(std::ostream &os, const RGBAColor &color)
    {
        os << "(" << color.r << ", " << color.g
           << ", " << color.b << ", " << color.a << ")";
        return os;
    }


    /**
     * \internal
     *
     * \brief The type used to describe opacity for objects throughout
     * the application.
     *
     * The \c absolute parameter denotes whether or not the object's opacity is
     * a product of an, from its ancestors, inherited opacity and the given opacity.
     * - \c true denotes that the opacity is absolute. That is, it does
     *   <em>not depend</em> on the opacity of its parent.
     *   The resulting opacity is \c opacity.
     * - \c false means that the object's actual opacity is \f$p\cdot o\f$, where \f$p\f$ is
     *   actual opacity of the parent object, and \f$o\f$ is \c opacity.
     */
    struct Opacity
    {
        Opacity() throw()
            : is_absolute(false), opacity(1.0f) {}

        Opacity(bool _is_absolute, float _opacity) throw()
            : is_absolute(_is_absolute), opacity(_opacity) {}

        Opacity(const Opacity &o) throw()
            : is_absolute(o.is_absolute), opacity(o.opacity) {}

        bool operator==(const Opacity &x) const throw()
        {
            return x.opacity == opacity && x.is_absolute == is_absolute;
        }

        bool operator!=(const Opacity &x) const throw()
        {
            return !(x == *this);
        }


        bool   is_absolute;
        float opacity;
    };

    inline std::ostream &operator<<(std::ostream &os, const Opacity &x)
    {
        os << x.opacity << " " << (x.is_absolute ? "(absolute)":"(relative)");
        return os;
    }


    struct Vector3
    {
        Vector3() {}

        Vector3(const Vector3 &other) : x(other.x), y(other.y), z(other.z) {}

        Vector3(float x_, float y_, float z_) : x(x_), y(y_), z(z_) {}

        float &operator()(int idx)
        {
            if( idx == 0 )
                return x;
            else if( idx == 1 )
                return y;
            else if( idx == 2 )
                return z;
            else
                assert( false );
        }

        float operator()(int idx) const
        {
            if( idx == 0 )
                return x;
            else if( idx == 1 )
                return y;
            else if( idx == 2 )
                return z;
            else
                assert( false );
        }

        float x, y, z;
    };


    struct Transformation
    {
        Vector3 &operator()(int idx)
        {
            if( idx == 0 )
                return x;
            else if( idx == 1 )
                return y;
            else if( idx == 2 )
                return z;
            else if( idx == 3 )
                return pos;
            else
                assert( false );
        }

        const Vector3 &operator()(int idx) const
        {
            if( idx == 0 )
                return x;
            else if( idx == 1 )
                return y;
            else if( idx == 2 )
                return z;
            else if( idx == 3 )
                return pos;
            else
                assert( false );
        }

        inline float &operator()(int row, int col)
        {
            return (*this)(col)(row);
        }

        inline float operator()(int row, int col) const
        {
            return (*this)(col)(row);
        }

        Vector3 x, y, z;
        Vector3 pos;
    };


    enum Axis
    {
        X_AXIS = 8,
        Y_AXIS = 16,
        Z_AXIS = 32
    };

    /**
     * \brief Type used to denote which coordinate system to use.
     */
    enum CoordinateSystem
    {
        /// \internal
        CAMERA_COORDINATES = 64,
        WORLD_COORDINATES = 128,
        LOCAL_COORDINATES = 256,
        PARENT_COORDINATES = 512
    };

    enum RotationCenterpoint
    {
        LOCAL_CENTER = 2048,
        PARENT_CENTER = 4096,
        WORLD_CENTER = 8192,
        AVERAGE_CENTER = 16384
    };

    /**
     * \brief The different actions peekabot can take to resolve a name
     * conflict.
     *
     * Manually ensuring that all paths are globally unique can be quite
     * cumbersome.
     * To make your life easier peekabot can often automatically resolve name
     * conflicts for you if you ask it to - this applies to
     * SceneObject::attach() and all proxy add methods, for example.
     */
    enum NameConflictPolicy
    {
        /**
         * \brief Fail on conflict.
         *
         * If a name conflict occurs, this mode will cause the offending
         * operation to fail.
         */
        FAIL_ON_CONFLICT,

        /**
         * \brief Auto-enumerate on conflict.
         *
         * E.g. if an object named \e test already exists, rename the added
         * object to \e test1.
         */
        AUTO_ENUMERATE_ON_CONFLICT,

        /**
         * \brief Replace on conflict.
         *
         * If a name conflict occurs, replace the old object of the same name
         * with the new object.
         */
        REPLACE_ON_CONFLICT,

        /**
         * \brief Alias on conflict.
         *
         * If a name conflicting arises when creating a new object, simply return
         * a handle that is an alias of the existing object instead.
         *
         * If the existing object is of a different type than the object being
         * created, the action will fail.
         */
        ALIAS_ON_CONFLICT
    };

    enum LineStyle
    {
        // 1111111111111111 (solid)
        // 1000100010001000 (dotted)
        // 1111111110000000 (dashed)
        // 1111111110001000 (dash-dot)
        // 1111111001000100 (dash-dot-dot)
        LINE_STYLE_SOLID = 0xFFFF,
        LINE_STYLE_DOTTED = 0x8888,
        LINE_STYLE_DASHED = 0xFF80,
        LINE_STYLE_DASH_DOT = 0xFF88,
        LINE_STYLE_DASH_DOT_DOT = 0xFE44
    };

    enum GridType
    {
        REGULAR_GRID = 0,
        RADIAL_GRID  = 1,
        ANGULAR_GRID = 2
    };

    enum TextAlignment
    {
        ALIGN_LEFT = 0,
        ALIGN_RIGHT = 1,
        ALIGN_CENTER = 2
    };

    typedef boost::uint16_t PropKey;

    enum VertexOverflowPolicy
    {
        VERTEX_OVERFLOW_CLEAR = 0,
        VERTEX_OVERFLOW_TRUNCATE = 1,
        VERTEX_OVERFLOW_TRUNCATE_HALF = 2
    };

    typedef boost::uint32_t ObjectType;
}


#endif // PEEKABOT_TYPES_HH_INCLUDED
