/********************************************************************************
*                                                                               *
*   Copyright (C) Golaem S.A.  All Rights Reserved.                             *
*                                                                               *
*   Original code & license:                                                    *
*                                                                               *
*   OpenSteer -- Steering Behaviors for Autonomous Characters                   *
*                                                                               *
*   Copyright (c) 2002-2003, Sony Computer Entertainment America                *
*   Original author: Craig Reynolds <craig_reynolds@playstation.sony.com>       *
*                                                                               *
*   Permission is hereby granted, free of charge, to any person obtaining a     *
*   copy of this software and associated documentation files (the "Software"),  *
*   to deal in the Software without restriction, including without limitation   *
*   the rights to use, copy, modify, merge, publish, distribute, sublicense,    *
*   and/or sell copies of the Software, and to permit persons to whom the       *
*   Software is furnished to do so, subject to the following conditions:        *
*                                                                               *
*   The above copyright notice and this permission notice shall be included in  *
*   all copies or substantial portions of the Software.                         *
*                                                                               *
*   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  *
*   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,    *
*   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL    *
*   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER  *
*   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING     *
*   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER         *
*   DEALINGS IN THE SOFTWARE.                                                   *
*                                                                               *
********************************************************************************/

#pragma once

#include "glmSDKAPI.h"
//#include <glmIEntityComponent.h>
#include <glmEntity.h>

#include "glmBruteForceProximityDatabase.h"
#include "glmLocalityQueryProximityDatabase.h"

namespace glm
{
class BoidContext;
class BoidFlockBehavior;
class AbstractFlockPerchingSupport;

/**
* \brief steerable point mass with a velocity-aligned local coordinate system.
*
* \author Golaem <dev@golaem.com>
*/
class GLM_SDK_API BoidEntityComponent
{
    friend class BoidFlockBehavior;
    friend class BoidContext;

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

    /**
	 * \brief Type describing the different modes available for synchronizing the end of the parallel process
	 */
    struct PerchingPhase
    {
      public:
        enum Value
        {
            START_PERCHING,
            INCOMING,
            PERCHED,
            OUTGOING,
            NONE
        };
    };

    BoidEntityComponent();
    BoidEntityComponent(const BoidEntityComponent& other) { *this = other; }
    ~BoidEntityComponent();

    Vector3 _steering;     //<! steering force
    float _steeringWeight; //<! steering weight to normalize steering before applying flocking force, this avoid flattening flock effect
    Vector3 _flocking;     //<! flocking force

    float _mass;              /// mass (defaults to unity so acceleration=force)
    float _radius;            /// size of bounding sphere, for obstacle avoidance, etc.
    float _maxForce;          /// the maximum steering force this vehicle can apply (steering force is clipped to this magnitude)
    float _maxSpeed;          /// the maximum speed this vehicle is allowed to move (velocity is clipped to this magnitude
    bool _setBodyOrientation; /// also set body orientation, additionnaly to moveOrientation ?
    float _bankAmount;

    float _tendencyToAlignUp; // coefficient from 0 to 1 to align up

    bool _computeDebugInfos;         // not thread safe, only used in Maya / synchronous monothread visu, obstacles raycasts are the only thing needing computation, last steering and velocity are available anyway in roles
    Vector3* _debugObstaclesRaycast; // not thread safe, only used in Maya / synchronous monothread visu
    Vector3 _debugLastSteering;

    bool _hasIntersection;
    Vector3 _lastIntersection;

    Time _perchedTimer;
    Vector3 _perchedPosition;
    Vector3 _initialPerchedPosition;
    Vector3 _perchedNormal;
	float _startingPerchDistance;
    Quaternion _perchedOrientation;        // deduced from normal + incoming forward or random
    Quaternion _initialPerchedOrientation; // original perchedOrientation backUp for transform update
    int _perchingPhase;                    // 0=approach, 1=perched, 2=leaving
    bool _adaptToActiveSupport;
    bool _randomOrientationPerching;
    AbstractFlockPerchingSupport* _activeSupport; // NULL ot the support used to perch on
    void* _activeSupportData;                     // NULL ot the support used to perch on
    float _incomingSpeed;
    float _stoppingInterpolationFactor;
    float _incomingAngle;
    Vector3 _currentUpVector;
    Vector3 _stoppingTargetUpVector;

    float _maxUpAngleWithZ; // default to 180, can return the entity

    float _separationWeight;
    float _alignmentWeight;
    float _cohesionWeight;

    glm::PODArray<AbstractFlockPerchingSupport*> _currentPerchingSupports;

    bool _hasSteerTarget = false;
    Vector3 _localSteerTargetPosition = Vector3(0, 0, 0);
    Vector3 _steerTargetPosition = Vector3(0, 0, 0);
    float _distanceToLocalTarget = 0;
    float _distanceToTarget = 0;
    bool _steerTargetReached = false;

    BoidEntityComponent& operator=(const BoidEntityComponent& other);

    void setFlockingSteeringRatio(float ratio)
    {
        _steerFlockRepartition = glm::clamp(ratio, 0.f, 1.f);
    }

    void checkPerchingIntersection(Entity& entity);

    void initBoidRNG(uint32_t seed);
    void clearBoidRNG();

  private:
    /// apply a given steering force to our momentum, adjusting our orientation to maintain velocity-alignment.
    void applySteeringForce(Entity& entity, float dtf, const BoidContext& boidContext /*, bool smoothAcceleration*/);

    /// adjust the steering force passed to applySteeringForce. default is to disallow backward-facing steering at low speed.
    void adjustRawSteeringForce(Entity& entity, Vector3& force) const;

    /// measure path curvature (1/turning-radius), maintain smoothed version
    void measurePathCurvature(Entity& entity, Time dt);

    /// align entity's forward axis with new velocity, moveDirection must be normalized
    void computeOrientation(Entity& entity, const Vector3& moveDirection, float dtf, const BoidContext& boidContext);

    /// handle boundary if outside flock radius
    void handleBoundary(const BoidContext& context, Entity& entity);

    /// handle perching phases
    void handlePerching(Entity& entity, float dtf);

    //GLM_DISALLOW_COPY_AND_ASSIGN(BoidEntityComponent);

    int _activeBehaviorsCount;

    float _curvature;
    float _smoothedCurvature;
    Vector3 _smoothedAcceleration;
    Vector3 _lastPosition;

    Time _lastObstacleDetectedTimestamp;

    AbstractProximityDatabase::AbstractTokenType* _proximityToken;

    float _steerFlockRepartition; //0.f = only steering, 1.f : only flocking

    RandomNumberGenerator* _boidRng;

	bool _isRegisteredInNbDb;
};
}
