class Tile_Tip
  attr_accessor  :id

  attr_accessor  :empty_tile
  
  def initialize(id,ncolumn , nrow ,empty_flag)
    @id = id
    @board_column = ncolumn
    @board_row = nrow
    @empty_tile = empty_flag
  end  
  
  def dposX
    @id % @board_column
  end
  
  def dposY
    @id / @board_column
  end  

end

class Puzzle15
  attr_accessor  :id
  attr_accessor :ncolumn
  attr_reader  :pic_name
  attr_accessor :nrow
  attr_accessor  :empty_tile
  attr_reader    :count_flag
  attr_reader    :enable_cancel
  attr_accessor  :variable 
  attr_accessor :max_count
  def initialize(id,ncolumn , nrow , pic_name , variable , enable_cancel, count_flag,max_count)
    @id = id
    @ncolumn = ncolumn
    @nrow = nrow
    @enable_cancel = enable_cancel
    @variable = variable
    @pic_name = pic_name
    @count_flag = count_flag
    @max_count = max_count  
  end  
  
  def size
    @ncolumn * @nrow
  end  

  def change_size(ncolumn,nrow)
    @ncolumn = ncolumn
    @nrow = nrow
  end  
  
  def max_count
    @max_count || max_count_formula
  end
  
  def max_count_formula
    a = (@ncolumn + @nrow ) - 2
    return 3 * a * (a + 1)  + 34 * (a - 3) - 60 + 42    
  end
end  

class Scene_Puzzle15 < Scene_MenuBase
  
  def initialize(id)
    @puzzle = $data_puzzle15s[id]
  end   
  #--------------------------------------------------------------------------
  # ● 開始処理
  #--------------------------------------------------------------------------
  def start
    super

    set_init
    create_puzzle_window
    set_Tile_Tips
    even_proposition_random(item_max)
    @puzzle_window.get_board_info(@board)
    @puzzle_window.refresh
  end
  
  #--------------------------------------------------------------------------
  # ● タイルたちの初期設定
  #--------------------------------------------------------------------------
  def set_Tile_Tips
    
    @board = (item_max - 1).times.collect do |i|
      Tile_Tip.new(i,@puzzle.ncolumn , @puzzle.nrow , false)
    end
    @board.push Tile_Tip.new((item_max - 1),@puzzle.ncolumn , @puzzle.nrow , true)
  end    
  #--------------------------------------------------------------------------
  # ● 変数などのセット
  #--------------------------------------------------------------------------
  def set_init
    @list = []
    @count = 0
  end
  #--------------------------------------------------------------------------
  # ● 項目数の取得
  #--------------------------------------------------------------------------
  def item_max
    return @puzzle.size
  end
  #--------------------------------------------------------------------------
  # ● コマンドウィンドウの作成
  #--------------------------------------------------------------------------
  def create_puzzle_window
    
    
    @puzzle_window = Window_Puzzle15.new(0, 0, Graphics.width, Graphics. height, @puzzle)
#~     @puzzle_window.get_board_info(@board)
    @puzzle_window.set_handler(:ok,     method(:on_category_ok))
    @puzzle_window.set_handler(:cancel, method(:return_scene)) if @puzzle.enable_cancel

    @puzzle_window.get_max_count(max_count)
    
    @puzzle_window.refresh
    @message_window = Window_Message.new

  end  
  #--------------------------------------------------------------------------
  # ● フレーム更新（ウェイト用）
  #--------------------------------------------------------------------------
  def update_for_wait
    update_basic
  end
  #--------------------------------------------------------------------------
  # ● ウェイト
  #--------------------------------------------------------------------------
  def wait(duration)
    duration.times {|i| update_for_wait if i < duration / 2 || !show_fast? }
  end
  #--------------------------------------------------------------------------
  # ● 早送り判定
  #--------------------------------------------------------------------------
  def show_fast?
    Input.press?(:A) || Input.press?(:C)
  end
  #--------------------------------------------------------------------------
  # ● ウェイト（早送り無効）
  #--------------------------------------------------------------------------
  def abs_wait(duration)
    duration.times {|i| update_for_wait }
  end
  #--------------------------------------------------------------------------
  # ● 短時間ウェイト（早送り無効）
  #--------------------------------------------------------------------------
  def abs_wait_short
    abs_wait(15)
  end
  #--------------------------------------------------------------------------
  # ● メッセージ表示が終わるまでウェイト
  #--------------------------------------------------------------------------
  def wait_for_message
    @message_window.update
    update_for_wait while $game_message.visible
  end
  #--------------------------------------------------------------------------
  # ● アニメーション表示が終わるまでウェイト
  #--------------------------------------------------------------------------
  def wait_for_animation
    update_for_wait
    update_for_wait while @spriteset.animation?
  end
  #--------------------------------------------------------------------------
  # ● エフェクト実行が終わるまでウェイト
  #--------------------------------------------------------------------------
  def wait_for_effect
    update_for_wait
    update_for_wait while @spriteset.effect?
  end
  # ●   
  def on_category_ok
    cell = @puzzle_window.index
    acell = cell_adjacent_empty?(cell)
    if acell >= 0
      Sound.play_ok
      @count += 1
      @puzzle_window.get_count(@count)
      transposition(cell ,acell)
      
      @puzzle_window.get_board_info(@board)
      if game_end?
        @puzzle_window.refresh
        @puzzle_window.game_end
        @puzzle_window.index = -1

        result_game
        $game_variables[@puzzle.variable] = @result_game 
        wait(20)
        @puzzle_window.refresh
        while !Input.trigger?(:C)
          update_for_wait
        end    
        if @count_flag
          result_message_txt
          reversi_message(2,0)

          wait_for_message    
        end  
        return_scene
      else
        @puzzle_window.refresh
        @puzzle_window.activate   

      end  
      
    else
      Sound.play_buzzer
      @puzzle_window.activate
    end    
  end
  #--------------------------------------------------------------------------
  # ● フレーム更新（ウェイト用）
  #--------------------------------------------------------------------------
  def update_for_wait
    update_basic
  end  
  #--------------------------------------------------------------------------
  # ● ウェイト
  #--------------------------------------------------------------------------
  def wait(duration)
    duration.times {|i| update_for_wait if i < duration / 2 || !show_fast? }
  end  
  #--------------------------------------------------------------------------
  # ● 文章の表示
  #--------------------------------------------------------------------------
  def reversi_message(position,index,type = 0)

#~     $game_message.face_name = face_name
#~     $game_message.face_index = face_index

    $game_message.position = position
    if type == 0      # 文章データ
      $game_message.add(@list[index])
    end
  end    
  def result_message_txt
    numw = max_count - @count
    resulttxt = sprintf("残りカウント : %s ",numw )
    
    @list[0]  = resulttxt   
  end  
  
  def result_game
    numw = max_count - @count
    @result_game = numw >= 0 ? 1 : 2
    @result_game = 1 unless @count_flag
    
  end  
    #--------------------------------------------------------------------------
  # ● 目標カウントの計算
  #--------------------------------------------------------------------------
  def max_count
    return @puzzle.max_count
  end    
  # ● ゲーム終了の判定
  def game_end?
    @board.each_with_index do |tile , index|
      return false unless tile.id == index 
    end  
    return true
  end    
  # ● 指定したセルidがボードの内部にあるか
  def in_board?(cell)
    return false if cell < 0
    return false if cell > @board.size - 1
    return true
  end  
  # ● 隣接するセルid direction 1から9で指定
  def get_cell_adjacent(cell_id,direction)
    dy = (direction - 1) / 3 - 1
    dx = (direction - 1) % 3 - 1
    return cell_id + @puzzle.ncolumn * dy + dx
  end    
  
  # ● 空タイルに隣接するセルであるか 真ならば空セルのidを返す　偽ならば-1
  def cell_adjacent_empty?(cell_id)
    directions_cell_movable(cell_id).each do |d|
      acell = get_cell_adjacent(cell_id,d)
      if in_board?(acell)
        return acell if @board[acell].empty_tile
      end  
    end  
    return - 1
  end    
  
  # ● セルが移動可能な方向を返す
  def directions_cell_movable(cell_id)
    array = []
    array.push(4) if cell_id % @puzzle.ncolumn != 0
    array.push(6) if cell_id % @puzzle.ncolumn != @puzzle.ncolumn - 1
    array.push(2) if cell_id / @puzzle.ncolumn != 0
    array.push(8) if cell_id / @puzzle.ncolumn != @puzzle.nrow - 1
    return array
  end      
  
  # ● 互換
  def transposition(cell1 ,cell2)
    tile1 = @board[cell1]
    tile2 = @board[cell2]
    @board[cell1] = tile2
    @board[cell2] = tile1    
  end  
  # ● ランダムな互換
  def transposition_random
    rnd = rand(item_max - 1)
    rnd2 = rand(item_max - 1)
    while rnd == rnd2
      rnd2 = rand(item_max - 1 )
    end
    transposition(rnd ,rnd2)  
  end    
  # ● ランダムな偶置換
  def even_proposition_random(count)
    count.times do
      transposition_random
      transposition_random
    end
  end  
  
end  

class Window_Puzzle15 < Window_Selectable


  
  #--------------------------------------------------------------------------
  # ● オブジェクト初期化
  #-------------------------------------------------------------------------
  def initialize(x, y, width, height , puzzle)
    @puzzle = puzzle
    @ncolumn = @puzzle.ncolumn
    @nrow = @puzzle.nrow
    @bwidth = $data_puzzle15_board_width
    @bheight = $data_puzzle15_board_height
    super(x, y, width, height )
    @index = 0
    @handler = {}
    @cursor_fix = false
    @cursor_all = false
    @count = 0
    update_padding
    activate
    set_bit_map
    make_bitmap_curosor
    @empty_cell = item_max - 1
    @game_end = false
    
  end
  
  def set_bit_map
    @bitmap = Cache.picture(@puzzle.pic_name)
    color = Color.new(0,0,0)
    @empty_bitmap = Bitmap.new(item_width , item_height)
    rect= @empty_bitmap.rect
    @empty_bitmap.fill_rect(rect, color) 

  end  
  
  def get_count(count)
    @count = count
  end  
  def get_max_count(count)
    @max_count = count
  end  
  #--------------------------------------------------------------------------
  # ● 桁数の取得
  #--------------------------------------------------------------------------
  def col_max
    return @ncolumn
  end  
  

  #--------------------------------------------------------------------------
  # ● 横に項目が並ぶときの空白の幅を取得
  #--------------------------------------------------------------------------
  def spacing
    return 0
  end
  #--------------------------------------------------------------------------
  # ● 項目数の取得
  #--------------------------------------------------------------------------
  def item_max
    return @puzzle.size
  end
  #--------------------------------------------------------------------------
  # ● 項目の幅を取得
  #--------------------------------------------------------------------------
  def item_width
    return @bwidth / @ncolumn
  end
  #--------------------------------------------------------------------------
  # ● 項目の高さを取得
  #--------------------------------------------------------------------------
  def item_height
    return @bheight / @nrow
  end
  #--------------------------------------------------------------------------
  # ● 項目の描画
  #--------------------------------------------------------------------------
  def draw_item(index)
    
    if @board
      set_bit_map
      @board.size.times {|i| draw_tile(i)}
#~       @bitmap.dispose
    end
    
  end
  #現在の盤情報の取得
  def get_board_info(board)
    @board = board.dup
  end  

  
  #--------------------------------------------------------------------------
  # ● タイルの描画
  #--------------------------------------------------------------------------
  def draw_tile(index ,x = 0 ,y = 0)
    return unless @board
    tx = index % @ncolumn 
    ty = index / @ncolumn
    tile_tip = @board[index]
    if tile_tip.empty_tile && !@game_end
      @empty_bitmap
      rect = @empty_bitmap.rect
      contents.blt(tx * item_width , ty * item_height ,@empty_bitmap, rect, 255)      
    else  
      rect = Rect.new(item_width * tile_tip.dposX , item_height * tile_tip.dposY , item_width, item_height)
      contents.blt(x + tx * item_width , y + ty * item_height ,@bitmap, rect, 255)
    end
  end  
  #--------------------------------------------------------------------------
  # ● 最後のタイルの描画
  #--------------------------------------------------------------------------
  def draw_last_tile
    return draw_last_tile_end if @game_end
    tx = (item_max - 1) % @ncolumn
    ty = (item_max - 1) / @ncolumn 
    return unless @board

    rect = Rect.new(item_width * (@ncolumn - 1) , item_height * (@nrow - 1) , item_width, item_height)
    contents.blt((tx + 1) * item_width , ty * item_height ,@bitmap, rect, 255)
    
  end 
  #--------------------------------------------------------------------------
  # ● ゲーム終了時の最後のタイルの描画
  #--------------------------------------------------------------------------
  def draw_last_tile_end
    
    tx = (item_max - 1) % @ncolumn
    ty = (item_max - 1) / @ncolumn
    return unless @board
    rect = @empty_bitmap.rect
    contents.blt((tx + 1) * item_width , ty * item_height ,@empty_bitmap, rect, 255)
    
  end   
  
  # ● ゲームの終了 
  def game_end
    @game_end = true
  end  
  #--------------------------------------------------------------------------
  # ● フレームの描画
  #--------------------------------------------------------------------------
  def draw_frame
    color = Color.new(0,0,0)
    bitmap = Bitmap.new(@ncolumn * item_width , @nrow * item_height) 
    for i in 0..@ncolumn * item_width 
      for j in 0..@nrow * item_height 
        bitmap.set_pixel(i,j,color) if i % item_width == 0 || j % item_height == 0
      end
    end  
    
    bitmap.set_pixel(x, y, color) 

    rect = bitmap.rect
    contents.blt(0 , 0 ,bitmap, rect, 255)
    
  end   
  # ● カーソルのBitmapの作成
  
  def make_bitmap_curosor
    color = Color.new(255,0,0)
    @cursor_bitmap = Bitmap.new( @ncolumn * item_width , @nrow * item_height) 
    for i in 0..@ncolumn * item_width
      for j in 0..@nrow * item_height
        if draw_board_curosr_line_thick( i , j)
          @cursor_bitmap.set_pixel(i, j, color) 
        end  
      end
    end         
  end  
  #--------------------------------------------------------------------------
  # ● カーソルの描画
  #--------------------------------------------------------------------------
  def draw_board_curosor
    rect = @cursor_bitmap.rect
    cx = index % @ncolumn 
    cy = index / @ncolumn
    contents.blt( cx * item_width  , cy * item_height, @cursor_bitmap, rect, 255)
  end 
  #--------------------------------------------------------------------------
  # ● カウントの描画
  #--------------------------------------------------------------------------
  def draw_count(x,y)
    left_count = @max_count - @count
    
    change_color(system_color)
    draw_text(x , y, 100 , line_height, "カウント", 0) 
    change_color(normal_color)
    draw_text(x + 100, y , 100 , line_height, @count, 2)
    change_color(system_color)
    draw_text(x , y + line_height , 100 , line_height, "残りカウント", 0) 
    change_color(count_color(left_count))
    draw_text(x + 100, y + line_height , 100 , line_height, left_count, 2)    
  end   
  #--------------------------------------------------------------------------
  # ● カウントの文字色を取得
  #--------------------------------------------------------------------------
  def count_color(lcount)
    return knockout_color if lcount < 0
    return crisis_color if lcount < @max_count / 5
    return normal_color
  end    
  #--------------------------------------------------------------------------
  # ● 座標の描画
  #--------------------------------------------------------------------------
  def draw_coordinate(x,y)
    return unless @board
    
    tile = @board[index]
    change_color(system_color)
    draw_text(x , y, 100 , line_height, "元の座標", 0) 
    coord = sprintf("%s , %s" , tile.id % @ncolumn , tile.id / @ncolumn )
    change_color(normal_color)
    draw_text(x + 100 , y, 100 , line_height, coord, 2)    
  
  end     
  #--------------------------------------------------------------------------
  # ● 太目のカーソルのライン
  #--------------------------------------------------------------------------
  def draw_board_curosr_line_thick(x,y)
    [- 1 , 0 ,1].each do |i|
      [- 1 , 0 ,1].each do |j|
        return true if draw_board_curosr_line(x + i,y + j)
      end  
      
    end  
    return false
  end      
  #--------------------------------------------------------------------------
  # ● カーソルのライン
  #--------------------------------------------------------------------------
  def draw_board_curosr_line(x,y)
    if x == 0 || x ==  item_width
      return y >= 0  && y <=  item_height
    end  
    if y == 0 || y ==  item_height
      return x >= 0  && x <= item_width
    end  
    return false
  end    
  #--------------------------------------------------------------------------
  # ● リフレッシュ
  #--------------------------------------------------------------------------
  def refresh
    contents.clear
#~     draw_board
    draw_all_items
    draw_frame unless @game_end
    draw_board_curosor unless @game_end
    draw_coordinate(320,0)
    draw_count(320 , line_height * 2) if @puzzle.count_flag
    draw_last_tile
#~     draw_players_info
#~     draw_board_curosr
  end  
  #--------------------------------------------------------------------------
  # ● カーソルを下に移動
  #--------------------------------------------------------------------------
  def cursor_down(wrap = false)
    if index < item_max - col_max || (wrap && col_max == 1)
      select((index + col_max) % item_max)
    end
    refresh
  end
  #--------------------------------------------------------------------------
  # ● カーソルを上に移動
  #--------------------------------------------------------------------------
  def cursor_up(wrap = false)
    if index >= col_max || (wrap && col_max == 1)
      select((index - col_max + item_max) % item_max)
    end
    refresh
  end
  #--------------------------------------------------------------------------
  # ● カーソルを右に移動
  #--------------------------------------------------------------------------
  def cursor_right(wrap = false)
    if col_max >= 2 && (index < item_max - 1 || (wrap && horizontal?))
      select((index + 1) % item_max)
    end
    refresh
  end
  #--------------------------------------------------------------------------
  # ● カーソルを左に移動
  #--------------------------------------------------------------------------
  def cursor_left(wrap = false)
    if col_max >= 2 && (index > 0 || (wrap && horizontal?))
      select((index - 1 + item_max) % item_max)
    end
    refresh
  end
   
end  

