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

#pragma once

#include "glmVector2.h"
#include "glmVector3.h"
#include "glmSegment2.h"

namespace glm
{
    class Triangle2;
    class Box2;
    class Segment3;
    class Triangle3;
    class FastBoundingBox;

    struct GLM_CORE_API Line
    {
        Vector3 _P; // point
        Vector3 _D; // direction (normalized)
    };

    struct GLM_CORE_API CenteredSegment
    {
        Vector3 _C; // center
        Vector3 _D; // direction (normalized)
        float _e;   // half extent
    };

    struct GLM_CORE_API CenteredRectangle
    {
        Vector3 _C;    // center
        Vector3 _D[2]; // first and second direction (normalized)
        float _e[2];   // half extent along each direction
    };

    struct GLM_CORE_API CenteredBox
    {
        Vector3 _C;    // center
        Vector3 _D[3]; // first, second and third direction (normalized)
        Vector3 _e;    // half extent along each direction
    };

    float distance(const Vector2& point1, const Vector2& point2);
    float distance2(const Vector2& point1, const Vector2& point2);
    float distance(const Vector3& point1, const Vector3& point2);
    float distance2(const Vector3& point1, const Vector3& point2);
    float distance(const Vector2& point, const Segment2& segment);
    float GLM_CORE_API distance2(const Vector2& point, const Segment2& segment);
    float GLM_CORE_API distance2(const Vector3& point, const Segment3& segment);
    float GLM_CORE_API distance2ClosestPoint(const Vector3& point, const Segment3& segment, Vector3& closestPoint);
    float GLM_CORE_API distance2(const Vector3& point, const Segment3& segment, const Vector3& upVector);
    float distance(const Segment2& segment1, const Segment2& segment2);
    float GLM_CORE_API distance2(const Segment2& segment1, const Segment2& segment2);
    float GLM_CORE_API distance2(const Segment3& segment1, const Segment3& segment2);
    float GLM_CORE_API distance2(const Segment3& segment1, const Segment3& segment2, const Vector3& upVector);
    float GLM_CORE_API distance(const Triangle2& triangle, const Vector2& point);
    float GLM_CORE_API distance2Optimized(const Triangle2& triangle, const Vector2& point);
    float GLM_CORE_API distance2Optimized(const Triangle3& triangle, const Vector3& point);
    float GLM_CORE_API distance2(const Triangle2& triangle, const Segment2& segment);
    float GLM_CORE_API distance2Optimized(const Triangle2& triangle, const Segment2& segment);
    float GLM_CORE_API distance2Optimized(const Triangle3& triangle, const Segment3& segment);
    float GLM_CORE_API distance2Optimized(const Triangle3& triangle, const Segment3& segment, const Vector3& upVector);
    float GLM_CORE_API distance2Optimized(const Triangle2& triangle1, const Triangle2& triangle2);
    float distance(const Box2& box, const Vector2& position);
    float GLM_CORE_API distance2(const Box2& box, const Vector2& position);
    float GLM_CORE_API distance2(const Triangle3& tri, const Segment3& segment);
    float GLM_CORE_API distance2(const Triangle3& tri, const Segment3& segment, const Vector3& upVector);
    float GLM_CORE_API distance2(const Triangle3& tri, const Vector3& point, Vector3& closestPoint);
	float GLM_CORE_API distance2(const Vector3& triangleA, const Vector3& triangleB, const Vector3& triangleC, const Vector3& point, Vector3& closestPoint);

    float GLM_CORE_API distance2(const Vector3& point, const Line& line, float& tClosest);
    float GLM_CORE_API distance2(const Vector3& point, const CenteredRectangle& rect, float rClosest[2]);
    float GLM_CORE_API distance2(const Vector3& point, const CenteredBox& box, float bClosest[3]);
	float GLM_CORE_API distance2(const Vector3& point, const Vector3& boxCenter, const Vector3& boxHalfExtent, Vector3& bClosest);
    float GLM_CORE_API distance2(const Line& line0, const Line& line1, float& l0Closest, float& l1Closest, float precision = GLM_NUMERICAL_PRECISION);
    float GLM_CORE_API distance2(const Line& line, const CenteredSegment& seg, float& lClosest, float& sClosest, float precision = GLM_NUMERICAL_PRECISION);
    float GLM_CORE_API distance2(const Line& line, const CenteredRectangle& rect, float& tClosest, float sClosest[2], float precision = GLM_NUMERICAL_PRECISION);
    float GLM_CORE_API distance2(const CenteredSegment& seg, const CenteredRectangle& rect, float& tClosest, float sClosest[2], float precision = GLM_NUMERICAL_PRECISION);
    float GLM_CORE_API distance2Optimized(const Line& line, const CenteredBox& box, float& lClosest, float bClosest[3]);
    float GLM_CORE_API distance2(const Line& line, const CenteredBox& box, float& lClosest, float bClosest[3], float precision = GLM_NUMERICAL_PRECISION);
    float GLM_CORE_API distance2Optimized(const CenteredSegment& seg, const CenteredBox& box, float& sClosest, float bClosest[3]);
    float GLM_CORE_API distance2(const CenteredSegment& seg, const CenteredBox& box, float& sClosest, float bClosest[3], float precision = GLM_NUMERICAL_PRECISION);

    //-----------------------------------------------------------------------------
    inline float distance(const Vector2& point1, const Vector2& point2)
    {
        return sqrtf(sqr(point1.y - point2.y) + sqr(point1.x - point2.x));
    }

    //-----------------------------------------------------------------------------
    inline float distance2(const Vector2& point1, const Vector2& point2)
    {
        return sqr(point1.y - point2.y) + sqr(point1.x - point2.x);
    }

    //-----------------------------------------------------------------------------
    inline float distance(const Vector3& point1, const Vector3& point2)
    {
        return sqrtf(sqr(point1.z - point2.z) + sqr(point1.y - point2.y) + sqr(point1.x - point2.x));
    }

    //-----------------------------------------------------------------------------
    inline float distance2(const Vector3& point1, const Vector3& point2)
    {
        return sqr(point1.z - point2.z) + sqr(point1.y - point2.y) + sqr(point1.x - point2.x);
    }

    //-----------------------------------------------------------------------------
    inline float distance(const Vector2& point, const Segment2& segment)
    {
        return sqrtf(distance2(point, segment));
    }

    //-----------------------------------------------------------------------------
    inline float distance(const Segment2& segment1, const Segment2& segment2)
    {
        return sqrtf(distance2(segment1, segment2));
    }

    //-----------------------------------------------------------------------------
    inline float distance(const Segment3& segment1, const Segment3& segment2, const Vector3& upVector)
    {
        return sqrtf(distance2(segment1, segment2, upVector));
    }

    //-----------------------------------------------------------------------------
    inline float distance(const Box2& box, const Vector2& position)
    {
        return sqrtf(distance2(box, position));
    }
} // namespace glm
