require 'monitor'

module Hako
  class TkHako
    def initialize(parent=nil, unit_size=64)
      @w = 4
      @h = 5
      @unit_size = unit_size
      @house = TkFrame.new(parent,
			   'width' => @unit_size * @w,
			   'height' => @unit_size * @h).pack
      @chips = {}
    end
    
    def add_chip(chip)
      widget = TkLabel.new(@house,
			   'text' => chip.name,
			   'relief' => 'raised')
      @chips[chip] = widget

      widget.bind('B1-Motion', proc{|x, y| do_motion(x, y, chip)}, "%x %y")
    end

    def do_motion(x, y, chip)
      if x >= @unit_size * chip.w
	dx = 1
      elsif x < 0
	dx = -1
      else
	dx = 0
      end

      if y >= @unit_size * chip.h
	dy = 1
      elsif y < 0
	dy = -1
      else
	dy = 0
      end

      if (dx != 0)
	dy = 0
      end

      return if (dx == 0) and (dy == 0)

      chip.try_move(dx, dy)
    end

    def moveto_chip(x, y, chip)
      widget = @chips[chip]
      widget.place('x' => @unit_size * x,
		   'y' => @unit_size * y,
		   'width' => @unit_size * chip.w,
		   'height' => @unit_size * chip.h)
    end
  end

  class TableHako
    include MonitorMixin
    def initialize(div)
      super()
      @w = 4
      @h = 5
      @chips = {}
      @arrow_icon = { 
	[0, 1] => 'v', 
	[0, -1] => '^', 
	[1, 0] => '&gt;', 
	[-1, 0] => '&lt;'
      }
      @color = {
	"Father" => "#ccccff",
	"Daughter" => "#ffcccc",
	"Mother" => "#ffdddd",
	"Boy" => "#ffccff",
	"Servant" => "#ccffff",
	"Manager" => "#ccffcc",
	nil => "#ffffff"
      }
      @div = div
      @nil_cell = chip_cell(nil)
    end

    def add_chip(chip)
      @chips[chip.id] = chip
    end

    def do_move(chip_id, dx, dy)
      chip = @chips[chip_id]
      return unless chip
      chip.try_move(dx, dy)
    end

    def moveto_chip(x, y, chip)
      synchronize { @map = nil }
    end

    def view(context)
      synchronize do
	@map = make_map unless @map

	s = '<table align="center" border="0"><tr><td></td>' + ('<td width="80"></td>' * (@w)) + '<td></td></tr>'
	@h.times do |y|
	  s << "<tr><td height='80'>#{@nil_cell}</td>"
	  @w.times do |x|
	    chip = @map[[x, y]]
	    s << (chip ? chip_cell(chip, context) : @nil_cell)
	  end
	  s << "<td>#{@nil_cell}</td></tr>"
	end
	s << ('<tr>' + ('<td></td>' * (@w + 2)) + '</tr></table>')
      end
    end

    private
    def chip_cell(chip, context=nil)
      return '' if chip == true

      if chip
	w = chip.w
	h = chip.h
	name = chip.name
	left = arrow(chip, -1, 0, context)
	right = arrow(chip, 1, 0, context)
	up = arrow(chip, 0, -1, context)
	down = arrow(chip, 0, 1, context)
	bg = @color[name]
      else
	w = h = 1
	left = right = up = down = "กก"
	name = "กกกก"
	bg = @color[nil]
      end

      "<th align='center' colspan='#{w}' rowspan='#{h}' bgcolor='#{bg}'>" +
	"<table border='0' cellpadding='0' cellspacing='0'>" +
	"<tr><th>#{up}</th></tr>" +
	"<tr><th>#{left}&nbsp;#{name}&nbsp;#{right}</th></tr>"+
	"<tr><th>#{down}</th></tr></table>" +
	"</th>"
    end

    def arrow(chip, dx ,dy, context)
      if chip && chip.move?(dx, dy)
	@div.a('move', {'chip'=>chip.id, 'dx' => dx, 'dy' => dy}, context) + 
	  @arrow_icon[[dx, dy]] + "</a>"
      else
	"<font color='#ffffff'>#{@arrow_icon[[dx, dy]]}</font>"
      end
    end

    def make_map
      map = {}
      @chips.values.each do |chip|
	area = chip.area
	map[area.shift] = chip
	area.each do |pos| 
	  map[pos] = true
	end
      end
      map
    end
  end

  class House
    def initialize(widget)
      @w = 4
      @h = 5
      @widget = widget
      @chips = []
      self
    end
    attr :widget
    attr :unit_size
    attr :chips

    def enter(chip)
      @chips.push(chip)
    end

    def wall
      hash = {}
      (-1..@w).each do |i|
	hash[[i, -1]] = true
	hash[[i, @h]] = true
      end
      (-1..@h).each do |i|
	hash[[-1, i]] = true
	hash[[@w, i]] = true
      end
      hash
    end

    def move?(chip, dx, dy)
      field = self.wall
      @chips.each do |c|
	unless c == chip
	  c.area.each do |a|
	    field[a] = chip
	  end
	end
      end

      chip.area(dx, dy).each do |a|
	return false if field[a]
      end
      return true
    end
  end

  class Chip
    def initialize(house, name, x, y, w=1, h=1)
      @name = name
      @w = w
      @h = h
      
      @house = house
      
      house.enter(self)
      house.widget.add_chip(self)
      moveto(x, y)
    end
    attr_reader :name, :w, :h, :x, :y

    def area(dx=0, dy=0)
      v = []
      for i in (1..@h)
	for j in (1..@w)
	  v.push([dx + @x + j - 1, dy + @y + i - 1])
	end
      end
      v
    end

    def moveto(x, y)
      @x = x
      @y = y
      @house.widget.moveto_chip(x, y, self)
    end

    def move(dx, dy)
      x = @x + dx
      y = @y + dy
      moveto(x, y)
    end
  
    def do_motion(x, y)
      try_move(dx, dy)
    end

    def move?(dx, dy)
      @house.move?(self, dx, dy)
    end

    def try_move(dx, dy)
      if move?(dx, dy)
	move(dx, dy)
      end
    end
  end

  class Game
    def initialize(widget)
      house = House.new(widget)

      @she = Chip.new(house, 'Daughter', 1, 0, 2, 2)
      father = Chip.new(house, "Father", 0, 0, 1, 2)
      mother = Chip.new(house, "Mother", 3, 0, 1, 2)
      kozou = []
      (0..3).each do |i|
	kozou.push Chip.new(house, "Boy", i, 2)
      end
      genan = []
      (0..1).each do |i|
	genan.push Chip.new(house, "Servant", i * 3, 3, 1, 2)
      end
      bantou = Chip.new(house, "Manager", 1, 3, 2, 1)
    end

    def finish?
      @she.x == 1 && @she.y == 3
    end
  end
end

if __FILE__ == $0

  def tk_main
    require 'tk'
    game = Hako::Game.new(Hako::TkHako.new(nil, 64))
    Tk.mainloop
  end

  tk_main
end

