%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 1999-2009. 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(ic_file).

-include_lib("ic/src/ic.hrl").

%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
-export([filename_push/4, filename_pop/2, open/2, close/1, remove_ext/1, join/2,
	 add_dot_erl/1, add_dot_hrl/1, add_dot_c/1, add_dot_h/1, add_dot_java/1, 
	 add_dot_idl/1, javaInterfaceFilePush/3, javaInterfaceFilePop/1, 
	 createDirectory/2, createJavaDirectory/2, open_java_file/3]).

%%-----------------------------------------------------------------
%% Internal exports
%%-----------------------------------------------------------------
-export([]).

%%-----------------------------------------------------------------
%% External functions
%%-----------------------------------------------------------------

%%-----------------------------------------------------------------
%% Func: filename_push
%%
%% Pushes a file name, can also push ignore in which case means that
%% no files should ever be opened at this scope. Note that empty in
%% the file descriptor entries means that the file just isn't open
%% yet.
%%-----------------------------------------------------------------
filename_push(G, _N, ignore, _) ->
    G#genobj{stubfile=[ignore | G#genobj.stubfile],
	     stubfiled=[ignore | G#genobj.stubfiled],
	     skelfile=[ignore | G#genobj.skelfile],
	     skelfiled=[ignore | G#genobj.skelfiled],
    	     includefile=[ignore | G#genobj.includefile],
	     includefiled=[ignore | G#genobj.includefiled]};

filename_push(G, N, X, Lang) ->
    Fullname = [ic_forms:get_id2(X) | N],
    EName0 = ic_util:to_undersc(Fullname),
    
    DoGen = ic_genobj:do_gen(G),

    ImplName = find_impl_name(G, Fullname),

    {StubName, EName} =
	case Lang of
	    erlang ->
		{join(ic_options:get_opt(G, stubdir), add_dot_erl(EName0)), 
		 EName0};
	    erlang_template ->
		{join(ic_options:get_opt(G, stubdir), add_dot_erl(ImplName)), 
		 ImplName};
	    c ->
		{join(ic_options:get_opt(G, stubdir), add_dot_c(EName0)), 
		 EName0};
	    c_server ->
		{join(ic_options:get_opt(G, stubdir), add_dot_c(EName0++"__s")), 
		 EName0};
	    erlang_template_no_gen ->
		{undefined, EName0};
	    erlang_no_stub ->
		{undefined, EName0};
	    c_no_stub ->
		{undefined, EName0}; 
	    c_server_no_stub ->
		{undefined, EName0}
	end,
    Stub = if DoGen==true -> 
		   case StubName of
		       undefined ->
			   ignore;
		       _ ->
			   ic_codegen:emit_stub_head(G, open(empty, StubName), EName, Lang)
		   end;
	      true -> ignore end,
    
    HrlName = case Lang of
		  erlang_template ->
		      ignore;
		  erlang_template_no_gen ->
		      ignore;
		  erlang ->
		      ?ifopt2(G, gen_hrl, 
			      join(ic_options:get_opt(G, stubdir), add_dot_hrl(EName)),
			      ignore);
		  c ->
		      ?ifopt2(G, gen_hrl, 
			      join(ic_options:get_opt(G, stubdir), add_dot_h(EName)),
			      ignore);
		  c_server ->
		      ?ifopt2(G, gen_hrl, 
			      join(ic_options:get_opt(G, stubdir),
				   add_dot_h(EName++"__s")),
			      ignore);
		  erlang_no_stub ->
		      ?ifopt2(G, gen_hrl, 
			      join(ic_options:get_opt(G, stubdir), add_dot_hrl(EName)),
			      ignore);
		  c_no_stub ->
		      ?ifopt2(G, gen_hrl, 
			      join(ic_options:get_opt(G, stubdir), add_dot_h(EName)),
			      ignore);
		  c_server_no_stub ->
		      ?ifopt2(G, gen_hrl, 
			      join(ic_options:get_opt(G, stubdir),
				   add_dot_h(EName++"__s")),
			      ignore)
	      end,
    Hrl = if  DoGen==true -> 
		  case Lang of
		      erlang_template ->
			  ignore;
		      erlang_template_no_gen ->
			  ignore;
		      erlang_no_stub ->
			  ic_codegen:emit_hrl_head(G, open(empty, HrlName),
						   EName, erlang);
		      c_no_stub ->
			  ic_codegen:emit_hrl_head(G, open(empty, HrlName),
						   EName, c);
		      c_server_no_stub ->
			  ic_codegen:emit_hrl_head(G, open(empty, HrlName),
						   EName, c_server);
		      _ ->
			  ic_codegen:emit_hrl_head(G, open(empty, HrlName),
						   EName, Lang)
		  end;
	      true -> ignore end,
    
    G#genobj{impl=ImplName,
	     stubfile=[StubName | G#genobj.stubfile],
	     stubfiled=[Stub | G#genobj.stubfiled],
	     includefile=[HrlName | G#genobj.includefile],
	     includefiled=[Hrl | G#genobj.includefiled]}.

%%-----------------------------------------------------------------
%% Func: join/2
%%
%% Special version of filename join.
%%-----------------------------------------------------------------
join([], File) ->
    File;
join(Path, File) ->
    filename:join(Path, File).


%%-----------------------------------------------------------------
%% Func: filename_pop/2
%%-----------------------------------------------------------------
filename_pop(G, Lang) ->
%%    io:format("Popped file names: ~p~n", [hd(G#genobj.stubfile)]),
%%    case is_skelfile_open(G) of
%%	true -> emit_skel_footer(G);
%%	false -> ok end,
%%    close(hd(G#genobj.skelfiled)),
    close(hd(G#genobj.stubfiled)),
    ic_codegen:emit_hrl_foot(G, Lang),
    close(hd(G#genobj.includefiled)),
    G#genobj{stubfile=tl(G#genobj.stubfile),
	     stubfiled=tl(G#genobj.stubfiled),
%%	     skelfile=tl(G#genobj.skelfile),
%%	     skelfiled=tl(G#genobj.skelfiled),
    	     includefile=tl(G#genobj.includefile),
	     includefiled=tl(G#genobj.includefiled)}.



%%-----------------------------------------------------------------
%% Func: javaInterfaceFilePush/3
%%-----------------------------------------------------------------
javaInterfaceFilePush(G, N, X) ->
    Name = ic_forms:get_java_id(X),
    {InterfaceFd, InterfaceFileName} = open_java_file(G, N, Name),
    
    StubClassName = "_" ++ Name ++ "Stub",
    {StubFd, StubFileName} = open_java_file(G, N, StubClassName),
    
    SkelClassName = "_" ++ Name ++ "ImplBase",
    {SkelFd, SkelFileName} = open_java_file(G, N, SkelClassName),
    
    HelperClassName = Name ++ "Helper",
    {HelperFd, HelperFileName} = open_java_file(G, N, HelperClassName),
    
    HolderClassName = Name ++ "Holder",
    {HolderFd, HolderFileName} = open_java_file(G, N, HolderClassName),
    
    G#genobj{
      interfacefile=[InterfaceFileName | G#genobj.interfacefile],
      interfacefiled=[InterfaceFd | G#genobj.interfacefiled],
      stubfile=[StubFileName | G#genobj.stubfile],
      stubfiled=[StubFd | G#genobj.stubfiled],
      skelfile=[SkelFileName | G#genobj.skelfile],
      skelfiled=[SkelFd | G#genobj.skelfiled],
      helperfile=[HelperFileName | G#genobj.helperfile],
      helperfiled=[HelperFd | G#genobj.helperfiled],
      holderfile=[HolderFileName | G#genobj.holderfile],
      holderfiled=[HolderFd | G#genobj.holderfiled]}.





%%-----------------------------------------------------------------
%% Func: javaInterfaceFilePop/1
%%-----------------------------------------------------------------
javaInterfaceFilePop(G) ->
    close(hd(G#genobj.interfacefiled)),
    close(hd(G#genobj.stubfiled)),
    close(hd(G#genobj.skelfiled)),
    close(hd(G#genobj.helperfiled)),
    close(hd(G#genobj.holderfiled)),
    G#genobj{
      interfacefile=tl(G#genobj.interfacefile),
      interfacefiled=tl(G#genobj.interfacefiled),
      stubfile=tl(G#genobj.stubfile),
      stubfiled=tl(G#genobj.stubfiled),
      skelfile=tl(G#genobj.skelfile),
      skelfiled=tl(G#genobj.skelfiled),
      helperfile=tl(G#genobj.helperfile),
      helperfiled=tl(G#genobj.helperfiled),
      holderfile=tl(G#genobj.holderfile),
      holderfiled=tl(G#genobj.holderfiled)}.

%%-----------------------------------------------------------------
%% Func: createDirectory/2 
%%-----------------------------------------------------------------
createDirectory(_G, []) ->
    ok;
createDirectory(G, Scope) ->
    Path = ic_file:join(ic_options:get_opt(G, stubdir), ic_pragma:slashify(Scope)),
    case file:make_dir(Path) of
	ok ->
	    ok;
	{error, eexist} ->
	    ok;
	{error, Reason} ->
	    ic_error:fatal_error(G, {create_dir, Path, Reason})
    end.


%%-----------------------------------------------------------------
%% Func: createJavaDirectory/2 
%%-----------------------------------------------------------------
createJavaDirectory(_G, []) ->
    ok;
createJavaDirectory(G, Scope) ->
    JavaScope = ic_util:adjustScopeToJava(G,Scope),
    Path = ic_file:join(ic_options:get_opt(G, stubdir), ic_pragma:slashify(JavaScope)),
    case file:make_dir(Path) of
	ok ->
	    ok;
	{error, eexist} ->
	    ok;
	{error, Reason} ->
	    ic_error:fatal_error(G, {create_dir, Path, Reason})
    end.
    



%%-----------------------------------------------------------------
%% Func: createJavaFileName/3
%%-----------------------------------------------------------------
createJavaFileName(G, Scope, FName) ->
    JavaScope = ic_util:adjustScopeToJava(G,Scope),
    join(ic_options:get_opt(G, stubdir), 
	 ic_pragma:slashify([FName++".java"|JavaScope])).

%%-----------------------------------------------------------------
%% Func: close/2 (used to be file_close)
%%-----------------------------------------------------------------
close(empty) -> ok;
close(ignore) -> ok;
close(Fd) -> 
    file:close(Fd).

%%-----------------------------------------------------------------
%% Func: remove_ext/1
%%-----------------------------------------------------------------
remove_ext(File) ->
    filename:rootname(filename:basename(File)).

%%-----------------------------------------------------------------
%% Func: open/2 (used to be file_open)
%%-----------------------------------------------------------------
open(_, ignore) -> ignore;
open(empty, Name) -> 
    case file:open(Name, [raw, binary, write]) of
	{ok, Fd} ->
	    Fd;
	{error, Reason} ->
	    exit({error, Reason})
%%		ic_error:fatal_error(G, {open_file, Name, Reason})
    end.

%%-----------------------------------------------------------------
%% Func: open_java_file/3
%%-----------------------------------------------------------------
open_java_file(G, N, Name) ->
    createJavaDirectory(G, N),
    FName =  createJavaFileName(G, N, Name),
    case file:open(FName, [raw, binary, write]) of
        {ok, Fd} ->
	    ic_codegen:emit_stub_head(G, Fd, Name, java),
	    emit_package(G, N, Fd),
            {Fd, FName};
        {error, Reason} ->
	    ic_error:fatal_error(G, {open_file, FName, Reason})
    end.

%%-----------------------------------------------------------------
%% Func: emit_package/3
%%-----------------------------------------------------------------
emit_package(_G, [], _Fd) ->
    ok;
emit_package(G, N, Fd) ->
    ic_codegen:emit(Fd, "package ~s;\n", [ic_util:to_dot(G,N)]),
    ic_codegen:nl(Fd).

%%-----------------------------------------------------------------
%% Func: add_dot_erl/1 
%%-----------------------------------------------------------------
add_dot_erl(F) ->
    File = ic_util:to_list(F),
    F2 = lists:reverse(File),
    case F2 of 
	[$l, $r, $e, $. | _Rest] -> 
	    File;
	_ ->
	    File ++ ".erl"
    end.

%%-----------------------------------------------------------------
%% Func: add_dot_hrl/1 
%%-----------------------------------------------------------------
add_dot_hrl(F) ->
    File = ic_util:to_list(F),
    F2 = lists:reverse(File),
    case F2 of 
	[$l, $r, $h, $. | _Rest] -> 
	    File;
	_ -> 
	    File ++ ".hrl"
    end.

%%-----------------------------------------------------------------
%% Func: add_dot_c/1 
%%-----------------------------------------------------------------
add_dot_c(F) ->
    File = ic_util:to_list(F),
    F2 = lists:reverse(File),
    case F2 of 
	[$c, $. | _Rest] -> 
	    File;
	_ -> 
	    File ++ ".c"
    end.

%%-----------------------------------------------------------------
%% Func: add_dot_h/1 
%%-----------------------------------------------------------------
add_dot_h(F) ->
    File = ic_util:to_list(F),
    F2 = lists:reverse(File),
    case F2 of 
	[$h, $. | _Rest] -> 
	    File;
	_ -> 
	    File ++ ".h"
    end.

%%-----------------------------------------------------------------
%% Func: add_dot_java/1 
%%-----------------------------------------------------------------
add_dot_java(F) ->
    File = ic_util:to_list(F),
    F2 = lists:reverse(File),
    case F2 of 
	[$a, $v, $a, $j, $. | _Rest] -> 
	    File;
	_ ->
	    File ++ ".java"
    end.

%%-----------------------------------------------------------------
%% Func: add_dot_idl/1 
%%-----------------------------------------------------------------
add_dot_idl(F) ->
    File = ic_util:to_list(F),
    F2 = lists:reverse(File),
    case F2 of 
	[$l, $d, $i, $. | _Rest] -> 
	    File;
	_ ->
	    File ++ ".idl"
    end.


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

%%--------------------------------------------------------------------
%%
%% File handling stuff
%%
%% 
%%	Shall open a file for writing. Also sets up the generator with
%%	usefull bits of information
%%
%%--------------------------------------------------------------------
find_impl_name(G, Name) ->
    N1 = ic_util:to_colon(Name),
    N2 = ic_util:to_undersc(Name),
    case {ic_options:get_opt(G, {impl, N1}),
	  ic_options:get_opt(G, {impl, N2})} of
	{false, false} ->
	    case {ic_options:get_opt(G, {impl, "::"++N1}),
		  ic_options:get_opt(G, {impl, N2})} of
		{false, false} -> N2 ++ "_impl";
		{X, _Y} when X /= false -> ic_util:to_list(X);
		{_X, Y} when Y /= false -> ic_util:to_list(Y)
	    end;
	{X, _Y} when X /= false -> ic_util:to_list(X);
	{_X, Y} when Y /= false -> ic_util:to_list(Y)
    end.
