%% ---------------------------------------------------------------------------
%% @doc umake
%% @author ming_up@foxmail.com
%% @since  2016-09-09
%% @deprecated 数学运算 TODO nif化
%% ---------------------------------------------------------------------------
-module(umath).

-export([
        ceil/1                      %% 向上取整
        , distance/2                %% 平面上两点距离
        , floor/1                   %% 向下取整
        , try_round_on_equal/1      %% 相等情况下,取整
        , degree_to_radian/1        %% 角度转换为弧度
        , radian_to_degree/1        %% 弧度转换为角度
        , radian_to_360degree/1     %% 弧度转换为360度
        , get_distance/4            %% 通过经纬度坐标获取地表距离
        , rand_point_in_circle/4    %% 在圆上随机选取一点
        , calc_position_around/3    %% 在周围半径上取一个点
    ]).

-export([
        roundup/1                   % 远零取整
        ,distance_power/4           % 平面两坐标距离之幂
    ]).

%% ---------------------------------------------------------------------------
%% @doc 计算两个经纬度的距离
-spec get_distance(RawLng1, RawLat1, RawLng2, RawLat2) -> Result when
    RawLng1     :: integer(),       %% 经度1 * 1000,未处理
    RawLat1     :: integer(),       %% 纬度1 * 1000,未处理
    RawLng2     :: integer(),       %% 经度2 * 1000,未处理
    RawLat2     :: integer(),       %% 纬度2 * 1000,未处理
    Result      :: integer().       %% 距离(米)
%% @end
%% ---------------------------------------------------------------------------
get_distance(RawLng1, RawLat1, RawLng2, RawLat2) ->
    % io:format("~n=uti:~p==",[[RawLat1, RawLng1, RawLat2, RawLng2]]),
    Lat1 = RawLat1 / 1000,
    Lng1 = RawLng1 / 1000,
    Lat2 = RawLat2 / 1000,
    Lng2 = RawLng2 / 1000,
    Rad = fun(D) -> D * math:pi() / 180.0 end,
    EARTH_RADIUS = 6378.137,
    RadLat1 = Rad(Lat1),
    RadLat2 = Rad(Lat2),
    A = RadLat1 - RadLat2,
    B = Rad(Lng1) - Rad(Lng2),
    S = 2 * math:asin(math:sqrt(math:pow(math:sin(A/2), 2) +
            math:cos(RadLat1) * math:cos(RadLat2) * math:pow(math:sin(B/2), 2))),
    NewS = S * EARTH_RADIUS,
    NewS1 = round(NewS * 10000) / 10000,
    round(NewS1 * 1000).

%% ---------------------------------------------------------------------------
%% @doc 获得圆内随机一点的坐标
-spec rand_point_in_circle(CenterX, CenterY, Radius, Type) -> {X, Y} when
    CenterX     :: integer(),       %% 圆心坐标X
    CenterY     :: integer(),       %% 圆心坐标Y
    Radius      :: integer(),       %% 圆半径
    Type        :: integer(),       %% 类型:1圆内随机一点|2圆边随机一点
    X           :: integer(),       %% 坐标X
    Y           :: integer().       %% 坐标Y
%% ---------------------------------------------------------------------------
rand_point_in_circle(CenterX, CenterY, Radius, Type) ->
    %% 随机半径
    if
        Type==1 ->
            RandRadius = urand:rand(0, Radius);
        true ->
            RandRadius = Radius
    end,
    %% 随机角度
    RandAngle = urand:rand(0, 360),
    Pi = math:pi(),
    AngleAsNum = (2 * Pi / 360) * RandAngle,
    X = floor( CenterX + RandRadius * math:cos(AngleAsNum) ),
    Y = floor( CenterY + RandRadius * math:sin(AngleAsNum) ),
    % Distance = (CenterX - X) * (CenterX - X) + (CenterY - Y) * (CenterY - Y),
    % D = math:sqrt(Distance),
    %{X, Y, D},
    {X, Y}.

%% 平面坐标两点距离
distance({X1, Y1}, {X2, Y2}) ->
    ceil(
        math:sqrt(
            distance_power(X1, Y1, X2, Y2)
        )
    ).

-compile([{inline, distance_power/4}]).
%% 平面两坐标距离的平方值（很多时候只需要平方值做比较，省去开方运算）
distance_power(X1, Y1, X2, Y2) ->
    math:pow(X2 - X1, 2) + math:pow(Y2 - Y1, 2).

%% 向上取整及向下取整
-ifdef(OTP_RELEASE).    %% OTP 21 or higher
-compile({inline, [ceil/1, floor/1]}).
-compile({no_auto_import, [ceil/1, floor/1]}).
ceil(N)  -> erlang:ceil(N).
floor(N) -> erlang:floor(N).
-else.  %% OTP 20 or lower.
ceil(N) ->
    T = trunc(N),
    case N > T of
        true  -> T + 1;
        false -> T
    end.
floor(X) ->
    T = trunc(X),
    case T > X of
        true  -> T - 1;
        false -> T
    end.
-endif.

%% 远零取整 （与trunc向零取整相反）
%% 向远离0的方向取整，正数时等于ceil，负数时等于floor
roundup(X) ->
    if
        X > 0 ->
            ceil(X);
        X < 0 ->
            floor(X);
        true  ->
            0
    end.

%% 相等情况下,取整
try_round_on_equal(X) ->
    RoundX = round(X),
    case RoundX == X of
        true -> RoundX;
        false -> X
    end.

%% 角度转换为弧度
degree_to_radian(Degree) ->
    math:pi()*(Degree/180).

%% 弧度转换为角度
radian_to_degree(Radian) ->
    Radian*180/math:pi().

%% 弧度转换为0-360度
radian_to_360degree(Radian) ->
    D = Radian*180/math:pi(),
    case D < 0 of
        true  -> floor(D+360);
        false -> floor(D)
    end.

%% 以自己为圆心，圆周上找一个位置
%% 如果找一圈还是没有，就在自己位置创建一个
%% y轴增长方向
%%        ↑
%%        |
%% 第二象限 | 第一象限
%%        |
%%        |(A, B)圆点
%% ---------------> x轴增长方向
%%        |
%%第三象限  |第四象限
%%        |
%%
%% 先在上下左右四个方向找，找不到再看其他地方
calc_position_around(Scene, A, B) ->
    R = 480,
    case calc_position_four_direction(Scene, A, B, R) of
        {X, Y} ->
            {X, Y};
        false ->
            %% 右边扇区:先判断第一项象限与X轴45°之内的，再判断第四象限与X轴45°之内的
            %% 以此规律，总的顺序为：注(0°=360°)
            %% [0, -45]->[5, 45]->[45, 130]->[230, 310]
            case calc_position_around(Scene, A, B, R, 0, 45, 5) of
                {X, Y} ->
                    {X, Y};
                false ->
                    case calc_position_around(Scene, A, B, R, -5, -45, -5) of
                        {X, Y} ->
                            {X, Y};
                        false ->
                            case calc_position_around(Scene, A, B, R, 180, 125, -5) of
                                {X, Y} ->
                                    {X, Y};
                                false ->
                                    case calc_position_around(Scene, A, B, R, 180, 225, 5) of
                                        {X, Y} ->
                                            {X, Y};
                                        false ->
                                            case calc_position_around(Scene, A, B, R, 50, 120, 5) of
                                                {X, Y} ->
                                                    {X, Y};
                                                false ->
                                                    case calc_position_around(Scene, A, B, R, 230, 310, 5) of
                                                        {X, Y} ->
                                                            {X, Y};
                                                        false ->
                                                            {A, B}
                                                    end
                                            end
                                    end
                            end
                    end
            end
    end.

%% 一次变化5°，直至结束
calc_position_around(_Scene, _A, _B, _R, Angle, Angle, _D) -> false;
calc_position_around(Scene, A, B, R, AngleBegin, AngleEnd, Direction) ->
    X = erlang:round(A + R * math:cos( math:pi() * AngleBegin / 180 )),
    Y = erlang:round(B + R * math:sin( math:pi() * AngleBegin / 180 )),
    case scene_landform:is_battle_pos(Scene, X, Y) of
        false  ->
            calc_position_around(Scene, A, B, R, AngleBegin + Direction, AngleEnd, Direction);
        true ->
            {X, Y}
    end.

calc_position_four_direction(Scene, A, B, R) ->
    case scene_landform:is_battle_pos(Scene, A + R, B) of
        true   ->
            {A + R, B};
        false  ->
            case scene_landform:is_battle_pos(Scene, A - R, B) of
                true ->
                    {A - R, B};
                false  ->
                    case scene_landform:is_battle_pos(Scene, A, B + R) of
                        true ->
                            {A, B + R};
                        false  ->
                            case scene_landform:is_battle_pos(Scene, A, B - R) of
                                true ->
                                    {A, B - R};
                                false  ->
                                    false
                            end
                    end
            end
    end.