/*
 *  ITnMapEngine.h
 *  MapDemo
 *
 *  Created by Matthew Thompson on 4/1/2010
 *  Copyright 2009 TeleNav Inc. All rights reserved.
 *
 */

#ifndef ITN_MAP_ENGINE_H
#define ITN_MAP_ENGINE_H

#include <string>
#include <map>
#include <set>
#include <vector>
#include <list>
#include <foundation/SharedArray.h>
#include <foundation/TnTypes.h>

#include <foundation/Math/Point.hpp>

#include <RenderEngine/TnDeprecate.h>

#include <RenderEngine/TnMapAnnotationParams.h>
#include <RenderEngine/TnMapBillboardParams.h>
#include <RenderEngine/TnMapArrowParams.h>
#include <RenderEngine/TnMapAnnotationAnimationParams.h>
#include <RenderEngine/TnMapLayer.h>

#include <RenderEngine/TnImage.h>

#include <boost/optional.hpp>
#include <foundation/Math/LatLonHeight.hpp>

#include <RenderInterface/TnRenderInterface.h>
#include <RenderEngine/TnMapGraveyard.h>

//Forward declarations
namespace Tn {
namespace openGLMap {
class Model;
} //end namespace openGLMap
} //end namespace Tn

class ITnMapProxyFactory;
class ITnMapResourceProxy;

class ITnMapEngine
{
public:
    /*! Shared pointer to OpenGL engine */
    typedef Tn::Foundation::shared_ptr<ITnMapEngine> ptr;
    typedef Tn::Foundation::weak_ptr<ITnMapEngine> wptr;

    /*! Opaque handle representing a single "view" into the OpenGL engine */
    class View {};
    typedef View const * ViewId;

    /*! Opaque handle representing a single engine annotation */
    class Annotation {};
    typedef Annotation const * AnnotationId;

    /*! Opaque handle representing a single graphic object that backs
     *  engine annotations. */
    class Graphic {};
    typedef Tn::Foundation::shared_ptr<Graphic> GraphicId;

    /*! Opaque handle representing an engine configuration */
    class EngineConf {};
    typedef Tn::Foundation::shared_ptr<EngineConf> EngineConfId;

    /*! Opaque handle representing a view configuration */
    class Conf {};
    typedef Tn::Foundation::shared_ptr<Conf> ConfId;

    class Model {};
    typedef Tn::Foundation::shared_ptr<Model> ModelId;
    
    class TnModel {};
    typedef Tn::Foundation::shared_ptr<TnModel> TnModelId;
    
    class TnCanvasModel {};
    typedef TnCanvasModel const * TnCanvasModelId;
    
    typedef float Vec3f[3];

    class RenderBufferObject {};
    typedef RenderBufferObject const * RBOId;

    enum eRBOStatus
    {
        eUnCreate = 0,
        eCreating,
        eCreated,
        eBinding,
        eBinded,
        eUnBinding
    };

    /*! Interaction Mode - Control how mouse movement interacts with the system                 
    */
    enum eInteractionMode
    {
        eInteractionMode_Invalid = -1,  //!< Indicates an error
        eInteractionMode_None = 0,      //!< No interaction
        eInteractionMode_PanAndZoom,    //!< The map pans to track the touched location
        eInteractionMode_FollowVehicle, //!< The camera follows the vehicle.
        eInteractionMode_RotateAroundPoint, //!< Rotate around the point in the center of the screen.
        eInteractionMode_FreeLook       //!< Place eye at location and look around.
    };


    /*! Rendering Mode - Indicate either 2D rendering (Top-Down) or 3D
     *  Rendering
     */
    enum eRenderingMode
    {
        eRenderingMode_Invalid = -1,
        eRenderingMode_3D = 0,
        eRenderingMode_3DNorthUp,
        eRenderingMode_3DHeadingUp,
        eRenderingMode_2D,
        eRenderingMode_2DNorthUp,
        eRenderingMode_2DHeadingUp
    };

    /*! Orientation that OpenGL should render to. "PortraitBottom"
     *  means "portrait mode with home button on the
     *  bottom". "LandscapeLeft" means "landscape mode with home
     *  button on the left." Etc. */

    enum eOrientation
    {
        eOrientation_Invalid = -1,
        eOrientation_PortraitBottom = 0,
        eOrientation_PortraitTop,
        eOrientation_LandscapeLeft,
        eOrientation_LandscapeRight
    };

    /*! Touch Event. */
    enum eTouchEvent
    {
        eTouchEvent_Begin,
        eTouchEvent_Move,
        eTouchEvent_End
    };

    /*! The engine supports two approaches to handling multi-touch
     *  events. In the first approach, the client application
     *  specifies explicitly how the engine is to interpret the touch
     *  events. In this case, the client application sets the
     *  MultiTouchMode directly.
     *
     *  The second approach is to allow the engine to detect gestures,
     *  and switch to the corresponding multi-touch mode for
     *  itself. Once the engine has switched to a given gesture, it
     *  doesn't switch to a new mode until a new gesture is
     *  started. For example, if the engine detects a camera tilt
     *  gesture, it will not switch to a rotate gesture unless a new
     *  gesture is started. (The one exception to this rule is that a
     *  pinch zoom can switch to a zoom-and-rotate once the rotate
     *  component passes a given threshold.
     *
     *  This second approach is enabled by setting the multi-touch
     *  mode to "Auto."  In "Auto" mode, the set of gestures
     *  recognized by the engine is specified by the
     *  MultiTouchGestureSet.
     *
     *  MultiTouchGestureSet defaults to zoom + rotate + tilt. 
     */
    enum eMultiTouchGestureSet
    {
        eMultiTouchGestureSet_None = 0,

        eMultiTouchGestureSet_Zoom =        1 << 0,
        eMultiTouchGestureSet_Pan =         1 << 1,
        eMultiTouchGestureSet_Rotate =      1 << 2,
        eMultiTouchGestureSet_Tilt =        1 << 3,
    };

    /*! Multi-Touch Mode */
    enum eMultiTouchMode
    {
        eMultiTouchMode_Invalid = -1,

        eMultiTouchMode_Auto = 0,

        eMultiTouchMode_Zoom = 1 << 0,
        eMultiTouchMode_Pan = 1 << 1,
        eMultiTouchMode_Rotate = 1 << 2,
        eMultiTouchMode_Tilt = 1 << 3,

        eMultiTouchMode_PanAndZoom = 
        (eMultiTouchMode_Zoom |
         eMultiTouchMode_Pan),

        eMultiTouchMode_ZoomAndRotate = 
        (eMultiTouchMode_Zoom |
         eMultiTouchMode_Rotate),

        eMultiTouchMode_PanAndZoomAndRotate = 
        (eMultiTouchMode_Pan |
         eMultiTouchMode_Zoom |
         eMultiTouchMode_Rotate)
    };

    
    /*! Pickable interface represents an object that can be "picked"
     * using GetNearest. Client (or engine) may derive from this
     * class to define its own objects from which the engine may pick. */
    
    class Pickable
    {
    public:

        // Shared ptr
        typedef Tn::Foundation::shared_ptr<const Pickable> const_ptr;
        
        Pickable() : id(0) {}

        /*! The ID is used by the UpdateAnnotation function. If the
         *  client assigns a non-zero ID to a proxy-fed annotation,
         *  that annotation's texture can be modified using the
         *  UpdateAnnotation function. */

        Pickable(uint64_t id) : id(id) {}
        
        uint64_t GetID() const { return id; }
        
        // Polymorphic class
        virtual ~Pickable();

    private:

        uint64_t id;
    };
        
    /*! Structure to hold annotation search result. */
    
    struct AnnotationSearchResult
    {
        //! Distance from the edge of the annotation to the touch point. Zero if touch is within.
        float distance;

        //! Distance along the pick ray. Can be used to select the
        //! item in front if more than one item is touched.
        float eye_distance;
        
        //! The point (in point size) within the annotation that was touched (if applicable)
        float insideX;
        float insideY;

		//! The point (in texture space) within the annotation that was touched (if applicable)
        float insideXtexture;
        float insideYtexture;

        //! The annotation layer that contains this annotation
        eTnMapLayer layer;

        //! The touched annotation id (for general, engine-fed annotations)
        AnnotationId annotationId;
        
        //! The touched Pickable (for proxy-fed annotations, if valid)
        Pickable::const_ptr pickable;
    };
    
    /*! Container to hold all annotation search results. */
    typedef std::vector<AnnotationSearchResult> AnnotationSearchResults;
    
    
    /*! Events that the engine will care about. */

    enum eEvent
    {
        eEvent_NewRoute,
        eEvent_NewTraffic,
        eEvent_NewWeather,
        eEvent_FlushEngineData_DO_NOT_USE,
        eEvent_ClearCaches_DO_NOT_USE,
        eEvent_ShowCarModel_DO_NOT_USE,
        eEvent_ShowPedestrianModel_DO_NOT_USE,
        eEvent_ShowDotModel_DO_NOT_USE,
        eEvent_NewTileAnnotations,
        eEvent_ClearBreadcrumbs,
        eEvent_DumpGlyphs,
        eEvent_EngineBackgrounded,
        eEvent_FreeMemory,
        eEvent_NewConfig
    };
    
    /*! Notify the OpenGL engine that a particular event has happened. 
        Everything happenging through Notify() is thread-safe*/

    virtual bool Notify(ViewId viewId, eEvent event) = 0;


    /*! This is identical to calling Notify with
     *  eEvent_NewTileAnnotations, only it allows the request to be
     *  associated with a given annotation layer. */

    TNMAP_DEPRECATED(virtual void RequestTileAnnotations(eTnMapLayer layer) = 0;)       // deprecated 1.7
    virtual bool RequestTileAnnotations(ViewId viewId, eTnMapLayer layer) = 0;


    /*!  General engine boolean parameters. This will be extended as needed. */

    enum eParameterBool
    {
        eParameterBool_BEGIN = 0,
        eParameterBool_ShowCar = eParameterBool_BEGIN,
        eParameterBool_ShowADI,
        eParameterBool_ShowRoute,
        eParameterBool_ShowMapScale,
        eParameterBool_EnglishUnits,
        eParameterBool_ShowRegions,
        eParameterBool_ShowRoads,
        eParameterBool_ShowAnnotations2D,
        eParameterBool_ShowAnnotations3D,
        eParameterBool_ShowAnnotationsModel,
        eParameterBool_ShowText2D,
        eParameterBool_ShowDebugGrid,
        eParameterBool_ShowTrafficHighlight,
        eParameterBool_ShowTrafficIncidents,
        eParameterBool_ShowBreadcrumbs,
        eParameterBool_ShowText3D,
        eParameterBool_ShowLandmarks,
        eParameterBool_RecordBreadcrumbs,
        eParameterBool_ShowSky,
        eParameterBool_ShowAnnotationsScreen,
        eParameterBool_DisableVehicleSmoothing,
        eParameterBool_ShowDebugInfo,
        eParameterBool_EnableDangerCone,
        eParameterBool_ShowRangeRings,
        eParameterBool_LowDetailView,
        eParameterBool_DriveRightSide,
        eParameterBool_ShowAnnotationsBillboardLabel,
        eParameterBool_ShowStatsGraph,
        eParameterBool_ShowGPSRadius,
        eParameterBool_SIZE
    };

    /*!  ShowArial needs to be pulled out of above, these states are mutually 
     *   exclusive in terms of what can be displayed on screen. */

    enum eRasterEnable
    {
        eRasterEnable_Aerial,
        eRasterEnable_AerialWithLabels,
        eRasterEnable_Road,
        eRasterEnable_TerrainAerial,
        eRasterEnable_TerrainAerialWithLabels,
        eRasterEnable_TerrainRoad,
        eRasterEnable_HeightmapAsRaster,
        eRasterEnable_NONE
    };

    virtual void EnableRasterType(ViewId viewId, eRasterEnable type) = 0;

    /*! General engine double parameters. This will be extended as needed. */

    enum eParameterDouble
    {
        eParameterDouble_BEGIN = 0,
        eParameterDouble_CarScale = eParameterDouble_BEGIN,
        eParameterDouble_ADILatitude,
        eParameterDouble_ADILongitude,
        eParameterDouble_VerticalOffset,
        eParameterDouble_LODBias,
        eParameterDouble_CameraDeclination2D,
        eParameterDouble_CameraDeclination3D,
        eParameterDouble_HorizontalOffset,
        eParameterDouble_AnnotationScale2D,
        eParameterDouble_AnnotationScale3D,
        eParameterDouble_DangerConeFieldOfView,
        eParameterDouble_DangerConeRadius,
        eParameterDouble_EyeDistance,
        eParameterDouble_OverlayAlpha,
        eParameterDouble_ZoomLowerBound,
        eParameterDouble_ZoomUpperBound,
        eParameterDouble_SIZE
    };
    
    /*! General engine string parameters. This will be extended as needed. */
    
    enum eParameterString
    {
        eParameterString_NoDraw,
        eParameterString_SIZE
    };
    
    /*! General engine binary data. This will be extended as needed. */
    
    enum eParameterBinary
    {
        eParameterBinary_Breadcrumbs,
        eParameterBinary_SIZE
    };
    
    
    /*! Set a boolean engine parameter */
    
    virtual bool SetBool(ViewId viewId, eParameterBool param, bool value) = 0;

    /*! Set a double engine parameter */
    
    virtual bool SetDouble(ViewId viewId, eParameterDouble param, double value) = 0;

    /*! Set a double array engine parameter */
    
    virtual bool SetDoubleArray(ViewId viewId, eParameterDouble param, std::vector<std::pair<int, double> > const & data) = 0;

    
    /*! Set a [list of] string engine parameter */
    
    virtual bool SetStringList(ViewId viewId, eParameterString param, std::vector<std::string> const & strings) = 0;

    /*! Get a boolean engine parameter */
    
    virtual bool GetBool(ViewId viewId, eParameterBool param, bool& value) const = 0;

    /*! Get a double engine parameter */
    
    virtual bool GetDouble(ViewId viewId, eParameterDouble param, double& value) const = 0;

    /*! Get a double array engine parameter */
    
    virtual bool GetDoubleArray(ViewId viewId, eParameterDouble param, std::vector<std::pair<int, double> > & data) = 0;

    /*! Set binary data */
    virtual bool SetBinary(ViewId viewId, eParameterBinary param, std::vector<uint8_t> const & data) = 0;
    
    /*! Get binary data */
    virtual bool GetBinary(ViewId viewId, eParameterBinary param, std::vector<uint8_t> & data) = 0;
    
    
    /*** Engine Configuration ***/
    
    /*! Get an EngineConfId representing the engine's default configuration. */
    static EngineConfId GetDefaultEngineConfig();

    /*! Add raw configuration data to a configuration object (currently must be JSON). */
    static void AddConfig(EngineConfId const & confId, std::vector<char> const & configData);

    /* @TODO: Implement this. */
    /*! Add a resource (currently can be a JSON file or URL) to a configuration object */
    static void AddConfig(EngineConfId const & confId, std::string const & source);


    /*** View Configuration ***/
    
    /*! Get a ConfId representing the view's default configuration */
    virtual ConfId GetDefaultConfig() const = 0;
    
    /*! Add a resource (currently can be a JSON file or URL) to a configuration object */
    virtual bool AddConfig(ConfId const & confId, std::string const & source) = 0;
    
    /*! Add raw configuration data to a configuration object (currently must be JSON) */
    virtual bool AddConfig(ConfId const & confId, std::vector<char> const & configData) = 0;

    
    /*** Engine state ***/
    
    /*! Engine State Object - Object returned after updating a view to
     *  describe current state of that view. Latitude and longitude
     *  are in degrees.
     *
     *  @TODO: Need to break this out into per-view state and global engine state
     */

    struct EngineState
    {
        double cameraLatitude;
        double cameraLongitude;
        float cameraHeading;
        float cameraHeight;
        float cameraFieldOfView;
        float cameraDeclination;

        double rangeHorizontal;

        double carLatitude;
        double carLongitude;
        float carHeading;

        // Exact continuous zoom level, representing
        // distance from camera to view center
        float zoomLevel;

        // Discreet zoom level containing the data that
        // composes this view
        uint32_t dataZoomLevel;

        eInteractionMode interactionMode;
        eRenderingMode renderMode;

        bool isAnimating;

        uint32_t tilesOnScreen;
        uint32_t tilesWithEdgesLoaded;
        uint32_t tilesWithPolygonsLoaded;
        uint32_t tilesWithTextLoaded;
        uint32_t tilesWithAnnotationsLoaded;
        uint32_t tilesWithLandmarksLoaded;
        uint32_t tilesWithTrafficLoaded;
        
        uint64_t totalGPUFootprint;
        uint64_t totalGPUTextureFootprint;
        uint64_t totalGPUVboFootprint;

        bool    updated;
        
        //Does the engine have the optimal tile set?
        bool    optimalTileSetLoaded;
        //Is the scene complete (optimalTileSetLoaded && textPlaced)
        bool    sceneComplete;
        bool    trafficComplete;
    };
    
    /*! Engine State Object - The actual object returned to engine callers
     *  is actually a container of EngineState structs.
     */
    typedef std::map<ViewId,EngineState> EngineStateObject;


    /*** Management ***/

    /*! Get the version number of the Engine as a string. */
    static std::string const & GetVersionNumber();
    
    /*! Create an instance of the map engine, with an optional engine configuration. */
    static ptr CreateMapEngine(Tn::Graphics::RenderInterfacePtr & renderInterface,
                               Tn::Foundation::shared_ptr<ITnMapProxyFactory> const & proxyFactory,
                               EngineConfId const & engineConfId = EngineConfId());

    virtual uint32_t GetBaseTileSize() const = 0;
    
    /*! Set a new proxy factory for the engine to use */
    virtual void SetProxyFactory(Tn::Foundation::shared_ptr<ITnMapProxyFactory> const & proxyFactory) = 0;

    /*! Create a view at the specified canvas location using the given
     *  configuration. */
    virtual ViewId CreateView(ConfId const & confId,
                              uint32_t x, 
                              uint32_t y, 
                              uint32_t width, 
                              uint32_t height,
                              float dots_per_inch,
                              float view_scale_factor = 1.0f,
                              Tn::math::LatLonHeight const & defCamPosition = Tn::math::LatLonHeight()) = 0;
    
    virtual ViewId CreateView(ConfId const & confId,
                              float zoomLevel,
                              uint32_t x,
                              uint32_t y,
                              uint32_t width,
                              uint32_t height,
                              float dots_per_inch,
                              float view_scale_factor = 1.0f,
                              Tn::math::LatLonHeight const & defCamPosition = Tn::math::LatLonHeight()) = 0;
    
    /*! Delete a view by its ID */
    virtual bool DeleteView(ViewId viewId) = 0;

    /*! Resize the canvas location that describes a view, by its ID */
    virtual bool ResizeView(ViewId viewId, 
                            int32_t x, int32_t y, 
                            uint32_t width, uint32_t height) = 0;

    /*! Resize the canvas location that describes a view, by its ID */
    virtual bool ResizeView(ViewId viewId, 
                            int32_t x, int32_t y, 
                            uint32_t width, uint32_t height,
                            float dots_per_inch,
                            float view_scale_factor = 1.0f) = 0;

    virtual RBOId CreateRenderBufferObject(uint32_t width, uint32_t height) = 0;
    
    virtual bool DeleteRenderBufferObject(RBOId rboId) = 0;

    virtual bool BindRenderBufferObject(RBOId rboId) = 0;

    virtual bool UnBindRenderBufferObject(RBOId rboId) = 0;

    virtual eRBOStatus const GetRenderBufferObjectStatus(RBOId rboId) const = 0;

    /*** Interaction ***/

    /*! Pass a touch event to the specified view. The touch coordinate
     * is specified relative to the view's screen coordinate
     * system.  */

    TNMAP_DEPRECATED(virtual bool HandleTouchEvent(ViewId, eTouchEvent, uint32_t, uint32_t, float = 0) = 0;)

    virtual bool HandleTouchEvent(ViewId viewId,
                                  uint64_t timestamp,           //!< Millseconds
                                  eTouchEvent event,
                                  int32_t x, 
                                  int32_t y, 
                                  float pinch = 0               //!< For pinch zoom.
        ) = 0;

    /* The touch points p1 and p2 are screen (viewport) coordinates,
     * but here they're in floats anyway. */

    virtual bool HandleMultiTouchEvent(ViewId viewId,
                                       uint64_t timestamp,           //!< Millseconds
                                       eTouchEvent event,
                                       Tn::math::Point2f const & p1,
                                       Tn::math::Point2f const & p2) = 0;

    /*! All camera control calls trigger smooth camera transitions to
     * the specified settings and mode changes. Transition time is
     * specified via SetTransitionTime. */

    virtual bool                SetTransitionTime(ViewId viewId, float seconds) = 0;

    virtual bool                SetInteractionMode(ViewId viewId, eInteractionMode mode) = 0;
    virtual eInteractionMode    GetInteractionMode(ViewId viewId) const = 0;

    virtual bool                SetMultiTouchMode(ViewId viewId, eMultiTouchMode mode) = 0;
    virtual eMultiTouchMode     GetMultiTouchMode(ViewId viewId) const = 0;

    virtual bool                SetMultiTouchGestureSet(ViewId viewId, eMultiTouchGestureSet set) = 0;
    virtual eMultiTouchGestureSet GetMultiTouchGestureSet(ViewId viewId) const = 0;

    virtual bool                SetRenderingMode(ViewId viewId, eRenderingMode mode) = 0;
    virtual eRenderingMode      GetRenderingMode(ViewId viewId) const = 0;

    virtual void                SetOrientation(ViewId viewId, eOrientation orientation) = 0;
    virtual eOrientation        GetOrientation(ViewId viewId) const = 0;


    /*! Moves the camera to the specified location */
    virtual bool LookAt(ViewId viewId, double latitude, double longitude, float heading) = 0;

    virtual bool SetZoomLevel(ViewId viewId, float zoomLevel) = 0;

    /*! This is a version of SetZoomLevel that allows the client to
     *  specify a point to zoom in on based on screen coordinates. The
     *  client can request that the engine merely pan to the new point
     *  (with no change in zoom level) by simply specifying the
     *  current zoom level. */
    virtual bool SetZoomLevel(ViewId viewId, float zoomLevel, uint32_t x, uint32_t y, double &lat, double &lon) = 0;
    
    virtual float GetZoomLevel(ViewId viewId) = 0;


    /*! The SmoothZoom functions are intended to support zoom in/out
     *  buttons. */
    virtual bool BeginSmoothZoomIn(ViewId viewId) = 0;
    virtual bool BeginSmoothZoomOut(ViewId viewId) = 0;
    virtual bool EndSmoothZoom(ViewId viewId) = 0;

    
    /*! Pan and zoom (but not rotate) view to encompass given region within given rect.
     *  The rect is specified by parameters of x, y, width and height.
     *  If gridAligned is false, the rectangle region can be encompassed in the giver rect
     *  in arbitary rotation. In fact the rect covers the circumcircle of the region in 
     *  this case. If gridAligned is true, the rect covers the rectangle of the region only 
     *  when it's in 2D north up mode.
     */
    virtual bool ShowRegion(ViewId viewId, 
                            double northLat, 
                            double westLon, 
                            double southLat, 
                            double eastLon,
                            uint32_t x, 
                            uint32_t y, 
                            uint32_t width, 
                            uint32_t height,
                            bool gridAligned = false) = 0;

    /*! Calculate the zoom level and centerpoint needed to encompass given region within given rect 
     *  The rect is specified by parameters of x, y, width and height.
     *  If gridAligned is false, the rectangle region can be encompassed in the giver rect
     *  in arbitary rotation. In fact the rect covers the circumcircle of the region in
     *  this case. If gridAligned is true, the rect covers the rectangle of the region only
     *  when it's in 2D north up mode.
     */
    virtual bool CalcRegion(ViewId viewId, 
                            double northLat, 
                            double westLon, 
                            double southLat, 
                            double eastLon,
                            uint32_t x, 
                            uint32_t y, 
                            uint32_t width, 
                            uint32_t height,
                            float& zoomLevel,
                            double& lookAtLatitude,
                            double& lookAtLongitude,
                            bool gridAligned = false) = 0;
    
    /*** Operations ***/

    /*! Causes the engine to update its internal state in preparation for
     *  drawing the next frame of animation, render that frame, and then
     *  build any geometry required for the following frame.
     */
    virtual void UpdateAndRenderAndBuild(float deltaSeconds) = 0;
    
    /*! Causes the engine to update its internal state in preparation for
     *  drawing the next frame of animation and rendering that frame, without
     *  building any additional geometry.
     */
    virtual void UpdateAndRender(float deltaSeconds) = 0;
    
    /*! Causes the engine to build any geometry required for the following
     * frame.
     */
    virtual void Build() = 0;
    
    /*! Causes the engine to update its internal state in preparation for
     *  drawing the next frame of animation.
     */
    virtual void Update(float deltaSeconds) = 0;
    
    /*! Causes the engine to render a frame of animation, returning the current
     *  engine state variables.
     */
    virtual void Render() = 0;

    /*! Get the state of the engine for all views, as of the last call
     *  to Render(), UpdateAndRender(), or UpdateAndRenderAndBuild()
     */
    
    //By View
    virtual void Render(ViewId viewId) = 0;
    virtual void Update(ViewId viewId, float dt) = 0;
    virtual void UpdateAndRender(ViewId viewId, float dt) = 0;
    virtual void UpdateAndRenderAndBuild(ViewId viewId, float dt) = 0;
    virtual void Build(ViewId viewId) = 0;

    
    virtual void GetEngineState(EngineStateObject & state) = 0;
    
    /*! Get the state of the engine for the given view, as of the last call
     *  to Render(), UpdateAndRender(), or UpdateAndRenderAndBuild()
     */
    virtual bool GetViewState(ViewId viewId, EngineState & state) = 0;

    /*! Enabled views are rendered. Disabled views skip rendering, but
     *  update and build still happen.
     */
    virtual bool EnableView(ViewId viewId) = 0;

    /*! Hide all annotations associated with the given layer */
    virtual bool DisableView(ViewId viewId) = 0;

    /*** Data Input ***/
    
    /*! Sets the current "vehicle" position tracked by the engine. There
     *  can be only one vehicle in the system at any time, but the vehicle
     *  may be represented in several ways.
     */
    virtual void SetVehiclePosition(double degreesLatitude, 
                                    double degreesLongitude, 
                                    float heading,
                                    uint32_t meterAccuracy) = 0;

    /*! This version of SetVehiclePosition supports predictive camera
     *  motion when given a position ahead and behind the vehicle by
     *  some amount.
     */

    virtual void SetVehiclePosition(Tn::math::LatLonHeight const & position,
                                    double heading,
                                    uint32_t meterAccuracy,
                                    Tn::math::LatLonHeight const & ahead,
                                    Tn::math::LatLonHeight const & behind) = 0;
    
    /*! Set radius of the range of the vehicle in meters. The inner range with 
     *  first radius shall be covered with the range with second radius.
     */
    virtual void SetVehicleRangeRadius(double radius1, double radius2) = 0;
    
    /*! Set GPS accuracy animation according to configurations.
     */
    virtual void SetGPSAccuracyAnimation(bool accurate, double radius) = 0;
    
    /*! Specify a shape point on route. If the point is valid and not at the 
     *  begin of the route, then a line segment of active route is visible only 
     *  if it is between the point and the end or between the vehicle and the 
     *  point.
     */
    virtual void SetRoutePositionAheadOfVehicle(std::string const & routeName,
                                                uint32_t segmentIndex,
                                                uint32_t edgeIndex,
                                                uint32_t pointIndex) = 0;
    
    /*! Adds a client-loaded graphic ID to the OpenGL engine */
    TNMAP_DEPRECATED(
    virtual GraphicId AddAnnotationGraphic(uint8_t *bytes, 
                                           uint32_t width, 
                                           uint32_t height) = 0;
    )
    
    virtual GraphicId AddAnnotationGraphic(Tn::Foundation::shared_array<uint8_t> bytes,
                                           uint32_t size,
                                           uint32_t width = 0,
                                           uint32_t height = 0,
                                           Tn::Graphics::eColorFormat format = Tn::Graphics::eColorFormat_RGBA) = 0;

    /*! Adds a proxy-loaded graphic ID to the OpenGL engine */
    virtual GraphicId AddAnnotationGraphic(std::string const & source) = 0;
    
    /*! Removes a graphic ID from the OpenGL engine */
    virtual bool RemoveAnnotationGraphic(GraphicId id) = 0;
    
    virtual TnModelId AddModel(Tn::Foundation::shared_ptr<Tn::openGLMap::Model> const & model) = 0;
    virtual bool      RemoveModel(TnModelId const & modelId) = 0;
    virtual TnCanvasModelId AddModelInstance(ViewId viewId,
                                             TnModelId modelId) = 0; //worry about the relative vs absolute model later
    virtual bool      RemoveModelInstance(ViewId viewId,
                                          TnCanvasModelId canvasModelId) = 0;
    
    virtual bool      SetModelAlphaValue(TnModelId const & modelId, uint8_t alpha) = 0;

    /*! Adds a client-loaded 3D model graphic ID to the OpenGL engine */
    virtual ModelId AddModel(uint8_t* bytes, size_t size) = 0;
    
    /*! Adds a proxy-loaded 3D model graphic ID to the OpenGL engine */
    virtual ModelId AddModel(std::string const & source) = 0;

    /*! Removes a 3D model graphic ID from the OpenGL engine */
    virtual bool RemoveModel(ModelId const & modelId) = 0;

    /*! Adds a model-based annotation to the engine */
    virtual AnnotationId AddModelAnnotation(ViewId viewId, 
                                            eTnMapLayer layer,  
                                            ModelId modelId,
                                            double degreesLatitude,
                                            double degreesLongitude, 
                                            double height,
                                            Pickable::const_ptr const & pickable) = 0;
    
    //Calls above without a pickable
    AnnotationId AddModelAnnotation(ViewId viewId,
                                    eTnMapLayer layer,
                                    ModelId modelId,
                                    double degreesLatitude,
                                    double degreesLongitude,
                                    double height)
    { return AddModelAnnotation(viewId, layer, modelId, degreesLatitude, degreesLongitude, height, Pickable::const_ptr()); }

    /*! A 2D annotation is placed in the view's screen coordinate
     *  system. */
    TNMAP_DEPRECATED(
    virtual AnnotationId 
    Add2DAnnotation(ViewId viewId, 
                    eTnMapLayer layer, 
                    GraphicId graphicId, 
                    uint32_t x, 
                    uint32_t y, 
                    uint32_t pixelWidth, 
                    uint32_t pixelHeight) = 0;
    )

     /*! A 2D annotation is placed in the view's screen coordinate
     *  system. Also accepts a bounding area of the GrahpicId to be used */
    TNMAP_DEPRECATED(
    virtual AnnotationId 
    Add2DAnnotation(ViewId viewId, 
                    eTnMapLayer layer, 
                    GraphicId graphicId, 
                    uint32_t x, 
                    uint32_t y, 
                    uint32_t pixelWidth,    // Size of 2d anntation (total width)
                    uint32_t pixelHeight,   // Size of 2d annotation (total height)
                    float left,          //Texel coordinate for left bound
                    float bottom,        //Texel coordinate for bottom bound
                    float right,         //Texel coordinate for right bound
                    float top) = 0;      //Texel coordinate for top bound
    )

    virtual bool Reposition2DAnnotation(AnnotationId annotationId,
                                        uint32_t x, 
                                        uint32_t y, 
                                        uint32_t pixelWidth, 
                                        uint32_t pixelHeight) = 0;

    virtual TnMapArrowParams GetDefaultArrowParams(ViewId viewId, std::string const & style) const = 0;
    
    /*! The returned object is initialized using a private configuration, each view has it's own config
     *  The viewId specifies which view's config is used to initialize the returned object */
    virtual TnMapAnnotationParams GetDefaultAnnotationParams(ViewId viewId, std::string const & style) const = 0;
    
    virtual TnMapBillboardParams GetDefaultBillboardParams(ViewId viewId, std::string const & style) const = 0;

    virtual AnnotationId
    CreateAnnotation(ViewId viewId,
                     TnMapAnnotationParams const & annotationParams,
                     GraphicId const & graphicId,
                     Pickable::const_ptr const & pickable) = 0;

    virtual AnnotationId
    CreateAnnotation(ViewId viewId,
                     TnMapBillboardParams const & annotationParams,
                     Pickable::const_ptr const & pickable) = 0;

    AnnotationId
    CreateAnnotation(ViewId viewId,
                     TnMapBillboardParams const & annotationParams)
    { return CreateAnnotation(viewId, annotationParams, Pickable::const_ptr()); }

    AnnotationId
    CreateAnnotation(ViewId viewId,    
                     TnMapAnnotationParams const & annotationParams)
    { return CreateAnnotation(viewId, annotationParams, GraphicId(), Pickable::const_ptr()); }
    
    AnnotationId
    CreateAnnotation(ViewId viewId,    
                     TnMapAnnotationParams const & annotationParams,
                     GraphicId const & graphicId)
    { return CreateAnnotation(viewId, annotationParams, graphicId, Pickable::const_ptr()); }
    
    AnnotationId
    CreateAnnotation(ViewId viewId,
                     TnMapAnnotationParams const & annotationParams,
                     Pickable::const_ptr const & pickable)
    { return CreateAnnotation(viewId, annotationParams, GraphicId(), pickable); }

    /*! Remove an annotation from the view, given its ID */
    virtual bool RemoveAnnotation(ViewId viewId, AnnotationId annotationId) = 0;

    /*! Update an annotation location from the view, given its ID */
    virtual void SetAnnotationLocation(AnnotationId annotationId, double latitude, double longitude, double altitude = 0.0, double heading = 0.0) = 0;

    /*! Enable the display of a given annotation */
    virtual bool EnableAnnotation(AnnotationId annotationId) = 0;
    
    /*! Disable the display of a given annotation */
    virtual bool DisableAnnotation(AnnotationId annotationId) = 0;
    
    /*! Hide the display of a given annotation. Its place may be kept */
    virtual bool HideAnnotation(AnnotationId annotationId) = 0;
    
    /*! Unhide the display of a given annotation */
    virtual bool UnhideAnnotation(AnnotationId annotationId) = 0;

    /*! Enable the display of annotations with given pickable id */
    virtual bool EnableAnnotation(ViewId viewId, uint64_t pickableId) = 0;

    /*! Disable the display of annotations with given pickable id */
    virtual bool DisableAnnotation(ViewId viewId, uint64_t pickableId) = 0;

    /*! Hide the display of annotations with given pickable id. Its place may be kept */
    virtual bool HideAnnotation(ViewId viewId, uint64_t pickableId) = 0;

    /*! Unhide the display of annotations with given pickable id */
    virtual bool UnhideAnnotation(ViewId viewId, uint64_t pickableId) = 0;

    /*! Remove all annotations associated with the given layer */
    virtual void ClearAnnotationLayer(ViewId, eTnMapLayer layer) = 0;

    /*! Show all annotations associated with the given layer */
    virtual bool EnableAnnotationLayer(ViewId viewId, eTnMapLayer layer) = 0;

    /*! Hide all annotations associated with the given layer */
    virtual bool DisableAnnotationLayer(ViewId viewId, eTnMapLayer layer) = 0;

    /*! Enable the display for all annotation in given immunity group */
    virtual bool EnableAnnotationByGroup(ViewId viewId, std::string const & groupName) = 0;

    /*! Disable the display for all annotation in given immunity group */
    virtual bool DisableAnnotationByGroup(ViewId viewId, std::string const & groupName) = 0;

    /*! Bloom a set of screen annotations, thus presenting them as a
     *  pop-up menu of sorts.
     *
     *  They must be created as annotations of type
     *  eTnMapAnnotationType_ScreenBloom
     *
     *  They are expected to have the same map location. (The
     *  animation wouldn't make sense otherwise.)
     *
     *  The radius is the distance the annotation should move from the
     *  center of the blossom (in screen points).
     */
    virtual bool Bloom(ViewId viewId,
                       std::list<AnnotationId> const & annotations,
                       float radius) = 0;

    virtual bool Shrivel(ViewId viewId,
                         std::list<AnnotationId> const & annotations) = 0;

    /*! Returns annotations (engine-fed or proxy-fed) sorted based on their
     *  distance on the screen from the specified screen coordinate. Results
     *  are returned as a sorted vector. The size of the vector is limited by
     *  the given 'radius' value.
     **/
    virtual AnnotationSearchResults GetNearest(ViewId viewId, uint32_t x, uint32_t y, float radius) = 0;
    
    virtual boost::optional<Tn::math::LatLonHeight> GetLatLon(ViewId viewId, uint32_t x, uint32_t y) = 0;

    /*! Reload the routes of the specified name.  */
    virtual void ReloadRoute(ViewId viewId, std::string const & name) = 0;
    
    /*! Enable the displaying of the routes of the specified name. */
    virtual bool EnableRoute(ViewId viewId, std::string const & name) = 0;

    /*! Disable the displaying of the routes of the specified name. */
    virtual bool DisableRoute(ViewId viewId, std::string const & name) = 0;

	 /*! Enable the displaying of the routes of the specified name. */
    virtual bool EnableAllRoutes(ViewId viewId) = 0;

    /*! Disable the displaying of the routes of the specified name. */
    virtual bool DisableAllRoutes(ViewId viewId) = 0;
    
    /*! Hightlight a route */
    enum eHighlightMethod
    {
        eHighlightMethod_Desaturate = 0,
        eHighlightMethod_DesaturateBackgroundToo
    };

    /* Returns true if ViewId is valid. */
    virtual bool HighlightRoute(ViewId viewId, 
                                std::string const & name, 
                                eHighlightMethod m = eHighlightMethod_Desaturate) = 0;
    
    virtual bool UnhighlightRoute(ViewId viewId) = 0;
    
    /*! Re-order route display, given a list of route names, stack from top to bottom. */
    virtual bool ReorderRoutes(ViewId viewId, std::list<std::string> const & names) = 0;
    
    /*! Pan camera along the length of a route */
    virtual void PanAlongRoute(ViewId viewId, std::string const & routeNames) = 0;

    /*! Pan and zoom (but not rotate) view to encompass given routes
     *  within given screen-relative rectangle. The rectangle's
     *  coordinates are automatically transformed to take into account
     *  the current screen orientation, and the origin is in the
     *  upper-left corner.
     *
     *  The rect is specified by parameters of x, y, width and height.
     *  If gridAligned is false, the rectangle region can be encompassed in the giver rect
     *  in arbitary rotation. In fact the rect covers the circumcircle of the region in
     *  this case. If gridAligned is true, the rect covers the rectangle of the region only
     *  when it's in 2D north up mode.
     *
     *  Note that this currently doesn't take into account skew, so
     *  for this to work, the HorizontalOffset and VerticalOffset must
     *  be set to zero.
     */

    TNMAP_DEPRECATED(virtual bool ShowRegionForRoutes(ViewId viewId, std::set<std::string> const & routeNames,
                                     uint32_t x, uint32_t y, uint32_t width, uint32_t height, bool gridAligned = false) = 0;)	 // deprecated 2.1

    virtual bool ShowRegionForRoutes(ViewId viewId, std::list<std::string> const & routeNames,
                                     uint32_t x, uint32_t y, uint32_t width, uint32_t height, bool gridAligned = false) = 0;

    /*! Enable the display of a route turn arrow */
    virtual bool EnableTurnArrow(ViewId viewId, int segment) = 0;
    
    /*! Enable the display of a route turn arrow (more specific) */
    virtual bool EnableTurnArrow(ViewId viewId, std::string const & routeName, int segment) = 0;
    
    /*! Disable the display of a route turn arrow */
    virtual bool DisableTurnArrow(ViewId viewId, int segment) = 0;
    
    /*! Disable the display of a route turn arrow (more specific) */
    virtual bool DisableTurnArrow(ViewId viewId, std::string const & routeName, int segment) = 0;
    
    /*! Disable the display of all route turn arrows */
    virtual bool DisableAllTurnArrows(ViewId viewId) = 0;
    
    /*! Get a raster snapshot of the current frame */
    virtual TnBitmap GetBitmapSnapshot(int xOrigin, int yOrigin, uint32_t width, uint32_t height) const = 0;
    
    enum eTerrainGridResolution
    {
        eTerrainGridResolution_32,
        eTerrainGridResolution_64,
        eTerrainGridResolution_128,
        eTerrainGridResolution_256
    };
    /*! Set the grid resolution of the terrain */
    virtual void SetTerrainGridResolution(eTerrainGridResolution resolution) = 0;

    /*! Update the parameters of annotation. Currently, only the texture
     *  image name and texture coordinates for icon can be updated dynamically. 
     *  The annotation is identified with the ID of pickable object optionally 
     *  assigned by the client when the pickable was created.
     */
    virtual bool UpdateAnnotation(ViewId viewId, std::map<uint64_t, TnMapAnnotationParams> const & textureSet) = 0;

    // This is not thread safe. Call from Update/Render thread after
    // Update has been called. It returns an empty optional if the
    // coordinate is clipped by the near or far clipping plane.
    virtual boost::optional<Tn::math::Point2f> WorldToViewport(ViewId viewId, Tn::math::LatLonHeight const &) const = 0;

    
    enum eAnnotationAnimation
    {
        eAnnotationAnimation_None =   0,
        eAnnotationAnimation_Fading = 1 << 0,
        eAnnotationAnimation_Halo = 1 << 1,
        eAnnotationAnimation_Max = eAnnotationAnimation_Halo
    };
    
    /*! Set animation for screen annotation. The parameter animationEnabled is 
     *  the bit mask for animation enabled. For bits not set in it, corresponding
     *  animation will be disabled.
     */
    virtual void SetScreenAnnotationAnimation(AnnotationId annotationId, uint32_t animationsEnabled, TnMapAnnotationAnimationParams const & params) = 0;
    
    
protected:

    ITnMapEngine() {};

public:
    virtual ~ITnMapEngine() {};
};

#endif
