/*
 * Darkflame Universe
 * Copyright 2019
 */

#ifndef MOVINGPLATFORMCOMPONENT_H
#define MOVINGPLATFORMCOMPONENT_H

#include "RakNetTypes.h"
#include "NiPoint3.h"
#include <string>

#include "dCommonVars.h"
#include "EntityManager.h"
#include "Component.h"
#include "eMovementPlatformState.h"
#include "eReplicaComponentType.h"

class Path;

 /**
  * Different types of available platforms
  */
enum class eMoverSubComponentType : uint32_t {
	mover = 4,

	/**
	 * Used in NJ
	 */
	 simpleMover = 5,
};

/**
 * Sub component for moving platforms that determine the actual current movement state
 */
class MoverSubComponent {
public:
	MoverSubComponent(const NiPoint3& startPos);
	~MoverSubComponent();

	void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate);

	/**
	 * The state the platform is currently in
	 */
	eMovementPlatformState mState = eMovementPlatformState::Stationary;

	/**
	 * The waypoint this platform currently wants to traverse to
	 */
	int32_t mDesiredWaypointIndex = 0;

	/**
	 * Whether the platform is currently reversing away from the desired waypoint
	 */
	bool mInReverse = false;

	/**
	 * Whether the platform should stop moving when reaching the desired waypoint
	 */
	bool mShouldStopAtDesiredWaypoint = false;

	/**
	 * The percentage of the way between the last point and the desired point
	 */
	float mPercentBetweenPoints = 0;

	/**
	 * The current position of the platofrm
	 */
	NiPoint3 mPosition{};

	/**
	 * The waypoint the platform is (was) at
	 */
	uint32_t mCurrentWaypointIndex;

	/**
	 * The waypoint the platform is attempting to go to
	 */
	uint32_t mNextWaypointIndex;

	/**
	 * The timer that handles the time before stopping idling and continue platform movement
	 */
	float mIdleTimeElapsed = 0;

	/**
	 * The speed the platform is currently moving at
	 */
	float mSpeed = 0;

	/**
	 * The time to wait before continuing movement
	 */
	float mWaitTime = 0;
};


/**
 * Represents entities that may be moving platforms, indicating how they should move through the world.
 * NOTE: the logic in this component hardly does anything, apparently the client can figure most of this stuff out
 * if you just serialize it correctly, resulting in smoother results anyway. Don't be surprised if the exposed APIs
 * don't at all do what you expect them to as we don't instruct the client of changes made here.
 * ^^^ Trivia: This made the red blocks platform and property platforms a pain to implement.
 */
class MovingPlatformComponent final : public Component {
public:
	static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MOVING_PLATFORM;

	MovingPlatformComponent(Entity* parent, const std::string& pathName);
	~MovingPlatformComponent() override;

	void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;

	/**
	 * Stops all pathing, called when an entity starts a quick build associated with this platform
	 */
	void OnQuickBuildInitilized();

	/**
	 * Starts the pathing, called when an entity completed a quick build associated with this platform
	 */
	void OnCompleteQuickBuild();

	/**
	 * Updates the movement state for the moving platform
	 * @param value the movement state to set
	 */
	void SetMovementState(eMovementPlatformState value);

	/**
	 * Instructs the moving platform to go to some waypoint
	 * @param index the index of the waypoint
	 * @param stopAtWaypoint determines if the platform should stop at the waypoint
	 */
	void GotoWaypoint(uint32_t index, bool stopAtWaypoint = true);

	/**
	 * Starts the pathing of this platform, setting appropriate waypoints and speeds
	 */
	void StartPathing();

	/**
	 * Continues the path of the platform, after it's been stopped
	 */
	void ContinuePathing();

	/**
	 * Stops the platform from moving, waiting for it to be activated again.
	 */
	void StopPathing();

	/**
	 * Determines if the entity should be serialized on the next update
	 * @param value whether to serialize the entity or not
	 */
	void SetSerialized(bool value);

	/**
	 * Returns if this platform will start automatically after spawn
	 * @return if this platform will start automatically after spawn
	 */
	bool GetNoAutoStart() const;

	/**
	 * Sets the auto start value for this platform
	 * @param value the auto start value to set
	 */
	void SetNoAutoStart(bool value);

	/**
	 * Warps the platform to a waypoint index, skipping its current path
	 * @param index the index to go to
	 */
	void WarpToWaypoint(size_t index);

	/**
	 * Returns the waypoint this platform was previously at
	 * @return the waypoint this platform was previously at
	 */
	size_t GetLastWaypointIndex() const;

	/**
	 * Returns the sub component that actually defines how the platform moves around (speeds, etc).
	 * @return the sub component that actually defines how the platform moves around
	 */
	MoverSubComponent* GetMoverSubComponent() const;

private:

	/**
	 * The path this platform is currently on
	 */
	const Path* m_Path = nullptr;

	/**
	 * The name of the path this platform is currently on
	 */
	std::u16string m_PathName;

	/**
	 * Whether the platform has stopped pathing
	 */
	bool m_PathingStopped = false;

	/**
	 * The type of the subcomponent
	 */
	eMoverSubComponentType m_MoverSubComponentType;

	/**
	 * The mover sub component that belongs to this platform
	 */
	void* m_MoverSubComponent;

	/**
	 * Whether the platform shouldn't auto start
	 */
	bool m_NoAutoStart;

	/**
	 * Whether to serialize the entity on the next update
	 */
	bool m_Serialize = false;
};

#endif // MOVINGPLATFORMCOMPONENT_H
