/***************************************************************************
*                                                                          *
*  Copyright (C) Golaem S.A.  All Rights Reserved.                         *
*                                                                          *
***************************************************************************/

#pragma once

#include "glmSDKAPI.h"
#include "limits.h"
#include "glmArray.h"
#include "glmVector3.h"
#include "glmMap.h"
#include "glmList.h"
#include "glmEntity.h"
#include "glmTrafficCrossing.h" 
#include "glmTrafficVehicle.h"
#include "glmTrafficPositionInRoadData.h"
#include "glmTrafficPerceptionData.h"
#include "glmTrafficDetectionData.h"
#include "glmTrafficPlanningData.h"
#include "glmTrafficCrossingTrajectory.h"

namespace glm
{
	class TrafficContext;
	class Crossing;

	// stack of crossings & Road trajectories (we have abscissa & trajectory in both elements in different ways : crossings have an integrated pre-computed trajectory, roads follow predefined lanes and keep their index
	class GLM_SDK_API TrafficElement
	{
	public:
		TrafficElement();
		~TrafficElement();

		bool isCrossing();
		bool isRoad();

		// helper
		float getLocalVehicleAbscissa(); 

		bool _isCrossing;
		PositionInRoadData _road;	// light-weight - aggregated to avoid realloc
		CrossingTrajectory* _crossing;	// use CrossingTrajectory definition if any, to avoid copy and alloc

		// runtime helpers:
		float _vehicleToElementStartDistance; // used when registering roads. If the trailer is at 0 of this segment, then this segment starts at 0, negative for backward
		float _vehicleToElementEndDistance; // used when registering roads. If the trailer is at 0, then this segment end distance is at +[segLentgh]
        bool _roadIsStop = false;
        bool _stopDone = false;
	};

	//-------------------------------------------------------------------------
	// Traffic component for a simulated entity.
	class GLM_SDK_API TrafficEntityComponent
	{
		friend class TrafficContext;
		friend class Crossing;
		//template <class TValue>
		//friend class Array;

	public:
		GLM_CLASS_STAT_ID( glm::MemoryStatIds::Simulation)

		TrafficEntityComponent();
		virtual ~TrafficEntityComponent();
		
		struct GotoMode
		{
			enum Value
			{
				STOPPED,
				WANDER,
				TARGET,
				END
			};
		};

		/**
		* \name TrafficComponentData method definitions.
		*/
		//@{
		void doInit(TrafficContext& trafficContext, Entity& entity);
		void doUpdate(TrafficContext& trafficContext, Entity& entity, Time dt);
		void doFinish(TrafficContext& trafficContext, Entity& entity);
		//@}

		void bindToClosestRoad(TrafficContext& trafficContext, const Entity& entity);

		void replan(TrafficContext& trafficContext, const Entity& entity);
	
		void pickNextLane(TrafficContext& trafficContext, const Entity& entity);

		void computeTimedCrossingAbscissa(CrossingRequest& crossingRequest, float currentSpeed, float dtf);

		void clear(); // when reseting a traffic component to reinit it with another trailer (trains reversing order)

		bool getPositionInTraffic(TrafficContext& trafficContext, const Vector3& targetPosition, PositionInRoadData& foundPositionInRoad/*, VehicleBox* trailerBox = NULL*/);

		bool isTargetReached(const TrafficWorld& trafficWorld, Entity& entity) const;
		float getRemainingDistance(const TrafficWorld& trafficWorld) const;
		float getLaneChangeOffset() const;
		bool isSwitchingLeft(const TrafficWorld& trafficWorld) const;
		bool isSwitchingRight(const TrafficWorld& trafficWorld) const;
		bool isTurningLeft(const TrafficWorld& trafficWorld) const;
		bool isTurningRight(const TrafficWorld& trafficWorld) const;
		bool isBraking() const;
        float getSteerAngle(const TrafficWorld& trafficWorld, const Entity& entity);

		void clearElementsAfterCurrent(TrafficContext& trafficContext);

	private:

		float getPolylineAbscissaFromPosition(glm::Array<Vector3>& polyLineVertices, glm::PODArray<float>& polyLineCumulativeLengths, float maxLength, const Vector3& position) const;
		bool getCarPosition(const TrafficWorld& trafficWorld, Vector3& carPosition);

		void computeCrossingTrajectory(TrafficContext& trafficContext, int crossingIndex, PositionInRoadData& inRoadData, PositionInRoadData& outRoadData, CrossingTrajectory& crossingChunk);

		void driveOnCrossing(CrossingTrajectory& crossingChunk, float& distance, Vector3& targetPosition);
		void driveOnLane(PositionInRoadData& inRoadData, TrafficContext& trafficContext, float& distance, Vector3& targetPosition, float dtf, float speed);

		void pushAllVerticesBetweenAbscissa(TrafficContext& trafficContext, PositionInRoadData& laneData, float startAbscissa, float endAbscissa,
			glm::Array<glm::Vector3>& whereToPushPositions, glm::PODArray<float>& whereToPushLength, glm::PODArray<float>& whereToPushCumulativeLength);

		//void postureToTraffic(TrafficContext& trafficContext, const Entity& entity);
		//void postureFromTraffic(TrafficContext& trafficContext, Entity& entity);

		void initTrafficRNG(uint32_t seed);
		void clearTrafficRNG();

	public:

		// configuration 
		Vehicle _vehicleProps;

		// runtime / helpers : keep all traffic elements where this element boxes are. 
		// Note that GlmList has an automatic internal recycler to avoid exploding allocations
		GlmList<TrafficElement> _nextTrafficElements; // stack of crossings & roads, used by boxes to drive, but also stores forecast roads that have already been picked when detecting
		GlmList<TrafficElement>::iterator _vehicleDriveOnElement; // the trailer center is on this element

		// planned path runtime data (when using a goto)
		GotoMode::Value _gotoMode;
		float _targetReachedTreshold;
		TrafficPlanningData _plannedRoads;

		TrafficDetectionData _detectionData;

		// threadSafe and reproducible access for other components (basically holds the data of previous frame)
		TrafficPerceptionData _perceptionData;

		// car trajectories we request / register once accepted
		GlmList<CrossingRequest> _crossingRequests;

		RandomNumberGenerator* _trafficRng;

		bool _isTurningRight;
		bool _isTurningLeft;
		bool _brakedLastFrame; // for low pass filter
		bool _isBraking;

		glm::PODArray<int> _registeredRoads; // the component boxes can span on several roads, let s record all. Record it to unregister when out of road

		// command : bind at start of traffic behavior
		bool _bindToClosestRoad;

		// is a traffic behavior running ?
		bool _trafficBehaviorRunning;
	};
}
