%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 1998-2013. All Rights Reserved.
%% 
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%% 
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%% 
%% %CopyrightEnd%
%%
-module(nteventlog).
-behaviour(gen_server).

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

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

-record(state, {port, mfa}).

%%----------------------------------------------------------------------
%% API
%%----------------------------------------------------------------------

start_link(Ident, MFA) ->
    gen_server:start_link({local, nteventlog}, nteventlog,
			  [Ident, MFA], []).

start(Ident, MFA) ->
    gen_server:start({local, nteventlog}, nteventlog, [Ident, MFA], []).

stop() ->
    gen_server:call(nteventlog, stop).

%%----------------------------------------------------------------------
%% gen_server callbacks
%%----------------------------------------------------------------------

init([Identifier,MFA0]) ->
    process_flag(trap_exit, true),
    process_flag(priority, low),

    Port = case os:type() of
	       {win32, _OSname} -> start_portprogram(Identifier);
	       OS -> exit({unsupported_os, OS})
	   end,

    %% If we're using os_sup:error_report/2,
    %% the setting of os_sup_errortag should be used as argument
    MFA = case MFA0 of
	      {os_sup, error_report, [_Tag]} ->
		  Tag = os_mon:get_env(os_sup, os_sup_errortag),
		  {os_sup, error_report, [Tag]};
	      _ ->
		  MFA0
	  end,

    {ok, #state{port=Port, mfa=MFA}}.

handle_call(stop, _From, State) ->
    {stop, normal, stopped, State}.

handle_cast(_Msg, State) ->
    {noreply, State}.

handle_info({_Port, {data, Data}}, #state{mfa={M,F,A}} = State) ->
    T = parse_log(Data),
    apply(M, F, [T | A]),
    State#state.port ! {self(), {command, "A"}},
    {noreply, State};
handle_info({'EXIT', _Port, Reason}, State) ->
    {stop, {port_died, Reason}, State#state{port=not_used}};
handle_info(_Info, State) ->
    {noreply, State}.

terminate(_Reason, State) ->
    case State#state.port of
	not_used -> ignore;
	Port ->
	    port_close(Port)
    end,
    ok.

%% os_mon-2.0
%% For live downgrade to/upgrade from os_mon-1.8[.1]
code_change(Vsn, PrevState, "1.8") ->
    case Vsn of

	%% Downgrade from this version
	{down, _Vsn} ->
	    process_flag(trap_exit, false),

	    %% Downgrade to old State tuple
	    State = {PrevState#state.port, PrevState#state.mfa},
	    {ok, State};

	%% Upgrade to this version
	_Vsn ->
	    process_flag(trap_exit, true),

	    %% Upgrade to this state record
	    {Port, MFA} = PrevState,
	    State = #state{port=Port, mfa=MFA},
	    {ok, State}
    end;
code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%%----------------------------------------------------------------------
%% Internal functions
%%----------------------------------------------------------------------

start_portprogram(Identifier) ->
    Command =
	"\"" ++ filename:join([code:priv_dir(os_mon),"bin","nteventlog.exe"]) ++
	"\" " ++ make_list(Identifier),
    open_port({spawn,Command},[{packet,2}]).

make_list(X) when is_atom(X) ->
    atom_to_list(X);
make_list(X) ->
    X.

holl_len([$H | Rest], Sum) ->
    {Sum, Rest};
holl_len([ N | Rest], Sum) ->
    NN = N - $0,
    holl_len(Rest, Sum * 10 + NN).
holl_len(L) ->
    holl_len(L,0).

splitlist(L,N) ->
    {lists:sublist(L,N),lists:nthtail(N,L)}. 

hollerith(Str) ->
    {Len, Rest} = holl_len(Str),
    splitlist(Rest,Len).

holl_time(Str) ->
    {Holl,Rest} = hollerith(Str),
    Rev = lists:reverse(Holl),
    B = list_to_integer(lists:reverse(lists:sublist(Rev,6))),
    A = list_to_integer(lists:reverse(lists:nthtail(6,Rev))),
    {{A,B,0},Rest}.

parse_log(Str) ->
    {Time, Rest1} = holl_time(Str),
    {Category,Rest2} = hollerith(Rest1),
    {Facility,Rest3} = hollerith(Rest2),
    {Severity,Rest4} = hollerith(Rest3),
    {Message,_} = hollerith(Rest4),
    {Time,Category,Facility,Severity,Message}.