%%--------------------------------------------------------------------
%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
%% 
%% Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%% 
%% %CopyrightEnd%
%%
%%
%%----------------------------------------------------------------------
%% File    : CosTime_TimeService_impl.erl
%% Purpose : 
%%----------------------------------------------------------------------

-module('CosTime_TimeService_impl').

%%--------------- INCLUDES -----------------------------------
-include("cosTimeApp.hrl").

%%--------------- EXPORTS ------------------------------------
%%--------------- External -----------------------------------
%% Interface functions
-export([universal_time/2, secure_universal_time/2, new_universal_time/5]).
-export([uto_from_utc/3, new_interval/4]).

%%--------------- gen_server specific exports ----------------
-export([handle_info/2, code_change/3]).
-export([init/1, terminate/2]).


%% Data structures
-record(state, 
	{tdf,
	 inaccuracy}).
%% Data structures constructors
-define(get_InitState(T,I), 
	#state{tdf        = T,
	       inaccuracy = I}).

%% Data structures selectors
-define(get_Inaccuracy(S),  S#state.inaccuracy). 
-define(get_Tdf(S),         S#state.tdf). 

%% Data structures modifiers

%% MISC

%%-----------------------------------------------------------%
%% function : handle_info, code_change
%% Arguments: 
%% Returns  : 
%% Effect   : Functions demanded by the gen_server module. 
%%------------------------------------------------------------

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

handle_info(_Info, State) ->
    ?debug_print("INFO: ~p~n", [_Info]),
    {noreply, State}.    

%%----------------------------------------------------------%
%% function : init, terminate
%% Arguments: 
%%-----------------------------------------------------------

init([Tdf, Inaccuracy]) ->
    process_flag(trap_exit, true),
    {ok, ?get_InitState(Tdf, Inaccuracy)}.

terminate(_Reason, _State) ->
    ok.

%%-----------------------------------------------------------
%%------- Exported external functions -----------------------
%%-----------------------------------------------------------
%%----------------------------------------------------------%
%% function : universal_time
%% Arguments: -
%% Returns  : CosTime::UTO |
%%            {'EXCEPTION", #'CosTime_TimeUnavailable'{}}
%% NOTE     : cosTime:create_universal_time will raise the correct 
%%            exception.
%%-----------------------------------------------------------
universal_time(OE_THIS, State) ->
    {ok, Time} = create_universal_time(),
    Inaccuracy = ?get_Inaccuracy(State),
    Utc = #'TimeBase_UtcT'{time=Time, inacclo = ?low_TimeT(Inaccuracy), 
			   inacchi = ?high_TimeT(Inaccuracy), 
			   tdf = ?get_Tdf(State)},
    {reply, 'CosTime_UTO':oe_create([Utc, OE_THIS], [{pseudo,true}|?CREATE_OPTS]), State}.

%%----------------------------------------------------------%
%% function : secure_universal_time
%% Arguments: 
%% Returns  : {'EXCEPTION", #'CosTime_TimeUnavailable'{}}
%%-----------------------------------------------------------
-spec secure_universal_time(_, _) -> no_return().
secure_universal_time(_OE_THIS, _State) ->
    corba:raise(#'CosTime_TimeUnavailable'{}).

%%----------------------------------------------------------%
%% function : new_universal_time
%% Arguments: Time - TimeBase::TimeT
%%            Inaccuracy - TimeBase::InaccuracyT inaccuracy
%%            Tdf - TimeBase::TdfT
%% Returns  : CosTime::UTO
%%-----------------------------------------------------------
new_universal_time(OE_THIS, State, Time, Inaccuracy, Tdf) when 
  is_integer(Time) andalso is_integer(Inaccuracy) andalso is_integer(Tdf) andalso
  Tdf=<12 andalso Inaccuracy=<?max_Inaccuracy andalso Time=<?max_TimeT ->
    Utc = #'TimeBase_UtcT'{time=Time, inacclo = ?low_TimeT(Inaccuracy), 
			   inacchi = ?high_TimeT(Inaccuracy), tdf = Tdf},
    {reply, 'CosTime_UTO':oe_create([Utc, OE_THIS], [{pseudo,true}|?CREATE_OPTS]), State};
new_universal_time(_, _, _, _, _) ->
    corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}).

%%----------------------------------------------------------%
%% function : uto_from_utc
%% Arguments: Utc - TimeBase::UtcT
%% Returns  : CosTime::UTO
%%-----------------------------------------------------------
uto_from_utc(OE_THIS, State, Utc) when is_record(Utc, 'TimeBase_UtcT') ->
    {reply, 'CosTime_UTO':oe_create([Utc, OE_THIS],[{pseudo,true}|?CREATE_OPTS]),State};
uto_from_utc(_, _, _) ->
    corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}).

%%----------------------------------------------------------%
%% function : new_interval
%% Arguments: Lower - TimeBase::TimeT
%%            Upper - TimeBase::TimeT
%% Returns  : CosTime::TIO
%%-----------------------------------------------------------
new_interval(OE_THIS, State, Lower, Upper) when is_integer(Lower) andalso is_integer(Upper) andalso
						Lower=<Upper ->
    {reply, 'CosTime_TIO':oe_create([#'TimeBase_IntervalT'{lower_bound=Lower, 
							   upper_bound=Upper},
				     ?get_Tdf(State),
				     OE_THIS], 
				    [{pseudo,true}|?CREATE_OPTS]), State};
new_interval(_, _, _, _) ->
    corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}).


%%--------------- LOCAL FUNCTIONS ----------------------------
%%-----------------------------------------------------------%
%% function : create_universal_utc
%% Arguments: -
%% Returns  : TimeT or raises exception.
%% Effect   : Creates a universal time; if time unavailable we
%%            must raise CosTime_TimeUnavailable.
%% NOTE     : 'datetime_to_gregorian_seconds' use year 0 as time
%%            base. We want to use 15 october 1582, 00:00 as base.
%%------------------------------------------------------------

create_universal_time() ->
    %% Time is supposed to be #100 nano-secs passed.
    %% We add micro secs for a greater precision.
    {MS,S,US} = erlang:timestamp(),
    case catch calendar:datetime_to_gregorian_seconds(
		 calendar:now_to_universal_time({MS,S,US})) of
	Secs when is_integer(Secs) ->
	    {ok, (Secs-?ABSOLUTE_TIME_DIFF)*10000000 + US*10};
	_ ->
	    corba:raise(#'CosTime_TimeUnavailable'{})
    end.

%%--------------- MISC FUNCTIONS, E.G. DEBUGGING -------------
%%--------------- END OF MODULE ------------------------------

