%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 1996-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%
%%

%%
%% ------------------------------------------------------------
%% Basic Frame Type.
%% ------------------------------------------------------------

-module(gstk_frame).

%%-----------------------------------------------------------------------------
%% 			    FRAME OPTIONS
%%
%%  Attributes:
%%	anchor			n,w,s,e,nw,se,ne,sw,center
%%	bg			Color
%%	bw			Int
%%	data			Data
%%	height			Int
%%	highlightbg		Color
%%	highlightbw		Int
%%	highlightfg		Color
%%	relief			Relief	[flat|raised|sunken|ridge|groove]
%%	width			Int
%%	x			Int
%%	y			Int
%%      cursor                  arrow|busy|cross|hand|help|resize|text
%%
%%  Commands:
%%	setfocus		Bool
%%
%%  Events:
%%	buttonpress		[Bool | {Bool, Data}]
%%	buttonrelease		[Bool | {Bool, Data}]
%%	configure		[Bool | {Bool, Data}]
%%	destroy			[Bool | {Bool, Data}]
%%	enter			[Bool | {Bool, Data}]
%%	focus			[Bool | {Bool, Data}]
%%	keypress		[Bool | {Bool, Data}]
%%	keyrelease		[Bool | {Bool, Data}]
%%	leave			[Bool | {Bool, Data}]
%%	motion			[Bool | {Bool, Data}]
%%
%%  Read Options:
%%	children
%%	id
%%	parent
%%	type
%%

-export([create/3,config/3,read/3,delete/2,event/5,option/5,read_option/5,
	mk_create_opts_for_child/4]).

-include("gstk.hrl").

%%-----------------------------------------------------------------------------
%%			MANDATORY INTERFACE FUNCTIONS
%%-----------------------------------------------------------------------------
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Function   	: create/3
%% Purpose    	: Create a widget of the type defined in this module.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
create(DB, GstkId, Opts) ->
    TkW = gstk_generic:mk_tkw_child(DB,GstkId),
    NGstkid=GstkId#gstkid{widget=TkW},
    PlacePreCmd = [";place ", TkW],
    case gstk_generic:make_command(Opts, NGstkid, TkW, "", PlacePreCmd, DB) of
	{error,Reason} -> {error,Reason};
	Cmd when is_list(Cmd) ->
	    gstk:exec(["frame ", TkW,
		      " -relief raised -bo 0",Cmd]),
	    NGstkid
    end.

mk_create_opts_for_child(DB,Cgstkid, Pgstkid, Opts) ->
    gstk_generic:mk_create_opts_for_child(DB,Cgstkid,Pgstkid,Opts).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Function   	: config/3
%% Purpose    	: Configure a widget of the type defined in this module.
%% Args        	: DB	  - The Database
%%		  Gstkid   - The gstkid of the widget
%%		  Opts    - A list of options for configuring the widget
%%
%% Return 	: [true | {bad_result, Reason}]
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
config(DB, Gstkid, Opts) ->
    TkW = Gstkid#gstkid.widget,
    SimplePreCmd = [TkW, " conf"],
    PlacePreCmd = [";place ", TkW],
    Opts2 = atomic_width_height(false,false,Opts),
    gstk_generic:mk_cmd_and_exec(Opts2,Gstkid,TkW,SimplePreCmd,PlacePreCmd,DB).

atomic_width_height(false,false,[]) ->
    [];
atomic_width_height(false,Width,[]) ->
    [{width,Width}];
atomic_width_height(Height,false,[]) ->
    [{height,Height}];
atomic_width_height(H,W,[]) ->
    [{width_height,{W,H}}];
atomic_width_height(_,W,[{height,H}|Opts]) ->
    atomic_width_height(H,W,Opts);
atomic_width_height(H,_,[{width,W}|Opts]) ->
    atomic_width_height(H,W,Opts);
atomic_width_height(H,W,[Opt|Opts]) ->
    [Opt|atomic_width_height(H,W,Opts)].

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Function   	: read/3
%% Purpose    	: Read one option from a widget
%% Args        	: DB	  - The Database
%%		  Gstkid   - The gstkid of the widget
%%		  Opt     - An option to read
%%
%% Return 	: [OptionValue | {bad_result, Reason}]
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
read(DB, Gstkid, Opt) ->
    gstk_generic:read_option(DB, Gstkid, Opt).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Function   	: delete/2
%% Purpose    	: Delete widget from databas and return tkwidget to destroy
%% Args        	: DB	  - The Database
%%		  Gstkid   - The gstkid of the widget
%%
%% Return 	: TkWidget to destroy
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
delete(DB, Gstkid) ->
    gstk_db:delete_widget(DB, Gstkid),
    Gstkid#gstkid.widget.

event(DB, Gstkid, Etype, Edata, Args) ->
    gstk_generic:event(DB, Gstkid, Etype, Edata, Args).


%%-----------------------------------------------------------------------------
%%			MANDATORY FUNCTIONS
%%-----------------------------------------------------------------------------
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Function   	: option/4
%% Purpose    	: Take care of options
%% Args        	: Option  - An option tuple
%%		  Gstkid   - The gstkid of the widget
%%		  TkW     - The  tk-widget
%%		  DB	  - The Database
%%
%% Return 	: A tuple {OptionType, OptionCmd}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
option(Option, Gstkid, _TkW, DB,_) ->
    case Option of
	{bg,          Color} -> {s, [" -bg ", gstk:to_color(Color)]};
	{packer_x, _Pack} ->
            gstk_db:insert_opt(DB,Gstkid,Option),
	    none;
	{packer_y, _Pack} ->
	    gstk_db:insert_opt(DB,Gstkid,Option),
	    none;
	{width, W} ->
	    execute_pack_cmds(DB,xpack(W,DB,Gstkid)),
	    {s,[" -wi ", gstk:to_ascii(W)]};
	{height, H} ->
	    execute_pack_cmds(DB,ypack(H,DB,Gstkid)),
	    {s,[" -he ", gstk:to_ascii(H)]};
	{width_height,{W,H}} ->
	    execute_pack_cmds(DB, merge_pack_cmds(xpack(W,DB,Gstkid),
						  ypack(H,DB,Gstkid))),
	    {s,[" -he ", gstk:to_ascii(H)," -wi ", gstk:to_ascii(W)]};
	_  -> invalid_option
    end.

xpack(W,DB,Gstkid) ->
    gstk_db:insert_opt(DB,Gstkid,{width,W}),
    case gstk_db:opt_or_not(DB,Gstkid,packer_x) of
	{value,Pack} when is_list(Pack) ->
	    ColSiz = gs_packer:pack(W,Pack),
	    pack_children(pack_x,x,width,DB,
			  gstk_db:lookup_kids(DB,Gstkid#gstkid.id),
			  ColSiz);
	_Else -> []
    end.

ypack(H,DB,Gstkid) ->
    gstk_db:insert_opt(DB,Gstkid,{height,H}),
    case gstk_db:opt_or_not(DB,Gstkid,packer_y) of
	{value,Pack} when is_list(Pack) ->
	    ColSiz = gs_packer:pack(H,Pack),
	    pack_children(pack_y,y,height,DB,
			  gstk_db:lookup_kids(DB,Gstkid#gstkid.id),
			  ColSiz);
	_Else -> []
    end.

merge_pack_cmds([{Id,Opts1}|Cmds1],[{Id,Opts2}|Cmds2]) ->
    [{Id,Opts1++Opts2}|merge_pack_cmds(Cmds1,Cmds2)];
merge_pack_cmds(L1,L2) ->
    L1++L2.

execute_pack_cmds(DB,[{Id,Opts}|Cmds]) ->
    gstk:config_impl(DB,Id,Opts),
    execute_pack_cmds(DB,Cmds);
execute_pack_cmds(_,[]) ->
    ok.

%%----------------------------------------------------------------------
%% Returns: list of {Id,Opts} to be executed (or merged with other first)
%%----------------------------------------------------------------------
pack_children(PackOpt,PosOpt,SizOpt,DB,Kids,Sizes) ->
    Schildren = keep_packed(Kids,PackOpt,DB),
    pack_children2(PackOpt,PosOpt,SizOpt,Schildren,Sizes).

pack_children2(PackOpt,PosOpt,SizOpt,[{StartStop,Id}|Childs],Sizes) ->
    [pack_child(Id,StartStop,SizOpt,PosOpt,Sizes)
     | pack_children2(PackOpt,PosOpt,SizOpt,Childs,Sizes)];
pack_children2(_,_,_,[],_) ->
    [].

pack_child(Id,{StartPos,StopPos},SizOpt,PosOpt,Sizes) ->
    {Pos,Size} = find_pos(StartPos,StopPos,1,0,0,Sizes),
    {Id,[{PosOpt,Pos},{SizOpt,Size}]}.

%%----------------------------------------------------------------------
%% Returns: {PixelPos,PixelSize}
%%----------------------------------------------------------------------
find_pos(_StartPos,Pos,Pos,AccPixelPos,AccPixelSize,[Size|_]) ->
    {AccPixelPos,Size+AccPixelSize};
find_pos(StartPos,StopPos,Pos,AccPixelPos,0,[Size|Sizes])
  when Pos < StartPos ->
    find_pos(StartPos,StopPos,Pos+1,Size+AccPixelPos,0,Sizes);
find_pos(_StartPos,StopPos,Pos,AccPixelPos,AccPixelSize,[Size|Sizes])
  when Pos < StopPos ->
    find_pos(Pos,StopPos,Pos+1,AccPixelPos,Size+AccPixelSize,Sizes).

    

keep_packed([Id|Ids],PackOpt,DB) ->
    case gstk:read_impl(DB,Id,PackOpt) of
	undefined ->
	    keep_packed(Ids,PackOpt,DB);
	StartStop ->
	    [{StartStop,Id} | keep_packed(Ids,PackOpt,DB)]
    end;
keep_packed([],_,_) ->
    [].
    


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Function   	: read_option/3
%% Purpose    	: Take care of a read option
%% Args        	: DB	  - The Database
%%		  Gstkid   - The gstkid of the widget
%%		  Option  - An option
%%
%% Return 	: The value of the option or invalid_option
%%		  [OptionValue | {bad_result, Reason}]
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
read_option(Option,Gstkid,TkW,_DB,_) -> 
    case Option of
	bg            -> tcl2erl:ret_color([TkW," cg -bg"]);
	_ -> {bad_result, {Gstkid#gstkid.objtype, invalid_option, Option}}
    end.

%% ----- Done -----