#ifndef TN_NAV_SIMULATOR_INCLUDED
#define TN_NAV_SIMULATOR_INCLUDED

#include <boost/optional.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <foundation/Mutex.h>
#include <foundation/Locks.h>
#include <foundation/TnTypes.h>
#include <vector>
#include <string>

#include <RenderEngine/ITnMapRouteProxy.h>
#include <GenericProxy/TnNavSimRouteData.h>


class TnNavSimulator : public ITnMapRouteProxy, public boost::enable_shared_from_this<TnNavSimulator>
{
public:

    typedef Tn::Foundation::shared_ptr<TnNavSimulator> ptr;

    typedef TnNavSimRouteData::RouteMap RouteMap;
    typedef TnNavSimRouteData::SegmentIndicesPtr SegmentIndicesPtr;

    static TnNavSimulator::ptr Create(ITnMapRouteProxy::ptr const & proxy);

    enum State
    {
        eState_NotStarted,
        eState_Navigating,
        eState_Paused,
        eState_Finished
    };

    struct Data {
    
        Data(double heading,
             Tn::math::LatLonHeight const & position,
             Tn::math::LatLonHeight const & ahead,
             Tn::math::LatLonHeight const & behind) :
            heading(heading),
            predictive(true),
            position(position),
            ahead(ahead),
            behind(behind) {}

        Data(double heading,
             Tn::math::LatLonHeight const & position) :
            heading(heading),
            predictive(false),
            position(position) {}

        double heading;     // in degrees
        bool predictive;

        Tn::math::LatLonHeight position;
        Tn::math::LatLonHeight ahead;
        Tn::math::LatLonHeight behind;
        
        // point ahead of vehicle
        uint32_t paovSegmentIndex;
        uint32_t paovEdgeIndex;
        uint32_t paovPointIndex;
    };

    typedef boost::optional<Data> DataOpt;
    
    State GetState() const { return m_state; }

    void SetSpeed(double kmph);
    void SetAhead(double meters);
    void SetBehind(double meters);

    // Returns true if route exists. Timestamp is in milliseconds.
    bool Start(std::string const & name, uint64_t timestamp);

    /* If there is only one route, start it and return true. Otherwise
     * return false.
     */
    bool Start(uint64_t timestamp);
    void Pause();
    void Resume(uint64_t timestamp);

    DataOpt Update(uint64_t timestamp);
    boost::optional<Tn::math::LatLonHeight> GetDestination();

    // The ITnMapRouteProxy part:

    virtual void RequestRoute(ITnMapRouteData::ptr const & data);
    virtual void RequestRoute(std::string const & name, 
                              ITnMapRouteData::ptr const & data);

private:

    TnNavSimulator(ITnMapRouteProxy::ptr const & proxy);

    friend class TnNavSimRouteData;
    void SetRoutes(RouteMap const &);


    void Navigate(uint64_t timestamp);
    Data Get();

    ITnMapRouteProxy::ptr m_proxy;

    State m_state;

    RouteMap m_routes;

    /* Current route (if there is one). */
    Tn::math::RoutePtr m_route;
    SegmentIndicesPtr m_segments;
    std::string m_name;
    TnNavSimRouteData::UIVectorPtr m_route_point_indexes;
    TnNavSimRouteData::UIVectorPtr m_edge_indexes;
    TnNavSimRouteData::UIVectorPtr m_segment_indexes;

    double m_speed;     // in meters per second
    double m_metersAhead;     // in meters
    double m_metersBehind;    // in meters

    Tn::math::RoutePos m_pos;
    Tn::math::RoutePos m_ahead;
    Tn::math::RoutePos m_behind;

    uint64_t m_timestamp;

    Tn::Foundation::mutex m_mutex;
};

#endif // TN_NAV_SIMULATOR_INCLUDED
