open Safestd
open Safeapplets
open Safetk
open Safetkanim

open Tk
open Tkanim
open Viewers

open Capabilities
module Provide = struct
  let capabilities = Capabilities.get()
  end

module Net = Safeapplets.Retrieval(Provide)
open Net


let shuffle l =
  let v = Array.of_list l in
  for i = Array.length v - 1 downto 2 do
    let j = Random.int (i - 1) in
      let el = v.(i) in
      	v.(i) <- v.(j);
      	v.(j) <- el
    done;
  Array.to_list v


let make_tiles img nx ny =
  let w = Imagephoto.width img
  and h = Imagephoto.height img in
  let tx = w / nx  (* X size of tiles *)
  and ty = h / ny  (* Y size of tiles *)
  in
   if tx <= 5 or ty <=5 then failwith "Tiles are too small";
  let tiles = ref [] in
    (* Create tiles from picture *)
    for i = 0 to nx - 1 do
     for j = 0 to ny - 1 do
       let tile = Imagephoto.create [Width (Pixels tx); Height (Pixels ty)] in
	Imagephoto.copy tile img [ImgFrom(i * tx, j * ty, (i+1)*tx, (j+1)*ty)];
	tiles := tile :: !tiles
       done
    done;
    (tx,ty, shuffle (List.tl !tiles))

let init c nx ny (tx, ty, tiles) =
  let board = Array.create_matrix nx ny (Id 0) in
  let blank = Imagephoto.create [Width (Pixels tx); Height (Pixels ty)] in
    Imagephoto.blank blank;
  let blankitem =
    Canvas.create_image c 
               (Pixels ((nx - 1) * tx)) (Pixels ((ny - 1) * ty))
       	       [ImagePhoto blank; Anchor NW; Tags [Tag "tile"]]  in

  let iblank = ref (nx - 1)
  and jblank = ref (ny - 1) in

    (* Fill the board *)
    let l = ref tiles in
      for i = 0 to nx - 1 do
      	for j = 0 to ny - 1 do
	  match !l with
	    [] -> (* Last one is missing *)
	      board.(i).(j) <- blankitem
	  | x::rest ->
	      board.(i).(j) <-
	      Canvas.create_image c (Pixels (i * tx)) (Pixels (j * ty))
		[ImagePhoto x; Anchor NW; Tags [Tag "tile"]];
	      l := rest
        done
      done;

  let move_tile tile i j =
    Canvas.coords_set c tile [Pixels (i * tx); Pixels (j * ty)];
    board.(i).(j) <- tile
  in
  let play ei =
    let x = ei.ev_MouseX / tx
    and y = ei.ev_MouseY / ty in
      if x = !iblank then
      	if y > !jblank then begin
	 for j = !jblank + 1 to y do
	   move_tile board.(x).(j) x (j - 1)
	   done;
	 move_tile blankitem x y;
	 iblank := x; jblank := y
	 end  
        else if y < !jblank then begin
	 for j = !jblank - 1 downto y do
	   move_tile board.(x).(j) x (j + 1)
	   done;
	 move_tile blankitem x y;
	 iblank := x; jblank := y
	 end  
	else ()
      else if y = !jblank then
      	if x > !iblank then begin
	 for i = !iblank + 1 to x do
	   move_tile board.(i).(y) (i - 1) y
	   done;
	 move_tile blankitem x y;
	 iblank := x; jblank := y
	 end  
        else if x < !iblank then begin
	 for i = !iblank - 1 downto x do
	   move_tile board.(i).(y) (i + 1) y
	   done;
	 move_tile blankitem x y;
	 iblank := x; jblank := y
	 end  
	else ()
      else ()
  in
   Canvas.bind c (Tag "tile") [[], ButtonPressDetail 1]
     (BindSet ([Ev_MouseX; Ev_MouseY], play));
   bind c [[], Destroy]
     (BindSet ([], (fun _ -> List.iter Imagephoto.delete tiles;
                             Imagephoto.delete blank)))


let f fw ctx =
 let nx = try int_of_string (List.assoc "x" ctx.viewer_params)
       	  with _ -> failwith "I need a PARAM X (integer)"
 and ny = try int_of_string (List.assoc "y" ctx.viewer_params)
       	  with _ -> failwith "I need a PARAM Y (integer)"
 and url = try List.assoc "url" ctx.viewer_params
       	  with _ -> failwith "I need a PARAM URL (URL)"
 in
     get_image
       (Lexurl.make url)
       (function
       	 Still (ImagePhoto img) ->
	   let w = Imagephoto.width img
	   and h = Imagephoto.height img in
	   let c = Canvas.create fw [Width (Pixels w); Height (Pixels h)] in
	      ctx.viewer_log "Initialising taquin";
      	      init c nx ny (make_tiles img nx ny);
	      ctx.viewer_log "Enjoy";
	      pack [c][]
       | _ -> failwith "I need an ImagePhoto")

let _ = Applets.register  "f" f
