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

-behaviour(application).

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

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

    iot_mnesia:safe_start(),

    %% 启动http服务。当前 supervisor 初始化依赖本机 simulator API，因此保留此顺序。
    start_http_server(),

    %% 启动endpoints的相关依赖
    start_endpoints(),

    %% 启动ssl服务
    start_ssl_server(),

    iot_sup:start_link().

stop(_State) ->
    stop_started_services(),
    ok.

%% internal functions

start_endpoints() ->
    EndpointInfos = iot_api_client:get_all_endpoints(),
    Endpoints = lists:filtermap(fun(EndpointInfo) ->
        case endpoint:endpoint_record(EndpointInfo) of
            error ->
                false;
            {ok, Endpoint} ->
                {true, Endpoint}
        end
                                end, EndpointInfos),
    ok = endpoint_adapter_sup:start_endpoints(Endpoints).

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]},
            {"/container/[...]", http_protocol, [container_handler]},
            {"/endpoint/[...]", http_protocol, [endpoint_handler]},
            {"/simulator/[...]", http_protocol, [simulator_api_handler]},
            {"/event_stream", event_stream_handler, []}
        ]}
    ]),

    TransOpts = #{
        max_connections => MaxConnections,
        num_acceptors => Acceptors,
        shutdown => brutal_kill,
        socket_opts => [
            {backlog, Backlog},
            {port, Port}
        ]
    },
    {ok, Pid} = cowboy:start_clear(http_listener, TransOpts, #{env => #{dispatch => Dispatcher}}),

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

%% 启动ssl服务
start_ssl_server() ->
    {ok, Props} = application:get_env(iot, ssl_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),
    PrivDir = code:priv_dir(iot),
    CertFile = filename:join([PrivDir, "ssl", "server.crt"]),
    KeyFile = filename:join([PrivDir, "ssl", "server.key"]),

    TransOpts = #{
        max_connections => MaxConnections,
        num_acceptors => Acceptors,
        shutdown => brutal_kill,
        socket_opts => [
            {nodelay, true},
            {backlog, Backlog},
            {port, Port},
            {certfile, CertFile},
            {keyfile, KeyFile}
        ]
    },
    {ok, _} = ranch:start_listener(ssl_server, ranch_ssl, TransOpts, ssl_channel, []),
    logger:debug("[iot_app] the ssl server start at: ~p", [Port]).

stop_started_services() ->
    _ = cowboy:stop_listener(http_listener),
    _ = ranch:stop_listener(ssl_server),
    _ = iot_mnesia:stop(),
    ok.
