%%--------------------------------------------------------------------
%% ``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 via the world wide web 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.
%% 
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
%% AB. All Rights Reserved.''
%% 
%%     $Id$
%%
%%----------------------------------------------------------------------
%% File    : etrap_logmgr.erl
%% Author  : Nicklas Eklund <nick@gwaihir>
%% Purpose : Make it easier to use disk_log.
%% Created : 15 Jun 1999 by Nicklas Eklund <nick@gwaihir>
%%----------------------------------------------------------------------

-module(etrap_logmgr).

%%--------------- INCLUDES -----------------------------------
%% Local
-include_lib("ETraP_Common.hrl").
%%--------------- IMPORTS-------------------------------------
%%--------------- EXPORTS-------------------------------------
-export([start/1, stop/1, log_safe/2, log_lazy/2, get_next/2]).


%%------------------------------------------------------------
%% function : start
%% Arguments: LogName   - name of the disk_log.
%% Returns  : 
%% Effect   : creating linked log
%%------------------------------------------------------------

start(LogName) ->  
    case catch disk_log:open([{name, LogName}, 
			      {file, LogName}, 
			      {type, halt},
			      {size, infinity}]) of
        {ok, LogName} ->
            ok;
        {error, Reason} ->
	    ?tr_error_msg("Initiating internal log failed: ~p", [Reason]),
	    exit({error, Reason});
	{repaired, LogName, {recovered, _Rec}, {badbytes, _Bad}} ->
            ok;
	Other ->
	    ?tr_error_msg("Initiating internal log failed: ~p", [Other]),
	    exit({error, Other})
    end.

%%------------------------------------------------------------
%% function : stop
%% Arguments: LogName - name of the disk_log.
%% Returns  : 
%% Effect   : 
%%------------------------------------------------------------

stop(LogName) ->
    case catch disk_log:close(LogName) of
        ok ->
            ok;
        {error, Reason} ->
	    ?tr_error_msg("Stopping internal log failed: ~p", [Reason]),
	    {error, Reason};
	Other ->
	    ?tr_error_msg("Stopping internal log failed: ~p", [Other]),
	    {error, Other}
    end.
    

%%------------------------------------------------------------
%% function : log_safe
%% Arguments: LogName -  name of the disk_log. If 'dummy' is
%%            used nothing should be logged. Reason, reuse code.
%%            LogRecord - record to store in the log.
%% Returns  : 
%% Effect   : Writes a logrecord and synchronizes to make sure
%%            that the record is stored.
%%------------------------------------------------------------

log_safe(dummy, _) ->
    ok;
log_safe(LogName, LogRecord) ->
    case write_safe(LogName, LogRecord) of
	ok ->
	    ok;
	_ ->
	    %% We have to catch the exit because in some cases
	    %% it's not possible to abort action in the 2PC-protocol.
	    case catch start(LogName) of
		ok ->
		    write_safe(LogName, LogRecord);
		{'EXIT', Reason} ->
		    {error, Reason}
	    end
    end.


write_safe(LogName, LogRecord) ->
    case catch disk_log:log(LogName, LogRecord) of
        ok -> % wrote to kernel successfully
            case catch disk_log:sync(LogName) of
		ok -> % Written to disk successfully
		    ok;
		{error, Reason} ->
		    ?tr_error_msg("Internal log write failed: ~p   ~p",
				  [Reason, LogName]),
		    {error, Reason};
		Other -> 
		    ?tr_error_msg("Internal log write failed: ~p   ~p",
				  [Other, LogName]),
		    {error, Other}
	    end;
	{error, Reason} ->
	    ?tr_error_msg("Internal log write failed: ~p   ~p", [Reason, LogName]),
	    {error, Reason};
	Other ->
	    ?tr_error_msg("Internal log write failed: ~p   ~p", [Other, LogName]),
	    {error, Other}
    end.


%%------------------------------------------------------------
%% function : log_lazy
%% Arguments: LogName -  name of the disk_log. If 'dummy' is
%%            used nothing should be logged. Reason, reuse code.
%%            LogRecord - record to store in the log. 
%% Returns  : 
%% Effect   : Writes a logrecord. The record may be lost.
%%------------------------------------------------------------

log_lazy(dummy, _LogRecord) ->
    ok;
log_lazy(LogName, LogRecord) ->
    case write_lazy(LogName, LogRecord) of
	ok ->
	    ok;
	_ ->
	    %% We have to catch the exit because in some cases
	    %% it's not possible to abort action in the 2PC-protocol.
	    case catch start(LogName) of
		ok ->
		    write_lazy(LogName, LogRecord);
		{'EXIT', Reason} ->
		    {error, Reason}
	    end
    end.

write_lazy(LogName, LogRecord) ->
    case catch disk_log:log(LogName, LogRecord) of
        ok ->  
	    %% wrote to kernel successfully
            ok;
        {error, Reason} -> 
	    %% Write to kernel failed with Reason
	    ?tr_error_msg("Internal log write failed: ~p", [Reason]),
	    {error, Reason};
	Other -> 
	    %% unknown message received.
	    ?tr_error_msg("Internal log write failed: ~p", [Other]),
	    {error, Other}
    end.


%%------------------------------------------------------------
%% function : get_next
%% Arguments: LogName -  name of the disk_log.
%%            Cursor - place to read from.
%% Returns  : {Cursor, LogRecs} - A cursor and up to N logrecords.
%%            eof - the atom 'eof', indicating logfile empty. 
%%            {error, Reason} - error.
%% Effect   : 
%% Purpose  : Used when performing a REDO scan
%%------------------------------------------------------------

get_next(LogName, Cursor) ->
    case catch disk_log:chunk(LogName, Cursor, 1) of
	{NewCursor, [Data]} ->
	    {Data, NewCursor};
	eof ->
	    eof;
	{error, Reason} ->
	    ?tr_error_msg("Internal log '~p' read failed: ~p",
		      [LogName, Reason]),
	    exit({error, Reason});
	_Other ->
	    ?tr_error_msg("Internal log '~p' read failed: 'log_corrupt'", [LogName]),
	    exit({error, "log_corrupt"})
    end.

%%--------------- END OF MODULE ------------------------------
