# SPDX-License-Identifier: LGPL-2.1-or-later

# ***************************************************************************
# *   Copyright (c) 2009, 2010 Yorik van Havre <yorik@uncreated.net>        *
# *   Copyright (c) 2009, 2010 Ken Cline <cline@frii.com>                   *
# *                                                                         *
# *   This file is part of the FreeCAD CAx development system.              *
# *                                                                         *
# *   This program is free software; you can redistribute it and/or modify  *
# *   it under the terms of the GNU Lesser General Public License (LGPL)    *
# *   as published by the Free Software Foundation; either version 2 of     *
# *   the License, or (at your option) any later version.                   *
# *   for detail see the LICENCE text file.                                 *
# *                                                                         *
# *   FreeCAD is distributed in the hope that it will be useful,            *
# *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
# *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
# *   GNU Library General Public License for more details.                  *
# *                                                                         *
# *   You should have received a copy of the GNU Library General Public     *
# *   License along with FreeCAD; if not, write to the Free Software        *
# *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
# *   USA                                                                   *
# *                                                                         *
# ***************************************************************************
"""Provides various functions to work with arcs."""
## @package arcs
# \ingroup draftgeoutils
# \brief Provides various functions to work with arcs.

import math
import lazy_loader.lazy_loader as lz

import FreeCAD as App
import DraftVecUtils

from draftgeoutils.general import geomType
from draftgeoutils.edges import findMidpoint

# Delay import of module until first use because it is heavy
Part = lz.LazyLoader("Part", globals(), "Part")

## \addtogroup draftgeoutils
# @{


def isClockwise(edge, ref=None):
    """Return True if a circle-based edge has a clockwise direction.

    Parameters
    ----------
    edge :
        The edge to be analyzed.

    ref : Vector, optional
        The normal around which the direction of the edge is to be determined.
        Defaults to the Z normal vector.

    Returns
    -------
    bool
        Returns True if the edge is clockwise oriented around the ref Vector
        or not.
    """
    if not geomType(edge) == "Circle":
        return True

    v1 = edge.Curve.tangent(edge.ParameterRange[0])[0]
    if DraftVecUtils.isNull(v1):
        return True

    # we take an arbitrary other point on the edge that has little chances
    # to be aligned with the first one
    v2 = edge.Curve.tangent(edge.ParameterRange[0] + 0.01)[0]
    n = edge.Curve.Axis
    # if that axis points "the wrong way" from the reference, we invert it
    if not ref:
        ref = App.Vector(0, 0, 1)
    if n.getAngle(ref) > math.pi / 2:
        n = n.negative()

    if DraftVecUtils.angle(v1, v2, n) < 0:
        return False

    if n.z < 0:
        return False

    return True


def isWideAngle(edge):
    """Return True if the given edge is an arc with angle > 180 degrees."""
    if geomType(edge) != "Circle":
        return False

    r = edge.Curve.Radius
    total = 2 * r * math.pi

    if edge.Length > total / 2:
        return True

    return False


def arcFrom2Pts(firstPt, lastPt, center, axis=None):
    """Build an arc with center and 2 points, can be oriented with axis."""
    radius1 = firstPt.sub(center).Length
    radius2 = lastPt.sub(center).Length

    # (PREC = 4 = same as Part Module),  Is it possible?
    if round(radius1 - radius2, 4) != 0:
        return None

    thirdPt = App.Vector(firstPt.sub(center).add(lastPt).sub(center))
    thirdPt.normalize()
    thirdPt.scale(radius1, radius1, radius1)
    thirdPt = thirdPt.add(center)
    newArc = Part.Edge(Part.Arc(firstPt, thirdPt, lastPt))

    if axis and newArc.Curve.Axis.dot(axis) < 0:
        thirdPt = thirdPt.sub(center)
        thirdPt.scale(-1, -1, -1)
        thirdPt = thirdPt.add(center)
        newArc = Part.Edge(Part.Arc(firstPt, thirdPt, lastPt))

    return newArc


def arcFromSpline(edge):
    """Turn given edge into a circular arc from three points.

    Takes its first point, midpoint and endpoint. It works best with bspline
    segments such as those from imported svg files. Use this only
    if you are sure your edge is really an arc.

    It returns None if there is a problem, including passing straight edges.
    """
    if geomType(edge) == "Line":
        print("This edge is straight, cannot build an arc on it")
        return None

    if len(edge.Vertexes) > 1:
        # 2-point arc
        p1 = edge.Vertexes[0].Point
        p2 = edge.Vertexes[-1].Point
        ml = edge.Length / 2
        p3 = edge.valueAt(ml)
        try:
            return Part.Arc(p1, p3, p2).toShape()
        except Part.OCCError:
            print("Couldn't make an arc out of this edge")
            return None
    else:
        # circle
        p1 = edge.Vertexes[0].Point
        p2 = findMidpoint(edge)
        ray = p2.sub(p1)
        ray.scale(0.5, 0.5, 0.5)
        center = p1.add(ray)
        radius = ray.Length
        try:
            return Part.makeCircle(radius, center)
        except Part.OCCError:
            print("couldn't make a circle out of this edge")
            return None


## @}
