%% -*- coding: utf-8 -*-


%%% @author M. Hiyama
%%% @doc findfのサーバー
%%%
-module(findf_server).

-behaviour(gen_server).

%% @headerfile "../include/findf.hrl"
-include("../include/findf.hrl").

%% @headerfile "../include/findf_types.hrl"
-include("../include/findf_types.hrl").

-include("./findf_names.hrl").

%% API
-export([start_link/0, start_link/1, start/0, start/1]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
	 code_change/3]).

%%====================================================================
%% API
%%====================================================================


%% @spec start_link() -> ({ok,Pid} | ignore | {error,Error})
%%       where
%%         Pid = pid()
%%         Error = term()
%%
%% @doc Starts the server
start_link() -> start_link(no_arg).

start_link(InitState) -> gen_server:start_link({local, ?SERVER}, ?MODULE, InitState, []).

start() -> start(no_arg).

start(InitState) -> gen_server:start({local, ?SERVER}, ?MODULE, InitState, []).

%%====================================================================
%% gen_server callbacks
%%====================================================================


%% @private
%% @spec init(Args) -> {ok, State} |
%%                     {ok, State, Timeout} |
%%                     ignore |
%%                     {stop, Reason}
%%        where
%%          State = #state{}
%%          Timeout = integer() | infinity
%%          Reason = term()
%%
%% @doc Initiates the server
init(no_arg) -> init(#state{});
init(InitState) ->
    if not is_record(InitState, state) -> {stop, badarg};
       true ->
	   State = #state{dirs =
			      InitState#state.dirs ++
				findf_util:get_list_from_arg(?APP_NAME, ?DIRS_KEY) ++
				  findf_util:get_list_from_env(?DIRS_ENV),
			  exts =
			      InitState#state.exts ++
				findf_util:get_list_from_arg(?APP_NAME, ?EXTS_KEY) ++
				  findf_util:get_list_from_env(?EXTS_ENV)},
	   {ok, State}
    end.

%% @private
%% @spec handle_call(Request, From, State) -> {reply, Reply, State} |
%%                                            {reply, Reply, State, Timeout} |
%%                                            {noreply, State} |
%%                                            {noreply, State, Timeout} |
%%                                            {stop, Reason, Reply, State} |
%%                                            {stop, Reason, State}
%%       where
%%         Request = term()
%%         From = {pid(), reference()}
%%         State = #state{}
%%         Timeout = integer() | infinity
%%         Reason = term()
%%
%% @doc Handling call messages


%% 状態のdirsに関する操作群
handle_call(get_dirs, _From, State) -> Reply = State#state.dirs, {reply, Reply, State};
handle_call({set_dirs, Dirs}, _From, State) ->
    NewState = #state{dirs = Dirs, exts = State#state.exts},
    Reply = ok,
    {reply, Reply, NewState};
handle_call({add_to_dirs, Dir}, _From, State) ->
    NewState = #state{dirs = findf_util:add_to_list(Dir, State#state.dirs),
		      exts = State#state.exts},
    Reply = ok,
    {reply, Reply, NewState};
handle_call({remove_from_dirs, Dir}, _From, State) ->
    NewState = #state{dirs = findf_util:remove_from_list(Dir, State#state.dirs),
		      exts = State#state.exts},
    Reply = ok,
    {reply, Reply, NewState};
%% 状態のextsに関する操作群
handle_call(get_exts, _From, State) -> Reply = State#state.exts, {reply, Reply, State};
handle_call({set_exts, Exts}, _From, State) ->
    NewState = #state{exts = Exts, dirs = State#state.dirs},
    Reply = ok,
    {reply, Reply, NewState};
handle_call({add_to_exts, Ext}, _From, State) ->
    NewState = #state{exts = findf_util:add_to_list(Ext, State#state.exts),
		      dirs = State#state.dirs},
    Reply = ok,
    {reply, Reply, NewState};
handle_call({remove_from_exts, Ext}, _From, State) ->
    NewState = #state{exts = findf_util:remove_from_list(Ext, State#state.exts),
		      dirs = State#state.dirs},
    Reply = ok,
    {reply, Reply, NewState};
%% 公開APIの処理
handle_call({find_file, Name}, _From, State) ->
    Reply = try
	      {ok, findf_lib:find_file(Name, State#state.dirs, State#state.exts)}
	    catch
	      Reason -> {error, Reason}
	    end,
    {reply, Reply, State};
handle_call({open_file, Name}, _From, State) ->
    Reply = try
	      {ok, findf_lib:open_file(Name, State#state.dirs, State#state.exts)}
	    catch
	      Reason -> {error, Reason}
	    end,
    {reply, Reply, State};
handle_call({read_file, Name}, _From, State) ->
    Reply = try
	      {ok, findf_lib:read_file(Name, State#state.dirs, State#state.exts)}
	    catch
	      Reason -> {error, Reason}
	    end,
    {reply, Reply, State}.

%% @private
%% @spec handle_cast(Msg, State) -> {noreply, State} |
%%                                  {noreply, State, Timeout} |
%%                                  {stop, Reason, State}
%%       where
%%         Msg = term()
%%         State = #state{}
%%         Timeout = integer() | infinity
%%         Reason = term()
%%
%% @doc Handling cast messages
handle_cast(stop, State) -> {stop, normal, State};
handle_cast(_Msg, State) ->
      % その他は無視
    {noreply, State}.

%% @private
%% @spec handle_info(Info, State) -> {noreply, State} |
%%                                   {noreply, State, Timeout} |
%%                                   {stop, Reason, State}
%%       where
%%         Info = term()
%%         State = #state{}
%%         Timeout = integer() | infinity
%%         Reason = term()
%% @doc Handling all non call/cast messages
handle_info(_Info, State) -> {noreply, State}.

%% @private
%% @spec terminate(Reason, State) -> void()
%%       where
%%         Reason = normal | shutdown | term()
%%         State = #state{}
%%
%% @doc This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
terminate(_Reason, _State) -> ok.

%% @private
%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
%%       where
%%         OldVsn = term() | {down, term()}
%%         State = #state{}
%%         Extra = term()
%%         NewState = #state{}
%%
%% @doc Convert process state when code is changed
code_change(_OldVsn, State, _Extra) -> {ok, State}.