%% ``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$
%%


-module(tv_pb).



-export([pb/1]).


-include("tv_int_def.hrl").
-include("tv_pd_int_msg.hrl").
-include("tv_pb_int_def.hrl").








%%%*********************************************************************
%%% EXTERNAL FUNCTIONS
%%%*********************************************************************




%%======================================================================
%% Function:      pb.
%%
%% Return Value:  None.
%%
%% Description:   Process controlling the grid buttons on the display.
%%
%% Parameters:    None.
%%======================================================================


pb(ParentPid) ->
    process_flag(trap_exit, true),
    ProcVars = #process_variables{parent_pid = ParentPid},
    loop(ProcVars).








%%%********************************************************************
%%% INTERNAL FUNCTIONS
%%%********************************************************************





%%======================================================================
%% Function:      loop.
%%
%% Return Value:  None.
%%
%% Description:   Eternal (well, almost) loop, receiving messages and 
%%                handling them.
%%
%% Parameters:    
%%======================================================================


loop(ProcVars) ->
    receive
	Msg ->
	    case Msg of
		
		#pb_update_vbtns{} ->
		    NewProcVars = update_vbtns(Msg, ProcVars),
		    loop(NewProcVars);

		#pb_key_info{} ->
		    NewProcVars = update_keys(Msg, ProcVars),
		    loop(NewProcVars);

		#pb_update_hbtns{} ->
		    NewProcVars = update_hbtns(Msg, ProcVars),
		    loop(NewProcVars);

		#pb_set_sort_col{} ->
		    NewProcVars = set_sort_col(Msg, ProcVars),
		    loop(NewProcVars);

		#pb_remove_marks{} ->
		    NewProcVars = remove_marks(ProcVars),
		    loop(NewProcVars);

		#pb_init_btns{} ->
		    NewProcVars = init_btns(Msg, ProcVars),
		    loop(NewProcVars);

		{gs, Id, Event, Data, Args} ->
		    NewProcVars = gs_messages({Id, Event, Data, Args}, ProcVars),
		    loop(NewProcVars);


		{'EXIT', Pid, Reason} ->
		    ParentPid = ProcVars#process_variables.parent_pid,
		    exit_signals({Pid, Reason}, ParentPid, ProcVars),
		    loop(ProcVars);

		_Other ->
		    loop(ProcVars)
	    end
    end.








%%======================================================================
%% Function:      
%%
%% Return Value:  
%%
%% Description:   
%%
%% Parameters:    
%%======================================================================


exit_signals(ExitInfo, ParentPid, _ProcVars) ->
    case ExitInfo of
	{ParentPid, _Reason} ->
	    exit(normal);
	_Other ->
	    done
    end.









%%======================================================================
%% Function:      
%%
%% Return Value:  
%%
%% Description:   
%%
%% Parameters:    
%%======================================================================


gs_messages(Msg, ProcVars) ->

    case Msg of

	{Id, click, {hbtn, RealCol, VirtualCol}, _Args} ->
	    handle_col_marking(Id, RealCol, VirtualCol, ProcVars);

	{Id, buttonpress, {resbtn, RealCol, VirtualCol, Xpos}, [1 | _Tail]} ->
	    handle_col_resizing(Id, RealCol, VirtualCol, Xpos, ProcVars),
	    ProcVars;

	{_Id, click, {vbtn, RealRow, VirtualRow}, _Args} ->
	    handle_row_marking(RealRow, VirtualRow, ProcVars);

	_OtherMessage ->
	    ProcVars

    end.
	    







%%======================================================================
%% Function:      
%%
%% Return Value:  
%%
%% Description:   
%%
%% Parameters:    
%%======================================================================


remove_marks(ProcVars) ->
    #process_variables{col_mark_params = ColMarkP,
		       row_mark_params = RowMarkP}  = ProcVars,

    #col_mark_params{col_btn_id          = BtnId,
		     virtual_col_marked  = VirtualCol,
		     virtual_sort_col    = SortCol}  = ColMarkP,
    
    case BtnId of 
	undefined ->
	    done;
	_AnyId ->
	    case VirtualCol of
		SortCol ->
		    gs:config(BtnId, [{bg, ?SORT_MARK_COLOR},
				      {fg, {0, 0, 0}}
				     ]);
		_Other ->
		    gs:config(BtnId, [{bg, ?DEFAULT_BG_COLOR},
				      {fg, {0, 0, 0}}
				     ])
	    end
    end,
    
    NewRowMarkP = RowMarkP#row_mark_params{virtual_row_marked = undefined,
					   real_row_marked    = undefined
					  },
    NewColMarkP = ColMarkP#col_mark_params{col_btn_id         = undefined,
					   virtual_col_marked = undefined
					  },
    ProcVars#process_variables{col_mark_params = NewColMarkP,
			       row_mark_params = NewRowMarkP
			      }.
    
				   
		    
		    
		    
    
    
    




%%======================================================================
%% Function:      
%%
%% Return Value:  
%%
%% Description:   
%%
%% Parameters:    
%%======================================================================


handle_col_marking(BtnId, RealCol, VirtualCol, ProcVars) ->
    #process_variables{parent_pid      = PdPid,
		       col_mark_params = ColMarkP,
		       row_mark_params = RowMarkP}  = ProcVars,

    #col_mark_params{col_btn_id          = OldBtnId,
		     virtual_col_marked  = OldVirtualCol,
		     virtual_sort_col    = SortCol}  = ColMarkP,

    {ColMarked, NewColMarkP} = mark_col_btn(BtnId, OldBtnId, VirtualCol, 
					    OldVirtualCol, RealCol, SortCol, 
					    ColMarkP),

    PdPid ! #pb_col_marked{sender      = self(),
			   col_marked  = ColMarked,
			   real_col    = RealCol,
			   virtual_col = VirtualCol
			  },

    NewRowMarkP = RowMarkP#row_mark_params{virtual_row_marked = undefined,
					   real_row_marked    = undefined
					  },
    ProcVars#process_variables{col_mark_params = NewColMarkP,
			       row_mark_params = NewRowMarkP
			      }.

    




%%======================================================================
%% Function:      
%%
%% Return Value:  
%%
%% Description:   
%%
%% Parameters:    
%%======================================================================


handle_row_marking(RealRow, VirtualRow, ProcVars) ->
    #process_variables{parent_pid      = PdPid,
		       col_mark_params = ColMarkP,
		       row_mark_params = RowMarkP}  = ProcVars,

    #col_mark_params{col_btn_id          = OldBtnId,
		     virtual_col_marked  = OldVirtualCol,
		     virtual_sort_col    = SortCol}  = ColMarkP,

    {_ColMarked, NewColMarkP} = mark_col_btn(OldBtnId, OldBtnId, OldVirtualCol, 
					     OldVirtualCol, undefined, SortCol, 
					     ColMarkP),

    #row_mark_params{virtual_row_marked = OldVirtualRow} = RowMarkP,

       % Check if row shall be marked or unmarked!
    {RowMarked, NewRowMarkP} = check_marked_row(VirtualRow, OldVirtualRow, RealRow,
						RowMarkP),

    PdPid ! #pb_row_marked{sender      = self(),
			   row_marked  = RowMarked,
			   real_row    = RealRow,
			   virtual_row = VirtualRow
			  },
    
    ProcVars#process_variables{row_mark_params = NewRowMarkP,
			       col_mark_params = NewColMarkP}.









%%======================================================================
%% Function:      
%%
%% Return Value:  
%%
%% Description:   
%%
%% Parameters:    
%%======================================================================


%% Three cases: no button previously clicked, or same button clicked,
%% or some other button clicked.

check_marked_row(NewVirtRow, undefined, RealRow, RowMarkP) ->
       % No btn already pressed!
    {true, RowMarkP#row_mark_params{virtual_row_marked = NewVirtRow,
				    real_row_marked    = RealRow}};
check_marked_row(NewVirtRow, OldVirtRow, _RealRow, RowMarkP) when NewVirtRow == OldVirtRow ->
       % The button previously pressed has been pressed again!
    {false, RowMarkP#row_mark_params{virtual_row_marked = undefined,
				     real_row_marked    = undefined}};
check_marked_row(NewVirtRow, _OldVirtRow, RealRow, RowMarkP) ->
       % A new btn has been pressed!
    {true, RowMarkP#row_mark_params{virtual_row_marked = NewVirtRow,
				    real_row_marked    = RealRow}}.












%%======================================================================
%% Function:      
%%
%% Return Value:  
%%
%% Description:   
%%
%% Parameters:    
%%======================================================================


set_sort_col(Msg, ProcVars) ->
    #pb_set_sort_col{virtual_col = SortCol} = Msg,
    tv_pb_funcs:set_new_sort_col(SortCol, ProcVars).
    






%%======================================================================
%% Function:      
%%
%% Return Value:  
%%
%% Description:   
%%
%% Parameters:    
%%======================================================================


%% Three cases: no button previously clicked, or same button clicked,
%% or some other button clicked.

mark_col_btn(NewId, undefined, NewVirtCol, _OldVirtCol, _RealCol, _SortCol, ColMarkP) ->
       % No btn already pressed!
    gs:config(NewId, [{bg, ?COL_MARK_COLOR},
		      {fg, {255, 255, 255}}
		     ]),
    {true, ColMarkP#col_mark_params{col_btn_id         = NewId,
				    virtual_col_marked = NewVirtCol}};
mark_col_btn(NewId, _OldId, NewVirtCol, OldVirtCol, _RealCol, SortCol, ColMarkP) when NewVirtCol == OldVirtCol, NewVirtCol == SortCol ->
       % The button previously pressed has been pressed again!
    gs:config(NewId, [{bg, ?SORT_MARK_COLOR},
		      {fg, {0, 0, 0}}
		     ]),
    {false, ColMarkP#col_mark_params{col_btn_id         = undefined,
				     virtual_col_marked = undefined}};
mark_col_btn(NewId, _OldId, NewVirtCol, OldVirtCol, _RealCol, _SortCol, ColMarkP) when NewVirtCol == OldVirtCol ->
       % The button previously pressed has been pressed again!
    gs:config(NewId, [{bg, ?DEFAULT_BG_COLOR},
		      {fg, {0, 0, 0}}
		     ]),
    {false, ColMarkP#col_mark_params{col_btn_id         = undefined,
				     virtual_col_marked = undefined}};
mark_col_btn(NewId, OldId, NewVirtCol, _OldVirtCol, _RealCol, _SortCol, ColMarkP) ->
       % A new btn has been pressed!
    gs:config(OldId, [{bg, ?DEFAULT_BG_COLOR},
			 {fg, {0, 0, 0}}
			]),
    gs:config(NewId, [{bg, ?COL_MARK_COLOR},
		      {fg, {255, 255, 255}}
		     ]),
    {true, ColMarkP#col_mark_params{col_btn_id         = NewId,
				    virtual_col_marked = NewVirtCol}}.
    		    
		    
	    
	    
    
    





%%======================================================================
%% Function:      
%%
%% Return Value:  
%%
%% Description:   
%%
%% Parameters:    
%%======================================================================


handle_col_resizing(RbtnId, RealCol, VirtualCol, Xpos, ProcVars) ->
    gs:config(RbtnId, [{motion, true}]),
    #process_variables{parent_pid        = ParentPid,
		       grid_frame_id     = GrFrId,
		       grid_frame_height = Height,
		       hbtn_height       = HbtnH,
		       resbtn_width      = RbtnW,
		       cols_shown        = ColsShown} = ProcVars,
    
    LineId = gs:frame(GrFrId, [{width, 1},
			       {height, Height - HbtnH},
			       {x, Xpos},
			       {y, HbtnH - 1},
			       {bg, ?DEFAULT_BG_COLOR}
			      ]),
    MinColWidth = RbtnW,
    
    OldColWidth = lists:nth(RealCol, ColsShown),
    Xdiff = get_xdiff(RbtnId, 1, 0, LineId, Xpos, MinColWidth - OldColWidth),

    ParentPid ! #pb_new_colwidth{sender      = self(),
				 real_col    = RealCol,
				 virtual_col = VirtualCol,
				 xdiff       = Xdiff},

    gs:config(RbtnId, [{motion, false}]),
    gs:destroy(LineId).


    









%%======================================================================
%% Function:      
%%
%% Return Value:  
%%
%% Description:   
%%
%% Parameters:    
%%======================================================================


get_xdiff(Id, Btn, LastXdiff, LineId, LineXpos, MinAllowedXdiff) ->
    receive
	{gs, Id, motion, {resbtn, _RealCol, _VirtCol, _OldXpos}, [NewXdiff | _T]} ->
	    UsedXdiff = max(MinAllowedXdiff, NewXdiff),
	    gs:config(LineId, [{x, LineXpos + UsedXdiff}]),
	    get_xdiff(Id, Btn, UsedXdiff, LineId, LineXpos, MinAllowedXdiff);
	{gs, Id, buttonrelease, _Data, [Btn | _T]} ->
	    LastXdiff;
	{gs, Id, buttonrelease, _Data, _Args} ->
	    get_xdiff(Id, Btn, LastXdiff, LineId, LineXpos, MinAllowedXdiff);
	{gs, Id, buttonpress, _Data, _Args} ->
	    get_xdiff(Id, Btn, LastXdiff, LineId, LineXpos, MinAllowedXdiff)
    end.











%%======================================================================
%% Function:      
%%
%% Return Value:  
%%
%% Description:   
%%
%% Parameters:    
%%======================================================================


init_btns(Msg, ProcVars) ->
    #pb_init_btns{parent_id       = ParentId,
		  parent_width    = Width,
		  parent_height   = Height,
		  ypos            = Ypos,
		  hbtn_height     = HbtnH,
		  resbtn_width    = RbtnW,
		  vbtn_width      = VbtnW,
		  nof_rows        = NofRows,
		  row_height      = RowHeight,
		  first_col_shown = FirstColShown,
		  cols_shown      = ColsShown} = Msg,

    NewProcVars = tv_pb_funcs:init_btns(ParentId, Ypos, HbtnH, VbtnW, RbtnW, 
					 FirstColShown, ColsShown, NofRows, 
					 RowHeight, ProcVars),

    gs:frame(ParentId, [{bg, {0, 0, 0}},
			{bw, 0},
			{width, 1300},
			{height, 1},
			{x, 0},
			{y, Ypos - 1}
		       ]),
    NewProcVars#process_variables{grid_frame_width  = Width,
				  grid_frame_height = Height
				 }.








%%======================================================================
%% Function:      
%%
%% Return Value:  
%%
%% Description:   
%%
%% Parameters:    
%%======================================================================


update_hbtns(Msg, ProcVars) ->
    #pb_update_hbtns{parent_width    = Width,
		     parent_height   = Height,
		     first_col_shown = FirstColShown,
		     cols_shown      = ColsShown} = Msg,

    NewProcVars = tv_pb_funcs:update_hbtns(FirstColShown, ColsShown, ProcVars),

    NewProcVars#process_variables{grid_frame_width  = Width,
				  grid_frame_height = Height
				 }.








%%======================================================================
%% Function:      
%%
%% Return Value:  
%%
%% Description:   
%%
%% Parameters:    
%%======================================================================


update_vbtns(Msg, ProcVars) ->
    #pb_update_vbtns{color_list       = Colors,
		     first_row_shown  = FirstRowShown,
		     nof_rows_shown   = NofRowsShown,
		     blinking_enabled = BlinkEnabled} = Msg,
    
    tv_pb_funcs:update_vbtns(NofRowsShown, FirstRowShown, Colors, BlinkEnabled, 
			      ProcVars).
    





%%======================================================================
%% Function:      
%%
%% Return Value:  
%%
%% Description:   
%%
%% Parameters:    
%%======================================================================


update_keys(Msg, ProcVars) ->
    #pb_key_info{list_of_keys = KeyList} = Msg,
    tv_pb_funcs:update_keys(KeyList, ProcVars).








%%======================================================================
%% Function:      
%%
%% Return Value:  
%%
%% Description:   
%%
%% Parameters:    
%%======================================================================


max(A, B) when A >= B ->
    A;
max(_, B) ->
    B.
    

