%%-*-erlang-*-
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 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%
%%----------------------------------------------------------------------
%% File    : xmerl_sax_simple_dom.erl
%% Description : 
%%
%% Created : 17 Apr 2009 
%%----------------------------------------------------------------------
-module(xmerl_sax_simple_dom).

%%----------------------------------------------------------------------
%% Include files
%%----------------------------------------------------------------------
-include("xmerl_sax_old_dom.hrl").

%%----------------------------------------------------------------------
%% External exports
%%----------------------------------------------------------------------
-export([
	 initial_state/0,
	 get_dom/1,
	 event/3
        ]).

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

%%======================================================================
%% Macros
%%======================================================================
%%----------------------------------------------------------------------
%% Error handling
%%----------------------------------------------------------------------
-define(error(Reason), 
	throw({sax_simple_dom_error, Reason})).

%%----------------------------------------------------------------------

%%======================================================================
%% Records
%%======================================================================

%%----------------------------------------------------------------------
%% State record for the validator
%%----------------------------------------------------------------------
-record(xmerl_sax_simple_dom_state, {
	  tags=[],         %% Tag stack
	  cno=[],          %% Current node number
	  namespaces = [], %% NameSpace stack
	  dom=[]           %% DOM structure 
	 }).

%%======================================================================
%% External functions
%%======================================================================

%%----------------------------------------------------------------------
%% Function: initial_state() -> Result
%% Parameters: 
%% Result: 
%% Description:
%%----------------------------------------------------------------------
initial_state() ->
    #xmerl_sax_simple_dom_state{}.

%%----------------------------------------------------------------------
%% Function: get_dom(State) -> Result
%% Parameters: 
%% Result: 
%% Description:
%%----------------------------------------------------------------------
get_dom(#xmerl_sax_simple_dom_state{dom=Dom}) ->
    Dom.

%%----------------------------------------------------------------------
%% Function: event(Event, LineNo, State) -> Result
%% Parameters: 
%% Result: 
%% Description:
%%----------------------------------------------------------------------
event(Event, _LineNo, State) ->
    build_dom(Event, State).


%%======================================================================
%% Internal functions
%%======================================================================

%%----------------------------------------------------------------------
%% Function  : build_dom(Event, State) -> Result
%% Parameters: Event = term()
%%             State = #xmerl_sax_simple_dom_state{}
%% Result    : #xmerl_sax_simple_dom_state{} |
%% Description: 
%%----------------------------------------------------------------------

%% Document
%%----------------------------------------------------------------------
build_dom(startDocument, State) ->
    State#xmerl_sax_simple_dom_state{dom=[startDocument]};
build_dom(endDocument, 
	  #xmerl_sax_simple_dom_state{dom=[{Tag, Attributes, Content} |D]} = State) ->
    case D of
	[startDocument] ->
	    State#xmerl_sax_simple_dom_state{dom=[{Tag, Attributes, 
						   lists:reverse(Content)}]};
	[#xmlDecl{} = Decl, startDocument] ->
	    State#xmerl_sax_simple_dom_state{dom=[Decl, {Tag, Attributes, 
						   lists:reverse(Content)}]};
	_ ->
	    io:format("~p\n", [D]),
	    ?error("we're not at end the document when endDocument event is encountered.")
    end;

%% Element
%%----------------------------------------------------------------------
build_dom({startElement, _Uri, LocalName, _QName, Attributes}, 
	  #xmerl_sax_simple_dom_state{tags=T, cno=CN, dom=D} = State) ->

    A = parse_attributes(LocalName, Attributes),
    {Num, NewCN} =
	case CN of
	    [] ->
		{1, [1]};
	    [ N |CNs] -> 
		{N, [1, N+1 |CNs]}
	end,

    State#xmerl_sax_simple_dom_state{tags=[{list_to_atom(LocalName), Num} |T],
				     cno=NewCN,
				     dom=[{list_to_atom(LocalName), 
					   lists:reverse(A),
					   []
					  } | D]};
build_dom({endElement, _Uri, LocalName, _QName}, 
	  #xmerl_sax_simple_dom_state{tags=[_ |T],
				   cno=[_ |CN],
				   dom=[{CName, CAttributes, CContent}, 
					{PName, PAttributes, PContent} | D]} = State) ->
    case list_to_atom(LocalName) of
	CName ->	    
	    State#xmerl_sax_simple_dom_state{tags=T,
					     cno=CN,
					     dom=[{PName, PAttributes, 
						   [{CName, CAttributes, 
						     lists:reverse(CContent)}
						    |PContent]
						  } | D]};
	_ ->
	    ?error("Got end of element: " ++ LocalName ++ " but expected: " ++ 
		   CName)
    end;

%% Text 
%%----------------------------------------------------------------------
build_dom({characters, String},
	  #xmerl_sax_simple_dom_state{tags=_T, 
				      cno=[Num |CN],
				      dom=[{Name, Attributes, Content}| D]} = State) ->
    State#xmerl_sax_simple_dom_state{cno=[Num+1 |CN], 
				     dom=[{Name, Attributes, [String|Content]} | D]};

build_dom({ignorableWhitespace, String},
	  #xmerl_sax_simple_dom_state{tags=T, 
				   cno=[Num |CN],
				   dom=[{Name, Attributes, Content}| D]} = State) ->
    State#xmerl_sax_simple_dom_state{cno=[Num+1 |CN],
				  dom=[{Name, Attributes, [#xmlText{value=String, parents=T, pos=Num, type=text}|Content]} | D]};

%% Comments
%%----------------------------------------------------------------------
build_dom({comment, String},
	  #xmerl_sax_simple_dom_state{tags=T, 
				   cno=[Num |CN],
				   dom=[#xmlElement{content=C} = Current| D]} = State) ->
    State#xmerl_sax_simple_dom_state{cno=[Num+1 |CN],
				  dom=[Current#xmlElement{content=[#xmlComment{parents=T, pos=Num, value=String}|C]} | D]};

%% NameSpaces
%%----------------------------------------------------------------------
build_dom({startPrefixMapping, [], _Uri}, State) -> 
    State;
build_dom({startPrefixMapping, Prefix, Uri},
	  #xmerl_sax_simple_dom_state{namespaces=NS} = State) -> 
    State#xmerl_sax_simple_dom_state{namespaces=[{Prefix, list_to_atom(Uri)} |NS]};
build_dom({endPrefixMapping, Prefix},
	  #xmerl_sax_simple_dom_state{namespaces=[{Prefix, _} |NS]} = State) -> 
    State#xmerl_sax_simple_dom_state{namespaces=NS};

%% Processing instructions
%%----------------------------------------------------------------------
build_dom({processingInstruction,"xml", PiData},
	  #xmerl_sax_simple_dom_state{dom=D} = State) ->
    {Vsn, PiData1}  = find_and_remove_attribute("version", PiData, []),
    {Enc, PiData2}  = find_and_remove_attribute("encoding", PiData1, []),
    {Standalone, PiData3}  = find_and_remove_attribute("standalone", PiData2, yes),
    State#xmerl_sax_simple_dom_state{dom=[#xmlDecl{vsn=Vsn, encoding=Enc, standalone=Standalone, attributes=PiData3}| D]};
build_dom({processingInstruction, PiTarget, PiData},
	  #xmerl_sax_simple_dom_state{cno=[Num |CN],
				   dom=[#xmlElement{content=C} = Current| D]} = State) ->
    State#xmerl_sax_simple_dom_state{cno=[Num+1 |CN], 
				  dom=[Current#xmlElement{content=[#xmlPI{name=PiTarget,pos=Num, value=PiData}
								   |C]} | D]};


%% Default
%%----------------------------------------------------------------------
build_dom(_E, State) ->
    State. 





%%----------------------------------------------------------------------
%% Function  : parse_attributes(ElName, Attributes) -> Result
%% Parameters: 
%% Result    : 
%% Description: 
%%----------------------------------------------------------------------
parse_attributes(ElName, Attributes) ->
    parse_attributes(ElName, Attributes, 1, []).

parse_attributes(_, [], _, Acc) ->
    Acc;
parse_attributes(ElName, [{_Uri, _Prefix, LocalName, AttrValue} |As], N, Acc) ->  
    parse_attributes(ElName, As, N+1, [{list_to_atom(LocalName), AttrValue} |Acc]).



%%----------------------------------------------------------------------
%% Function  : find_and_remove_attribute(Key, Data, Default) -> Result
%% Parameters: 
%% Result    : 
%% Description: 
%%----------------------------------------------------------------------
find_and_remove_attribute(Key, Data, Default) ->
    case lists:keysearch(Key, 1, Data) of
	{value, {Key, Value}} ->
	    Data2 = lists:keydelete(Key, 1, Data),
	    {Value, Data2};
	false ->
	    {Default, Data}
    end.