%%%-------------------------------------------------------------------
%% @doc iot public API
%% @end
%%%-------------------------------------------------------------------
-module(iot_app).

-behaviour(application).

-include("iot.hrl").

-export([start/2, stop/1]).
-export([start_http_server/0]).

start(_StartType, _StartArgs) ->
    io:setopts([{encoding, unicode}]),
    %% 加速内存的回收
    erlang:system_flag(fullsweep_after, 16),

    %% 启动数据库
    start_mnesia(),

    {ok, SupPid} = iot_sup:start_link(),
    %% 启动udp服务
    start_udp_server(),
    %% 启动http服务
    start_http_server(),

    {ok, SupPid}.

stop(_State) ->
    ok.

%% internal functions

%% 启动http服务
start_http_server() ->
    {ok, Props} = application:get_env(iot, http_server),
    Acceptors = proplists:get_value(acceptors, Props, 50),
    MaxConnections = proplists:get_value(max_connections, Props, 10240),
    Backlog = proplists:get_value(backlog, Props, 1024),
    Port = proplists:get_value(port, Props),

    Dispatcher = cowboy_router:compile([
        {'_', [
            {"/host/[...]", http_protocol, [host_handler]},
            {"/device/[...]", http_protocol, [device_handler]},
            {"/totalizator/[...]", http_protocol, [totalizator_handler]},
            {"/test/[...]", http_protocol, [test_handler]},
            {"/ws", ws_channel, []}
        ]}
    ]),

    TransOpts = [
        {port, Port},
        {num_acceptors, Acceptors},
        {backlog, Backlog},
        {max_connections, MaxConnections}
    ],

    {ok, Pid} = cowboy:start_clear(http_listener, TransOpts, #{env => #{dispatch => Dispatcher}}),

    lager:debug("[iot_app] the http server start at: ~p, pid is: ~p", [Port, Pid]).

start_udp_server() ->
    {ok, Props} = application:get_env(iot, udp_server),
    Port = proplists:get_value(port, Props),
    UdpOpts = [{udp_options, [binary, {reuseaddr, true}]}],
    {ok, _} = esockd:open_udp(iot_udp_server, Port, UdpOpts, {iot_udp_handler, start_link, []}),
    lager:debug("[iot_app] the udp server start at: ~p", [Port]).

%% 启动内存数据库
start_mnesia() ->
    %% 启动数据库
    ok = mnesia:start(),
    Tables = mnesia:system_info(tables),

    LoadTables = [totalizator],
    case lists:all(fun(Tab) -> lists:member(Tab, Tables) end, LoadTables) of
        true ->
            lager:debug("[iot_app] waiting for mnesia start: ~p", [LoadTables]),
            %% 加载必须等待的数据库表
            mnesia:wait_for_tables(LoadTables, infinity),
            lager:debug("[iot_app] waiting for mnesia end");
        false ->
            lager:warning("[iot_app] tables: ~p not exists, recreate mnesia schema", [LoadTables]),
            %% 清理掉以前的schema
            mnesia:stop(),
            mnesia:delete_schema([node()]),

            %% 创建schema
            ok = mnesia:create_schema([node()]),
            ok = mnesia:start(),

            %% 创建数据库表
            %% id生成器
            %% mnesia_id_generator:create_table(),
            %% 大数据统计表
            mnesia_totalizator:create_table()
    end.