/*
 * Copyright Staffan Gimåker 2008-2010.
 *
 * ---
 *
 * 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_CLIENT_OCCUPANCY_GRID_2D_PROXY_HH_INCLUDED
#define PEEKABOT_CLIENT_OCCUPANCY_GRID_2D_PROXY_HH_INCLUDED


#include "ObjectProxy.hh"
#include "../../Deprecated.hh"

#include <cstddef>
#include <boost/scoped_ptr.hpp>


namespace peekabot
{
    namespace client
    {
        /**
         * \brief Class used to feed data to
         * OccupancyGrid2DProxyBase::set_cells().
         *
         * \remark This class is not thread safe.
         */
        class PEEKABOT_API OccupancySet2D
        {
            friend class OccupancyGrid2DProxyBase;

        public:
            OccupancySet2D();

            OccupancySet2D(const OccupancySet2D &other);

            ~OccupancySet2D();

            OccupancySet2D &operator=(const OccupancySet2D &other);

            /**
             * \brief Add a cell and its occupancy value to the set.
             *
             * \param x An x-coordinate in the cell to update.
             * \param y An y-coordinate in the cell to update.
             * \param belief The occupancy value of the cell, in the
             * range [0,1], where 1 means fully occupied.
             *
             * \since peekabot 0.8.0
             */
            void add(float x, float y, float belief);

            /**
             * \deprecated Deprecated since peekabot 0.8.0, use add() instead.
             */
            void set_cell(float x, float y, float belief) PEEKABOT_DEPRECATED;

            void clear();

            std::size_t size() const;

            bool empty() const;

        private:
            class Impl;
            boost::scoped_ptr<Impl> m_impl;
        };

        /**
         * \brief Base class for OccupancyGrid2DProxy.
         */
        class PEEKABOT_API OccupancyGrid2DProxyBase : public ObjectProxyBase
        {
        public:
            OccupancyGrid2DProxyBase();

            OccupancyGrid2DProxyBase(OccupancyGrid2DProxyBase &p);

            /**
             * \brief Set/update occupancy values.
             *
             * Note that cell \em coordinates are used, not cell indices. See
             * OccupancyGrid2DProxy for more information.
             */
            DelayedDispatch set_cells(const OccupancySet2D &cells);

            /**
             * \brief Set the color to use for fully unoccupied cells.
             */
            DelayedDispatch set_unoccupied_color(float r, float g, float b);

            /**
             * \brief Set the color to use for fully occupied cells.
             */
            DelayedDispatch set_occupied_color(float r, float g, float b);
        };


        /**
         * \brief Proxy class used to add and manipulate 2D occupancy grids.
         *
         * Cells in the occupancy grid are addressed by coordinates, rather
         * than indices. To, for example, update the greyed cell in the example
         * below one would specify any coordinate \f$(x,y)\f$ where
         * \f$x\in(1.5d, 2.5d), y\in(0.5d, 1.5d)\f$.
         *
         * \image html og_coord_sys.png
         * \image latex og_coord_sys.eps
         *
         * As shown in the figure
         * above, the object's origin is centered on a cell.
         *
         * Cells for which there's no information are drawn as translucent,
         * other cells are drawn in a color linearly interpolated between the
         * set unoccupied color (for belief/occupancy = 0) to the set occupied
         * color (belief/occupancy = 1).
         *
         * The occupancy grid is drawn in object's local XY-plane, which means
         * that any other wanted drawing plane can be achieved by rotating the
         * object appropriately.
         *
         * \par Usage example
         *
         * \code
         * // Add an occupancy grid to the scene
         * peekabot::OccupancyGrid2DProxy og;
         * og.add(client, "root.og", 0.2,
         *        0.8, 0.8, 0.8,
         *        0.0, 0.0, 0.0);
         *
         * // Set a bunch of cells
         * peekabot::OccupancySet2D cells;
         * cells.set_cell(7.2, 9.8, 0.5);
         * cells.set_cell(7.4, 9.8, 0.4);
         * cells.set_cell(7.6, 10.0, 0.55);
         * og.set_cells(cells);
         * \endcode
         */
        class PEEKABOT_API OccupancyGrid2DProxy :
            public OccupancyGrid2DProxyBase
        {
        public:
            OccupancyGrid2DProxy();

            OccupancyGrid2DProxy(OccupancyGrid2DProxy &p);

            OccupancyGrid2DProxy &operator=(const OccupancyGrid2DProxy &p);

            OccupancyGrid2DProxy &operator=(const OccupancyGrid2DProxyBase &p);

            /**
             * \copydoc ScalableProxy::assign(const ObjectProxyBase&)
             */
            DelayedDispatch assign(const ObjectProxyBase &p);

            /**
             * \copydoc ObjectProxy::assign(PeekabotClient&,const std::string&)
             */
            DelayedDispatch assign(
                PeekabotClient &client,
                const std::string &path);

            /**
             * \copydoc
             * ObjectProxy::assign(const ObjectProxyBase&,const std::string&)
             */
            DelayedDispatch assign(
                const ObjectProxyBase &parent,
                const std::string &rel_path);

            /**
             * \brief Add a 2D occupancy grid to the scene.
             *
             * Adds a occupancy grid to the scene at the given path and assigns
             * the proxy to point to it. If an object already exists at the
             * given path the outcome is governed by \a conflict_policy.
             *
             * \param path The path where the object will be added, including
             * the name of the object.
             * \param cell_size The length of the sides, in meters, of the
             * (square) occupancy grid cells.
             * \param conflict_policy Determines how name conflicts are handled.
             */
            DelayedDispatch add(
                PeekabotClient &client,
                const std::string &path,
                float cell_size,
                NameConflictPolicy conflict_policy = AUTO_ENUMERATE_ON_CONFLICT);

            /**
             * \brief Add a 2D occupancy grid to the scene.
             *
             * Adds a occupancy grid with the given name under the the object
             * referred to by \a parent and assigns the proxy to point to it.
             * If an object already exists at the given path the outcome is
             * governed by \a conflict_policy.
             *
             * \param parent The parent under which the object will be added.
             * \param name The name to assign to the created object.
             * \param cell_size The length of the sides, in meters, of the
             * (square) occupancy grid cells.
             * \param conflict_policy Determines how name conflicts are handled.
             */
            DelayedDispatch add(
                const ObjectProxyBase &parent,
                const std::string &name,
                float cell_size,
                NameConflictPolicy conflict_policy = AUTO_ENUMERATE_ON_CONFLICT);

            /**
             * \copydoc add(const PeekabotClient&, const std::string&, float, NameConflictPolicy)
             *
             * \deprecated Deprecated since 0.8.0, use
             * add(PeekabotClient &, const std::string &, float, NameConflictPolicy),
             * set_occupied_color() and set_unoccupied_color() instead.
             */
            DelayedDispatch add(
                PeekabotClient &client,
                const std::string &path,
                float cell_size,
                float unoccupied_r, float unoccupied_g, float unoccupied_b,
                float occupied_r, float occupied_g, float occupied_b,
                NameConflictPolicy conflict_policy = AUTO_ENUMERATE_ON_CONFLICT)
                PEEKABOT_DEPRECATED;

            /**
             * \copydoc add(const ObjectProxyBase&, const std::string&, float, NameConflictPolicy)
             *
             * \deprecated Deprecated since 0.8.0, use
             * add(const ObjectProxyBase &, const std::string &, float, NameConflictPolicy),
             * set_occupied_color() and set_unoccupied_color() instead.
             */
            DelayedDispatch add(
                const ObjectProxyBase &parent,
                const std::string &name,
                float cell_size,
                float unoccupied_r, float unoccupied_g, float unoccupied_b,
                float occupied_r, float occupied_g, float occupied_b,
                NameConflictPolicy conflict_policy = AUTO_ENUMERATE_ON_CONFLICT)
                PEEKABOT_DEPRECATED;
        };
    }
}


#endif // PEEKABOT_CLIENT_OCCUPANCY_GRID_2D_PROXY_HH_INCLUDED
