%% ``The contents of this file are subject to the Erlang Public License,
%% Version 1.0, (the "License"); you may not use this file except in
%% compliance with the License. You may obtain a copy of the License at
%% http://www.erlang.org/EPL1_0.txt
%% 
%% 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.
%% 
%% The Original Code is Erlang-4.7.3, December, 1998.
%% 
%% The Initial Developer of the Original Code is Ericsson Telecom
%% AB. Portions created by Ericsson are Copyright (C), 1998, Ericsson
%% Telecom AB. All Rights Reserved.
%% 
%% Contributor(s): ______________________________________.''
%%
-module(error_logger_tty_h).
-copyright('Copyright (c) 1991-97 Ericsson Telecom AB').
-vsn('$Revision: /main/release/free/1').
-author('magnus@erix.ericsson.se').
-revision('$Revision: 1.6 $ ').
-modified('$Date: 1996/05/24 10:07:56 $ ').
-modified_by('$Author: magnus $ ').

-behaviour(gen_event).

%%%
%%% A handler that can be connected to the error_logger
%%% event handler.
%%% Writes all events formatted to stdout.
%%%   Handles events tagged error, emulator and info.
%%%
%%% It can only be started from error_logger:swap_handler(tty)
%%% or error_logger:tty(true)
%%%

-export([init/1,
	 handle_event/2, handle_call/2, handle_info/2,
	 terminate/2]).

%% This one is used when we takeover from the simple error_logger.
init({[], {error_logger, Buf}}) ->
    User = set_group_leader(),
    write_events(Buf),
    {ok, {User, error_logger}};
%% This one is used if someone took over from us, and now wants to
%% go back.
init({[], {error_logger_tty_h, PrevHandler}}) ->
    User = set_group_leader(),
    {ok, {User, PrevHandler}};
%% This one is used when we are started directly.
init([]) ->
    User = set_group_leader(),
    {ok, {User, []}}.
    
handle_event({Type, GL, Msg}, State) when node(GL) /= node() ->
    {ok, State};
handle_event(Event, State) ->
    write_event(tag_event(Event)),
    {ok, State}.

handle_info({'EXIT', User, _Reason}, {User, PrevHandler}) ->
    case PrevHandler of
	[] ->
	    remove_handler;
	_ -> 
	    {swap_handler, install_prev, {User, PrevHandler}, 
	     PrevHandler, go_back}
    end;
handle_info({emulator, GL, Chars}, State) when node(GL) == node() ->
    write_event(tag_event({emulator, GL, Chars})),
    {ok, State};
handle_info({emulator, noproc, Chars}, State) ->
    write_event(tag_event({emulator, noproc, Chars})),
    {ok, State};
handle_info(_, State) ->
    {ok, State}.

handle_call(_Query, State) -> {ok, {error, bad_query}, State}.

% unfortunately, we can't unlink from User - links are not counted!
%    if pid(User) -> unlink(User); true -> ok end,
terminate(install_prev, State) ->
    [];
terminate(_Reason, {User, PrevHandler}) ->
    {error_logger_tty_h, PrevHandler}.

%%% ------------------------------------------------------
%%% Misc. functions.
%%% ------------------------------------------------------

set_group_leader() ->
    case whereis(user) of
	User when pid(User) -> link(User), group_leader(User,self()), User;
	_                   -> false
    end.

tag_event(Event) ->    
    {erlang:localtime(), Event}.

write_events(Events) -> write_events1(lists:reverse(Events)).

write_events1([Event|Es]) ->
    write_event(Event),
    write_events1(Es);
write_events1([]) ->
    ok.

write_event({Time, {error, _GL, {Pid, Format, Args}}}) ->
    T = write_time(Time),
    case catch io_lib:format(add_node(Format,Pid), Args) of
	S when list(S) ->
	    format(T ++ S);
	_ ->
	    F = add_node("ERROR: ~p - ~p~n", Pid),
	    format(T ++ F, [Format,Args])
    end;
write_event({Time, {emulator, _GL, Chars}}) ->
    T = write_time(Time),
    case catch io_lib:format(Chars, []) of
	S when list(S) ->
	    format(T ++ S);
	_ ->
	    format(T ++ "ERROR: ~p ~n", [Chars])
    end;
write_event({Time, {info, _GL, {Pid, Info, _}}}) ->
    T = write_time(Time),
    format(T ++ add_node("~p~n",Pid),[Info]);
write_event({Time, {error_report, _GL, {Pid, std_error, Rep}}}) ->
    T = write_time(Time),
    S = format_report(Rep),
    format(T ++ S ++ add_node("", Pid));
write_event({Time, {info_report, _GL, {Pid, std_info, Rep}}}) ->
    T = write_time(Time, "INFO REPORT"),
    S = format_report(Rep),
    format(T ++ S ++ add_node("", Pid));
write_event({Time, {info_msg, _GL, {Pid, Format, Args}}}) ->
    T = write_time(Time, "INFO REPORT"),
    case catch io_lib:format(add_node(Format,Pid), Args) of
	S when list(S) ->
	    format(T ++ S);
	_ ->
	    F = add_node("ERROR: ~p - ~p~n", Pid),
	    format(T ++ F, [Format,Args])
    end;
write_event({Time, Error}) ->
    ok.

format(String)       -> io:format(user, String, []).
format(String, Args) -> io:format(user, String, Args).

format_report(Rep) when list(Rep) ->
    case string_p(Rep) of
	true ->
	    io_lib:format("~s~n",[Rep]);
	_ ->
	    format_rep(Rep)
    end;
format_report(Rep) ->
    io_lib:format("~p~n",[Rep]).

format_rep([{Tag,Data}|Rep]) ->
    io_lib:format("    ~p: ~p~n",[Tag,Data]) ++ format_rep(Rep);
format_rep([Other|Rep]) ->
    io_lib:format("    ~p~n",[Other]) ++ format_rep(Rep);
format_rep(_) ->
    [].

add_node(X, Pid) when atom(X) ->
    add_node(atom_to_list(X), Pid);
add_node(X, Pid) when node(Pid) /= node() ->
    lists:concat([X,"** at node ",node(Pid)," **~n"]);
add_node(X, _) ->
    X.

string_p([]) ->
    false;
string_p(Term) ->
    string_p1(Term).

string_p1([H|T]) when integer(H), H >= $ , H < 255 ->
    string_p1(T);
string_p1([$\n|T]) -> string_p1(T);
string_p1([$\r|T]) -> string_p1(T);
string_p1([$\t|T]) -> string_p1(T);
string_p1([$\v|T]) -> string_p1(T);
string_p1([$\b|T]) -> string_p1(T);
string_p1([$\f|T]) -> string_p1(T);
string_p1([$\e|T]) -> string_p1(T);
string_p1([H|T]) when list(H) ->
    case string_p1(H) of
	true -> string_p1(T);
	_    -> false
    end;
string_p1([]) -> true;
string_p1(_) ->  false.

write_time(Time) -> write_time(Time, "ERROR REPORT").

write_time({{Y,Mo,D},{H,Mi,S}},Type) ->
    io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s ===~n",
		  [Type,D,month(Mo),Y,t(H),t(Mi),t(S)]).

t(X) when integer(X) ->
    t1(integer_to_list(X));
t(_) ->
    "".
t1([X]) -> [$0,X];
t1(X)   -> X.

month(1) -> "Jan";
month(2) -> "Feb";
month(3) -> "Mar";
month(4) -> "Apr";
month(5) -> "May";
month(6) -> "Jun";
month(7) -> "Jul";
month(8) -> "Aug";
month(9) -> "Sep";
month(10) -> "Oct";
month(11) -> "Nov";
month(12) -> "Dec".





