# pxd for py_ballisticcalc_exts.base_engine

from libcpp.vector cimport vector
from py_ballisticcalc_exts.base_types cimport (
    BCLIBC_Config,
    BCLIBC_ShotProps,
    BCLIBC_WindSock,
    BCLIBC_TrajFlag,
)
from py_ballisticcalc_exts.v3d cimport BCLIBC_V3dT
from py_ballisticcalc_exts.traj_data cimport (
    BCLIBC_BaseTrajSeq,
    BCLIBC_BaseTrajData,
    BCLIBC_TrajectoryData,
    BCLIBC_BaseTrajDataHandlerInterface
)


cdef void zero_finding_error(object exception, const BCLIBC_ZeroFindingError &zero_error)
cdef list TrajectoryData_list_from_cpp(const vector[BCLIBC_TrajectoryData] &records)
cdef TrajectoryData_from_cpp(const BCLIBC_TrajectoryData& cpp_data)


cdef extern from "include/bclibc/engine.hpp" namespace "bclibc" nogil:
    DEF MAX_ERR_MSG_LEN = 256

    cdef enum class BCLIBC_ZeroInitialStatus:
        CONTINUE
        DONE

    cdef enum class BCLIBC_TerminationReason:
        # Solver specific, not real errors, just termination reasons!
        NO_TERMINATE
        MINIMUM_VELOCITY_REACHED
        MAXIMUM_DROP_REACHED
        MINIMUM_ALTITUDE_REACHED

    cdef cppclass BCLIBC_ZeroInitialData:
        BCLIBC_ZeroInitialStatus status
        double look_angle_rad
        double slant_range_ft
        double target_x_ft
        double target_y_ft
        double start_height_ft

    cdef cppclass BCLIBC_MaxRangeResult:
        double max_range_ft
        double angle_at_max_rad

    cdef enum class BCLIBC_ZeroFindingErrorType:
        NO_ERROR
        ZERO_FINDING_ERROR
        OUT_OF_RANGE_ERROR

    cdef cppclass BCLIBC_OutOfRangeErrorData:
        double requested_distance_ft
        double max_range_ft
        double look_angle_rad

    cdef cppclass BCLIBC_ZeroFindingErrorData:
        double zero_finding_error
        int iterations_count
        double last_barrel_elevation_rad

    cdef cppclass BCLIBC_ZeroFindingError:
        BCLIBC_ZeroFindingErrorType type
        BCLIBC_ZeroFindingErrorData zero_finding
        BCLIBC_OutOfRangeErrorData out_of_range

    # Forward declaration
    cdef cppclass BCLIBC_Engine

    # Declare the function signature type (not a pointer yet)
    ctypedef void BCLIBC_IntegrateFunc(
        BCLIBC_Engine &eng,
        double range_limit_ft,
        double range_step_ft,
        double time_step,
        BCLIBC_BaseTrajDataHandlerInterface &trajectory,
        BCLIBC_TerminationReason &reason,
    ) except +

    # Declare pointer to function
    ctypedef BCLIBC_IntegrateFunc *BCLIBC_IntegrateFuncPtr

    cdef cppclass BCLIBC_Engine:
        int integration_step_count
        BCLIBC_V3dT gravity_vector
        BCLIBC_Config config
        BCLIBC_ShotProps shot
        BCLIBC_IntegrateFuncPtr integrate_func_ptr

        void integrate(
            double range_limit_ft,
            double range_step_ft,
            double time_step,
            BCLIBC_BaseTrajDataHandlerInterface &handler,
            BCLIBC_TerminationReason &reason) except +

        void integrate_filtered(
            double range_limit_ft,
            double range_step_ft,
            double time_step,
            BCLIBC_TrajFlag filter_flags,
            vector[BCLIBC_TrajectoryData] &records,
            BCLIBC_TerminationReason &reason,
            BCLIBC_BaseTrajSeq *dense_trajectory) except +

        void find_apex(BCLIBC_BaseTrajData &apex_out) except +

        double error_at_distance(
            double angle_rad,
            double target_x_ft,
            double target_y_ft) except +

        BCLIBC_MaxRangeResult find_max_range(
            double low_angle_deg,
            double high_angle_deg,
            double APEX_IS_MAX_RANGE_RADIANS) except +

        void init_zero_calculation(
            double distance,
            double APEX_IS_MAX_RANGE_RADIANS,
            double ALLOWED_ZERO_ERROR_FEET,
            BCLIBC_ZeroInitialData &result,
            BCLIBC_ZeroFindingError &error) except +

        double zero_angle_with_fallback(
            double distance,
            double APEX_IS_MAX_RANGE_RADIANS,
            double ALLOWED_ZERO_ERROR_FEET,
            BCLIBC_ZeroFindingError &error) except +

        double zero_angle(
            double distance,
            double APEX_IS_MAX_RANGE_RADIANS,
            double ALLOWED_ZERO_ERROR_FEET,
            BCLIBC_ZeroFindingError &error) except +

        double find_zero_angle(
            double distance,
            int lofted,
            double APEX_IS_MAX_RANGE_RADIANS,
            double ALLOWED_ZERO_ERROR_FEET,
            BCLIBC_ZeroFindingError &error) except +

cdef class CythonizedBaseIntegrationEngine:

    cdef:
        public object _config
        list[object] _table_data  # list[object]
        BCLIBC_Engine _this

    cdef double get_calc_step(CythonizedBaseIntegrationEngine self)

    cdef BCLIBC_ShotProps* _init_trajectory(
        CythonizedBaseIntegrationEngine self,
        object shot_info
    )
    cdef void _init_zero_calculation(
        CythonizedBaseIntegrationEngine self,
        double distance,
        BCLIBC_ZeroInitialData &out,
    )
    cdef double _find_zero_angle(
        CythonizedBaseIntegrationEngine self,
        object shot_info,
        double distance,
        bint lofted
    )
    cdef double _zero_angle(
        CythonizedBaseIntegrationEngine self,
        object shot_info,
        double distance
    )
    cdef BCLIBC_MaxRangeResult _find_max_range(
        CythonizedBaseIntegrationEngine self,
        object shot_info,
        double low_angle_deg,
        double high_angle_deg,
    )
    cdef BCLIBC_TrajectoryData _find_apex(
        CythonizedBaseIntegrationEngine self,
        object shot_info
    )
    cdef double _error_at_distance(
        CythonizedBaseIntegrationEngine self,
        double angle_rad,
        double target_x_ft,
        double target_y_ft
    )

    cdef void _integrate(
        CythonizedBaseIntegrationEngine self,
        object shot_info,
        double range_limit_ft,
        double range_step_ft,
        double time_step,
        BCLIBC_BaseTrajDataHandlerInterface &handler,
        BCLIBC_TerminationReason &reason,
    )
