import math

from qgis.core import QgsPoint
from qgis.core import QgsLineString
from xplordb.datamodel.collar import Collar
from xplordb.datamodel.survey import Survey


def minimum_curvature_method(surveys: [Survey], collar: Collar) -> QgsLineString:
    """
    Define QgsLineString vertex with Minimum curvature method implementation from https://www.drillingformulas.com/minimum-curvature-method/

        Args:
            surveys: list of surveys
            collar: collar

    """
    result = QgsLineString()

    surveys = add_collar_start_if_needed(surveys)

    surveys.sort(key=lambda x: x.depth)

    current_x = collar.x
    current_y = collar.y
    current_z = collar.z

    point = QgsPoint(x=current_x, y=current_y, z=current_z)
    result.addVertex(point)

    for i in range(0, len(surveys) - 1):
        s2 = surveys[i + 1]
        s1 = surveys[i]
        # In xplordb dip is changed (for convention ?)
        i2 = math.radians(s2.dip + 90.0)
        i1 = math.radians(s1.dip + 90.0)
        a2 = math.radians(s2.azimuth)
        a1 = math.radians(s1.azimuth)
        CL = s2.depth - s1.depth
        DL = math.acos(math.cos(i2 - i1) - (math.sin(i1) * math.sin(i2)) * (1 - math.cos(a2 - a1)))

        # Formula with different https//directionaldrillingart.blogspot.com/2015/09/directional-surveying-calculations.html
        # Produce same results
        # DL = math.acos((math.sin(i1) * math.sin(i2) * math.cos(a2 - a1)) + (math.cos(i1) * math.cos(i2)))
        RF = 1.0
        if DL != 0.0:
            # Formula with different method https//directionaldrillingart.blogspot.com/2015/09/directional-surveying-calculations.html
            # RF = math.tan(DL / 2) * (180 / math.pi) * (2 / DL)
            RF = math.tan(DL / 2) * (2 / DL)

        current_x = ((math.sin(i1) * math.sin(a1)) + (math.sin(i2) * math.sin(a2))) * (RF * (CL / 2)) + current_x
        current_y = ((math.sin(i1) * math.cos(a1)) + (math.sin(i2) * math.cos(a2))) * (RF * (CL / 2)) + current_y
        current_z = - (math.cos(i1) + math.cos(i2)) * (CL / 2) * RF + current_z
        point = QgsPoint(x=current_x, y=current_y, z=current_z)
        result.addVertex(point)
    return result


def add_collar_start_if_needed(surveys) -> [Survey]:
    """
    Insert a survey at 0.0 depth if not available

    Args:
        surveys: list of available survey

    Returns: list of survey with value at 0.0 depth if not defined

    """
    res = surveys
    if len(res):
        res.sort(key=lambda x: x.depth)
        if res[0].depth != 0.0:
            res.append(Survey(hole_id=res[0].hole_id,
                              data_set=res[0].data_set,
                              depth=0,
                              azimuth=res[0].azimuth,
                              dip=res[0].dip,
                              loaded_by=res[0].loaded_by))

    return res
