%% ``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(othello_board).
-export([start/0,stop/0,init/0]).


%%----------------------------------------------------------------------
%% The Othello program now uses the gs graphical package instead of the 
%% pxw package.
%%
%% Differences are, explanation why and source of change in parenthesis:
%%
%%	-  Buttons looks different (gs feature)
%%	-  The black box around "Black to draw" have been removed. (me)
%%	-  The Colour and Level menues have been moved directly down to the 
%%	     'Status' box. (usability update, my addition)
%%	-  The mouse pointer does not change into a watch when the computer 
%%	     is thinking (not supported in gs)
%%	-  Buttons does not flash when being beeped. (not supported in gs)
%%
%%
%% /Peter
%%
%%----------------------------------------------------------------------


-define(BGCOL,forestgreen).

start() ->
    spawn(othello_board,init,[]).

stop() ->
    ok.

%% This is not an application so we don't have their way of knowing
%% a private data directory where the GIF files are located (this directory).
%% We can find GS and makes it relative from there /kgb

-define(BitmapPath,"../contribs/othello/priv").

setup_path() ->
    GsPrivDir = code:priv_dir(gs),
    Path = filename:join(GsPrivDir,?BitmapPath),
    put(path,Path).
    
path() -> get(path).



%%
%% The button area are the Quit, Rules buttons at the top of the window.
%% The Status area is the black and white scores level and colour etc 
%% inbetween the buttons and the othello board.
%% The board is the 8x8 board where othello battles are fought.
%%
init() ->
    process_flag(trap_exit,true),
    setup_path(),
    S = gs:start(),
    put(windowroot,S),				% Ugly global store

    %% Shell coordinates
    W = 496,
    H = 636,
    
    %% Fix top window
    Shell = gs:create(window, S, [{title,"Othello"}, 
				  {width, W},{height, H}]),


    %% Setup window contents

    setup_buttons(Shell,0,0,W,40),		% Fix Menubar
    setup_status_box(Shell,0,40,W,100),		% Fix Status area
    setup_board(Shell,0,140,496,496),		% Combat board

    GamePid = othello:new_game(white,black,1,first_time),

    %% Default settings
    Options = {white,black,1},
    %%Wids = {Status,B,W,Dr,Le,Co},
    Wids = {change,this,at,later,stage,ponto},
    write_options(Options,Wids),

    gs:config(Shell, {map, true}),		%Make win visible

    loop(computer,GamePid,Shell,Wids,Options).






loop(User,GamePid,Shell,Wids,Options) ->
    receive
	{gs,ButtId, click,_ButtId1,[Button]} ->
	    GamePid1 = but_pressed(Button,ButtId,User,GamePid,Shell,
				   Wids,Options),
	    loop(User,GamePid1,Shell,Wids,Options);

	{gs,_, click,_,[MenuItem,_MenuIndex]} ->
	    Ops = menu_selected(MenuItem,User,GamePid,Wids,Options),
	    loop(User,GamePid,Shell,Wids,Ops);

	{'EXIT',GamePid,_} ->
	    loop(User,null,Shell,Wids,Options);

	{'EXIT',_,_} ->
	    loop(User,GamePid,Shell,Wids,Options);

	GameMsg ->
	    game_msg(GameMsg,User,GamePid,Shell,Wids,Options)
    end.

but_pressed("Quit",_ButtId,_User,_GamePid,_Shell,_Wids,_Op) ->
    stop(),
    exit(quit);
but_pressed("Rules",_ButtId,_User,GamePid,_Shell,_Wids,_Op) ->
    io:format("No rules, do as you wish~n",[]),
    GamePid;
but_pressed("Help",_ButtId,_User,GamePid,_Shell,_Wids,_Op) ->
    io:format("Othello game~n",[]),
    io:format("------------~n",[]),
    io:format("  Put markers by clicking in squares~n",[]),
    io:format("  Change level by clicking on it~n",[]),
    io:format("  Change colour by clicking on it~n",[]),
    io:format("~n",[]),
    GamePid;
but_pressed("Newgame",_ButtId,_User,GamePid,_Shell,Wids,Options) ->
    new_game(GamePid,Wids,Options);
but_pressed([],ButtId,User,GamePid,_Shell,_Wids,_Op) 
					when pid(GamePid),User == player ->
    [C,R] = atom_to_list(ButtId),
    GamePid ! {self(),position,othello_adt:pos(C-96,translate(R-48))},
    GamePid;
but_pressed([],ButtId,_User,GamePid,_Shell,_Wids,_Op) ->
    [C,R] = atom_to_list(ButtId),
    beep(othello_adt:pos(C-96,translate(R-48))),
    GamePid;
but_pressed(Button,ButtId,_User,GamePid,_Shell,_Wids,_Op) ->
    io:format('Not implemented button pressed ~p, ~p!!!~n',[ButtId,Button]),
    GamePid.

menu_selected("Black",_User,_GamePid,Wids,Options) ->
    Op0 = setelement(1,Options,white),
    Op1 = setelement(2,Op0,white),
    write_options(Op1,Wids),
    Op1;
menu_selected("White",_User,_GamePid,Wids,Options) ->
    Op0 = setelement(1,Options,black),
    Op1 = setelement(2,Op0,black),
    write_options(Op1,Wids),
    Op1;
menu_selected("Black (begin)",_User,_GamePid,Wids,Options) ->
    Op0 = setelement(1,Options,white),
    Op1 = setelement(2,Op0,black),
    write_options(Op1,Wids),
    Op1;
menu_selected("White (begin)",_User,_GamePid,Wids,Options) ->
    Op0 = setelement(1,Options,black),
    Op1 = setelement(2,Op0,white),
    write_options(Op1,Wids),
    Op1;
menu_selected("Beginner",_User,_GamePid,Wids,Options) ->
    Op1 = setelement(3,Options,1),
    write_options(Op1,Wids),
    Op1;
menu_selected("Intermediate",_User,_GamePid,Wids,Options) ->
    Op1 = setelement(3,Options,2),
    write_options(Op1,Wids),
    Op1;
menu_selected("Advanced",_User,_GamePid,Wids,Options) ->
    Op1 = setelement(3,Options,3),
    write_options(Op1,Wids),
    Op1;
menu_selected("Expert",_User,_GamePid,Wids,Options) ->
    Op1 = setelement(3,Options,4),
    write_options(Op1,Wids),
    Op1;
menu_selected(What,_User,_GamePid,_Wids,Options) ->
    io:format('Menu item not implemented <~s>~n',[What]),
    Options.

game_msg(Msg,User,GamePid,Shell,Wids,Options) ->
    case Msg of
	{GamePid,new_mark,Pos,Colour} ->
	    new_mark(Pos,Colour),
	    loop(User,GamePid,Shell,Wids,Options);

	{GamePid,illegal_draw,Draw} ->
	    beep(Draw),
	    loop(User,GamePid,Shell,Wids,Options);

	{GamePid,player,Computer,Computer} ->
	    show_player(element(1,Wids),Computer),
	    cursor("watch"),
	    GamePid ! {self(),go_on_play},
	    loop(computer,GamePid,Shell,Wids,Options);

	{GamePid,player,_Computer,Player} ->
	    show_player(element(1,Wids),Player),
	    cursor("top_left_arrow"),
	    GamePid ! {self(),go_on_play},
	    loop(player,GamePid,Shell,Wids,Options);

	{GamePid,omit_draw,Player} ->
	    omit_draw(GamePid,Player),
	    loop(User,GamePid,Shell,Wids,Options);

	{GamePid,score,WhiteRes,BlackRes} ->
	    write_score(Wids,WhiteRes,BlackRes),
	    loop(User,GamePid,Shell,Wids,Options);

	{GamePid,draw,Draw} ->
	    write_draw(Wids,Draw),
	    loop(User,GamePid,Shell,Wids,Options);

	{GamePid,game_over,WhiteRes,BlackRes} ->
	    game_over(WhiteRes,BlackRes),
	    loop(User,GamePid,Shell,Wids,Options);

	What ->
            io:format('game_msg received: ~w~n',[What]),    
	    loop(User,GamePid,Shell,Wids,Options)
    end.
    

new_game(GamePid,Wids,Options) when pid(GamePid) ->
    exit(GamePid,kill),
    new_game(Wids,Options);
new_game(_,Wids,Options) ->
    new_game(Wids,Options).

new_game(_Wids,Options) ->
    label("",lastdraw),
    Computer = element(1,Options),
    Start = element(2,Options),
    Depth = element(3,Options),
    othello:new_game(Computer,Start,Depth,restart).

new_mark(Pos,Colour) ->
    Col = othello_adt:col(Pos),
    Row = othello_adt:row(Pos),
    Name = [Col+96,translate(Row)+48],
    Button = get(Name),
    butbit(Button,Colour).

beep(Draw) ->
    Col = othello_adt:col(Draw),
    Row = othello_adt:row(Draw),
    Name = [Col+96,translate(Row)+48],
    Button = get(Name),
    bell(Button).

show_player(_Status,white) ->
    label("White to draw",todraw);
show_player(_Status,black) ->
    label("Black to draw",todraw).

write_score(_Wids,WhiteRes,BlackRes) ->
    label(integer_to_list(BlackRes),bscore),
    label(integer_to_list(WhiteRes),wscore).

write_draw(_Wids,Draw) ->
    Col = othello_adt:col(Draw),
    Row = othello_adt:row(Draw),
    label(lists:flatten(io_lib:format('{~w,~w}',[Col,Row])), lastdraw).

write_options(Options,Wids) ->
    write_colour(Options,Wids),
    write_level(Options,Wids).

write_colour(Options,Wids) ->
    write_colour(element(1,Options),element(2,Options),Wids).

write_colour(black,white,_Wids) -> label("White (begin)",colour);
write_colour(black,black,_Wids) -> label("White",colour);
write_colour(white,black,_Wids) -> label("Black (begin)",colour);
write_colour(white,white,_Wids) -> label("Black",colour).
    
write_level(Options,_Wids) ->
    case element(3,Options) of
	1 -> label("Beginner",level);
	2 -> label("Intermediate",level);
	3 -> label("Advanced",level);
	4 -> label("Expert",level)
    end.

cursor(_What) ->
    done.
%cursor(What) -> cursor(get(),What).

%cursor([{[C,R],Button}|Buts],What) ->
%    set_widget(Button,"cursor",What),
%    cursor(Buts,What);
%cursor([_|Buts],What) ->
%    cursor(Buts,What);
%cursor([],_) ->
%    true.

translate(1) -> 8;
translate(2) -> 7;
translate(3) -> 6;
translate(4) -> 5;
translate(5) -> 4;
translate(6) -> 3;
translate(7) -> 2;
translate(8) -> 1.

bitmap(grey)  -> bitmap_path("square.bm");
bitmap(black) -> bitmap_path("marker.bm");
bitmap(white) -> bitmap_path("marker.bm").

bitmap_path(Bitmap) ->
    filename:join(path(),Bitmap).

xy_position([[Letter,Digit],_,_]) ->
    LettPos = Letter - 97,
    X = LettPos*60 ,
    Y = (8 - list_to_integer([Digit])) * 60,
    {X+6,Y+6};
xy_position(X) ->
    io:format("xy_position: ~w~n",[{error,X}]).


board() ->
    [["a1",grey,nil],
     ["b1",grey,nil],
     ["c1",grey,nil],
     ["d1",grey,nil],
     ["e1",grey,nil],
     ["f1",grey,nil],
     ["g1",grey,nil],
     ["h1",grey,nil],

     ["a2",grey,nil],
     ["b2",grey,nil],
     ["c2",grey,nil],
     ["d2",grey,nil],
     ["e2",grey,nil],
     ["f2",grey,nil],
     ["g2",grey,nil],
     ["h2",grey,nil],

     ["a3",grey,nil],
     ["b3",grey,nil],
     ["c3",grey,nil],
     ["d3",grey,nil],
     ["e3",grey,nil],
     ["f3",grey,nil],
     ["g3",grey,nil],
     ["h3",grey,nil],

     ["a4",grey,nil],
     ["b4",grey,nil],
     ["c4",grey,nil],
     ["d4",white,nil],
     ["e4",black,nil],
     ["f4",grey,nil],
     ["g4",grey,nil],
     ["h4",grey,nil],

     ["a5",grey,nil],
     ["b5",grey,nil],
     ["c5",grey,nil],
     ["d5",black,nil],
     ["e5",white,nil],
     ["f5",grey,nil],
     ["g5",grey,nil],
     ["h5",grey,nil],

     ["a6",grey,nil],
     ["b6",grey,nil],
     ["c6",grey,nil],
     ["d6",grey,nil],
     ["e6",grey,nil],
     ["f6",grey,nil],
     ["g6",grey,nil],
     ["h6",grey,nil],

     ["a7",grey,nil],
     ["b7",grey,nil],
     ["c7",grey,nil],
     ["d7",grey,nil],
     ["e7",grey,nil],
     ["f7",grey,nil],
     ["g7",grey,nil],
     ["h7",grey,nil],

     ["a8",grey,nil],
     ["b8",grey,nil],
     ["c8",grey,nil],
     ["d8",grey,nil],
     ["e8",grey,nil],
     ["f8",grey,nil],
     ["g8",grey,nil],
     ["h8",grey,nil]].


omit_draw(GamePid,Player) ->
%    %% Find mouse coords first
%    %% This was not possible in gs

    W = 200, H = 100, Root = get(windowroot),
    Box = gs:create(window, Root, [{title,"OMIT"}, {width, W},{height, H}]),
    
    mk_label_c(lists:flatten(io_lib:format('~w has to omit draw !',[Player])),
	     Box, W, 10),

    mk_button_c("Ok", Box, W, H-40, 80, 30),

    gs:config(Box, {map, true}),		%Make win visible

    receive
	{gs,_, click,_,["Ok"]} ->
	    gs:destroy(Box),
	    GamePid ! {self(),continue}
    end.

game_over(WhiteRes,BlackRes) ->
%    %% Find mouse coords first
%    %% This was not possible in gs

    W = 200, H = 160, 
    Root = get(windowroot),
    Box = gs:create(window, Root, [{title,"GAME OVER"}, 
				   {width, W},{height, H}]),
    
    mk_label_c("GAME OVER", Box, W, 10),

    mk_label_c(lists:flatten(io_lib:format('White score: ~w',[WhiteRes])),
	       Box,W,40),
    mk_label_c(lists:flatten(io_lib:format('Black score: ~w',[BlackRes])),
	       Box,W,70),

    mk_button_c("Ok", Box, W, H-40, 80, 30),

    gs:config(Box, {map, true}),		%Make win visible

    receive
	{gs,_, click,_,["Ok"]} ->
	    gs:destroy(Box)
    end.



%% ----------------------------------------------------------------
%% Library functions.
%% ----------------------------------------------------------------

bell(Widget) ->
    %% gs does not support bells,
    Widget.

label(Text,Label) ->
    gs:config(Label,[{label,{text,Text}}]).

%% mk_label in centered version
mk_label_c(Label,Parent,Width,Y) ->
    W = 8*length(Label),
    X = trunc((Width-W)/2),
    gs:create(label,Parent,[{width,W}, {height, 20}, {x,X}, {y,Y}, 
			     {label, {text, Label}}]).




setup_buttons(Shell,X,Y,W,H) ->
    ButBox = gs:create(frame, Shell,[{x,X}, {y,Y},{bg,white},
				     {width,W}, {height,H}]),
    C = gs:create(canvas,ButBox,[{x,X}, {y,Y}, {width, W}, {height, H},
				 {bg,white}]),
    gs:create(line, C, [{coords, [{0,H-1},{W,H-1}]}]),


    mk_button("Quit",ButBox, 10, 10, 70, 20),
    mk_button("Rules",ButBox, 80, 10, 70, 20),
    mk_button("Newgame",ButBox, 150, 10, 70, 20),
    mk_button("Help",ButBox, 220, 10, 70, 20),
%%    mk_button("Level",ButBox, 290, 10, 70, 20),
    
    done.

    



%%----------------------------------------
%% Sets up the middle window w. all the status info in.
%% The labels are given names:
%% bscore, wscore, lastdraw, todraw, level and colour to simplify
%% their frequent setting
%%
setup_status_box(Shell,X,Y,W,H) ->
    F = gs:create(frame, Shell,[{x,X}, {y,Y},
				  {width,W}, {height,H},{bg,white}]),
    C = gs:create(canvas,F,[{x,0}, {y,0}, {width, W}, {height, H},{bg,white}]),
    gs:create(line, C, [{coords, [{0,H-1},{W,H-1}]}]),

    %% Left side
    gs:create(label,F,[{align,w},{x,10}, {y,5}, {width, 100},
		       {label,{text, "Black score:"}},{bg,white}]),
    gs:create(label,bscore,F,[{align,w},{x,110}, {y,5}, {width, 40},
			      {label,{text, "2"}},{bg,white}]),
    gs:create(label,F,[{align,w},{x,10}, {y,35}, {width, 100},
		       {label,{text, "White score:"}},{bg,white}]),
    gs:create(label,wscore,F,[{align,w},{x,110}, {y,35}, {width, 40},
			      {label,{text, "2"}},{bg,white}]),
    gs:create(label,F,[{align,w},{x,10}, {y,65}, {width, 100},
			      {label,{text, "Last draw:"}},{bg,white}]),
    gs:create(label,lastdraw,F,[{align,w},{x,110}, {y,65}, {width, 40},
				{label,{text, ""}},{bg,white}]),
    

    %% Right side
    X2 = trunc(W/2)+10,
    gs:create(label,todraw,F,[{align,w},{x,X2}, {y,5}, {width, 100},
			      {label,{text, "Black to draw:"}},{bg,white}]),

    gs:create(label,F,[{align,w},{x,X2}, {y,35}, {width, 80},
			      {label,{text, "Level:"}},{bg,white}]),
    setup_level_menu(F,X2+80,35),

%%    gs:create(label,level,F,[{align,w},{x,X2+80}, {y,35}, {width, 130},
%%			      {label,{text, "Intermediate"}},{bg,white}]),
    gs:create(label,F,[{align,w},{x,X2}, {y,65}, {width, 80},
			      {label,{text, "Colour:"}},{bg,white}]),
    setup_col_menu(F,X2+80,65),

%%    gs:create(label,colour,F,[{align,w},{x,X2+80}, {y,65}, {width, 120},
%%			      {label,{text, "black (begin)"}},{bg,white}]),
    
    done.


setup_col_menu(P,X,Y) ->
    MB = gs:create(menubutton,colour,P,
		   [{x,X}, {y,Y}, {bw,3},
		    %%{width,W}, {height,H}, 
		    {align,w}, {bg,white},
		    {relief, raised},
		    {activefg,white}, {activebg,black},
		    {label, {text,"Colours"}}]),

    M = gs:create(menu,MB,[]),
    gs:create(menuitem,M,[{label,{text,"Black (begin)"}}]),
    gs:create(menuitem,M,[{label,{text,"Black"}}]),
    gs:create(menuitem,M,[{label,{text,"White (begin)"}}]),
    gs:create(menuitem,M,[{label,{text,"White"}}]),
    done.

setup_level_menu(P,X,Y) ->
    MB = gs:create(menubutton,level,P,
		   [{x,X}, {y,Y},
		    %%{width,W}, {height,H}, 
		    {relief, raised},
		    {activefg,white}, {activebg,black},
		    {align,w}, {bg,white},
		    {label, {text,"Colours"}}]),

    M = gs:create(menu,MB,[]),
    gs:create(menuitem,M,[{label,{text,"Beginner"}}]),
    gs:create(menuitem,M,[{label,{text,"Intermediate"}}]),
    gs:create(menuitem,M,[{label,{text,"Advanced"}}]),
    gs:create(menuitem,M,[{label,{text,"Expert"}}]),
    done.


setup_board(Shell,X,Y,W,H) ->
    F = gs:create(frame, Shell,[{x,X}, {y,Y},
				  {width,W}, {height,H},{bg,white}]),
    display_board(F).


mk_button(Label, Parent, X, Y, W, H) ->
    gs:create(button,Parent,[{width,W}, {height, H}, {x,X}, {y,Y}, 
			     {label, {text, Label}}, {bg,white},
			     {activefg,white}, {activebg,black}]).

%% Centers a button around Width
mk_button_c(Label, Parent, Width, Y, W, H) ->
    X = trunc((Width-W)/2),
    gs:create(button,Parent,[{width,W}, {height, H}, {x,X}, {y,Y}, 
			     {label, {text, Label}}, {bg,white}]).


butbit(Button,Col) ->
    gs:config(Button,[
		      {label,{image,bitmap(Col)}},
		      {fg, Col},
		      {activefg,Col},
		      {label, {image, bitmap(Col)}}]),
    Button.

mk_board_butt(Top,Name,X,Y,Col) ->
    B = gs:create(button,list_to_atom(Name), Top,
		  [{x,X}, {y,Y}, {width,60}, {height,60},
		   {padx,5},{pady,5},
		   {relief,flat},
		   {bg,?BGCOL}, {activebg,?BGCOL}]),
    butbit(B,Col),
    B.
	     


display_board(Top) ->
    Board = board(),
    display_board(Top,Board,1).

display_board(_,_,65) -> true;
display_board(Top,[H|T],Place) ->
    [Name,Colour,_] = H,
    {X,Y} = xy_position(H),
    Button = mk_board_butt(Top,Name,X,Y,Colour),
    %%Button = mk_button("",Name,Top,X,Y,60,60),
    put(Name,Button),
    %%Bitmap = bitmap(Colour),
    %%butbit(Button,Bitmap),
    %%set_widget(Button,"internalWidth","1"),
    %%set_widget(Button,"internalHeight","1"),
    %%borderWidth(2,Button),
    display_board(Top,T,Place+1).
    

