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

#pragma once

#include "glmSDKAPI.h"
#include <glmSimulation.h>
#include <glmSimuEntityHandle.h>
#include <glmTrafficUtils.h>
#include <glmTrafficCrossing.h>
#include <glmTrafficRoad.h>
#include <glmTrafficEntityComponent.h>

namespace glm
{
	class GLVisualizationTrafficInterface;

	class Crossing;
	class TrafficContext;

	//-------------------------------------------------------------------------
	class GLM_SDK_API TrafficWorld
	{
		friend class TrafficEntityComponent;
		friend class GLVisualizationTrafficInterface;
		friend class Crossing;
		friend class TrafficContext;

	public: //methods

		GLM_CLASS_STAT_ID( glm::MemoryStatIds::Simulation);

		TrafficWorld();

		~TrafficWorld();

		// accessors 
		int getRoadCount() const { return _roads.sizeInt(); }
		int getCrossingCount() const { return _crossings.sizeInt(); }
		Road& getRoad(int roadIndex) { return _roads[roadIndex]; }
		//Road& getRoad(PositionInRoadData& positionInRoad) { return _roads[positionInRoad._laneDirection]; }
		const Road& getRoad(int roadIndex) const { return _roads[roadIndex]; }
		const Lane& getLane(PositionInRoadData& laneData) const { return _roads[laneData._roadIndex]._lanes[laneData._laneDirection][laneData._laneIndexFromCenter]; }
		Crossing& getCrossing(int crossingIndex) { return _crossings[crossingIndex]; }
		const Crossing& getCrossing(int crossingIndex) const { return _crossings[crossingIndex]; }
		const glm::Array<Crossing>& getCrossings() const { return _crossings; }

		void init();

		// first add crossings then roads
        int addCrossing(const Vector3& position, float radius, bool uTurnForbidden, float keepStraightRatio, bool invertPriority, Crossing::CrossingType::Value crossingType, bool automaticLights, float greenLightTime, float orangeLightTime, float redLightTime, float stopMinTime, float stopMaxTime, const char* name);
        int addRoad(const glm::Array<Vector3>& positions, int startCrossing, int endCrossing, float interlaneSpace, float laneWidth, int forwardLaneCount, int backwardLaneCount, float speedLimit, bool startIsStop, bool endIsStop, const char* name);
		//void setRoadConnections(int roadIndex, int startRoadIndex, int endRoadIndex);

		void setRoadStartCrossingLightStatus(int iRoad, TrafficLightStatus::Value lightStatus);
		TrafficLightStatus::Value getRoadStartCrossingLightStatus(int iRoad);
		void setRoadEndCrossingLightStatus(int iRoad, TrafficLightStatus::Value lightStatus);
		TrafficLightStatus::Value getRoadEndCrossingLightStatus(int iRoad);

        void setRoadStartCrossingIsStop(int iRoad, bool isStop);
        void setRoadEndCrossingIsStop(int iRoad, bool isStop);

		void getNetworkGeometry(glm::Array<Segment3>& _networkGeometry) const;

		void updateForCurrentFrame(int currentFrame, Time currentTime);

		bool isInCrossing(Vector3 position) const;

		void addFrontDistance(float distance) { _maxFrontDistance = max(_maxFrontDistance, distance); }
		void addBackDistance(float distance) { _maxBackDistance = max(_maxFrontDistance, distance); }

		float getMaxFrontDistance() const { return _maxFrontDistance; }
		float getMaxBackDistance() const { return _maxBackDistance; }
		float getMaxDistance() const { return glm::max(_maxBackDistance, _maxBackDistance); }
		int getCurrentFrame() const { return _currentFrame; }

	public: //data
		TrafficDrivingSide::Value	_drivingSide; // 0 = right, 1 left
		bool _laneChangePossible;
		float _laneSwitchDuration;
		float _laneSwitchBrakePercent;

		int	_crossingsSampleCount;
		float _crossingsSpeedRatio;

	private: //data

		glm::Array<Road>		_roads;
		glm::Array<Crossing>	_crossings;
		glm::ComponentsArray<TrafficEntityComponent> _components;
		SpinLock				_carsOnRoadLock;
		float					_maxFrontDistance;
		float					_maxBackDistance;
		int						_currentFrame;
	};
}
