#  -*- indent-tabs-mode: nil -*-
class CanvasSampleFifteen < Gtk::VBox
  PIECE_SIZE = 50

  def test_win
    0.upto(14) do |i|
      if @board[i].nil? || @board[i].num != i
        return
      end
    end
    #    dialog = Gnome::MessageBox.new("You stud, you win!", Gnome::MessageBox::INFO, "OK")
    #    dialog.set_modal(true)
    #    dialog.run_and_close()
  end

  def piece_event(item, event)
    case event.event_type
    when Gdk::Event::ENTER_NOTIFY
      item.text.set({:fill_color => "white"})
    when Gdk::Event::LEAVE_NOTIFY
      item.text.set({:fill_color => "black"})
    when Gdk::Event::BUTTON_PRESS
      y = item.pos / 4
      x = item.pos % 4

      move = true

      if (y > 0) && @board[(y - 1) * 4 + x].nil?
        dx = 0.0
        dy = -1.0
        y -= 1
      elsif (y < 3) && @board[(y + 1) * 4 + x].nil?
        dx = 0.0
        dy = 1.0
        y += 1
      elsif (x > 0) && @board[y * 4 + x - 1].nil?
        dx = -1.0
        dy = 0.0
        x -= 1
      elsif (x < 3) && @board[y * 4 + x + 1].nil?
        dx = 1.0
        dy = 0.0
        x += 1
      else
        move = false
      end

      if move
        newpos = y * 4 + x
        @board[item.pos] = nil
        @board[newpos] = item
        item.pos = newpos
        item.move(dx * PIECE_SIZE, dy * PIECE_SIZE)
        test_win()
      end
    end
  end

  SCRAMBLE_MOVES = 32
  def scramble
    srand()

    # First, find the blank spot
    pos = 0
    0.upto(15) do |i|
      if @board[i].nil?
        pos = i
        break
      end
    end

    # "Move the blank spot" around in order to scramble the pieces

    0.upto(SCRAMBLE_MOVES) do
      dir = rand(4).to_i

      x = y = 0

      if (dir == 0) && (pos > 3) # up
        y = -1
      elsif (dir == 1) && (pos < 12) # down
        y = 1
      elsif (dir == 2) && ((pos % 4) != 0) # left
        x = -1
      elsif (dir == 3) && ((pos % 4) != 3) # right
        x = 1
      else
        retry
      end

      oldpos = pos + y * 4 + x;
      @board[pos] = @board[oldpos];
      @board[oldpos] = nil
      @board[pos].pos = pos
      @board[pos].move(-x * PIECE_SIZE, -y * PIECE_SIZE)
      @canvas.update_now()
      pos = oldpos
    end
  end

  def initialize
    super(false, 4)
    border_width = 4
    show()

    alignment = Gtk::Alignment.new(0.5, 0.5, 0.0, 0.0)
    pack_start(alignment, true, true, 0)
    alignment.show()

    frame = Gtk::Frame.new
    frame.set_shadow_type(Gtk::SHADOW_IN);
    alignment.add(frame)
    frame.show()

    # Create the canvas and board

    @canvas = Gnome::Canvas.new()
    @canvas.set_size_request(PIECE_SIZE * 4 + 1, PIECE_SIZE * 4 + 1)
    @canvas.set_scroll_region(0, 0, PIECE_SIZE * 4 + 1, PIECE_SIZE * 4 + 1)
    frame.add(@canvas)
    @canvas.show()

    @board = Array.new(16)

    0.upto(14) do |i|
      @board[i] = Piece.new(@canvas.root, {})
      @board[i].setup(self, i)
    end
    @board[15] = nil;

    # Scramble button
    button = Gtk::Button.new("Scramble")
    pack_start(button, false, false, 0)
    button.signal_connect("clicked") do |button|
      scramble()
    end
    button.show()
  end

  class Piece < Gnome::CanvasGroup
    attr_reader :text, :num, :pos
    attr_writer :pos

    def initialize(*arg)
      super(*arg)
    end

    def setup(app, i)
      y = i / 4
      x = i % 4

      self.set({:x => x * PIECE_SIZE,
                 :y => y * PIECE_SIZE})
      Gnome::CanvasRect.new(self,
                            {:x1 => 0.0,
                              :y1 => 0.0,
                              :x2 => PIECE_SIZE,
                              :y2 => PIECE_SIZE,
                              :fill_color => get_piece_color(x, y),
                              :outline_color => "black",
                              :width_pixels => 0})
      
      @text = Gnome::CanvasText.new(self,
                                    {:text => i.to_s,
                                      :x => PIECE_SIZE / 2.0,
                                      :y => PIECE_SIZE / 2.0,
                                      :font => "Sans bold 24",
                                      :anchor => Gtk::ANCHOR_CENTER,
                                      :fill_color => "black"})
      @num = i
      @pos = i
      self.signal_connect("event") do |item, event|
        app.piece_event(item, event)
      end
    end

    private
    def get_piece_color(x, y)
      r = ((4 - x) * 255) / 4;
      g = ((4 - y) * 255) / 4;
      b = 128;
      return sprintf("#%02x%02x%02x", r, g, b)
    end

  end
end
