class GnuGoError < StandardError
end

class GnuGo < GoWin

  include Sgf_Info

  PUT   = 11
  GET   = 12
  PASS  = 13
  SCORE = 14
  UNDO  = 15

  CONFIG= 21
  MOVE  = 22
  INFO  = 23
  HANDICAP = 24
  KOMI  = 25

  def initialize(file=nil)

    @gg_player_white= Defaults.get_default_value('gg_player_white')
    @gg_player_black=Defaults.get_default_value('gg_player_black')


    super()
    @top.help_add('command','label'=>'Playing with GnuGo','command'=>proc { TkHtml.new("file://#{$current_path}/../help/gnugo.html") })


    @file_menu=Menu.new
    @file_menu.add('command', 'label' => 'New game','command' => proc {get_size})
    @file_menu.add('command', 'label' => 'Open','command' => proc {open_file})
    @file_menu.add('command', 'label' => 'Save','command' => proc {save_file})
    @file_menu.add('separator')
    @file_menu.add('command', 'label' => 'Edit','command' => proc { open_editor})
    @file_menu.add('command', 'label' => 'Score','command' => proc {score_game })
    @file_menu.add('command', 'label' => 'Info','command' => proc {open_sgf_info(@root) })
    @file_menu.add('separator')
    @file_menu.add('command', 'label' => 'Quit','command' => proc { quit})

    @gtp_menu= Menu.new
    @gtp_menu.add('command', 'label' => 'Estimate score','command' => proc {send(INFO,"estimate_score")})
    @gtp_menu.add('command', 'label' => 'Suggest black move','command' => proc {send(INFO,"genmove_black"); send(INFO,"undo")})
    @gtp_menu.add('command', 'label' => 'Suggest white move','command' => proc {send(INFO,"genmove_white"); send(INFO,"undo")})


=begin
    @game_menu=Menu.new

    wm= Menu.new
    wm.add('radiobutton','label'=> 'Human','variable'=>@gg_player_white,'value'=>'Human','command'=>proc {get_players_defs})
    wm.add('radiobutton','label'=> 'GnuGo','variable'=>@gg_player_white,'value'=>'GnuGo','command'=>proc {get_players_defs})
    @game_menu.add('cascade','menu' => wm , 'label' => 'White')


    bm= Menu.new
    bm.add('radiobutton','label'=> 'Human','variable'=>@gg_player_black,'value'=>'Human','command'=>proc {get_players_defs})
    bm.add('radiobutton','label'=> 'GnuGo','variable'=>@gg_player_black,'value'=>'GnuGo','command'=>proc {get_players_defs})
    @game_menu.add('cascade','menu' => bm , 'label' => 'Black')

=end

    @top.menubar.add('cascade','menu' => @file_menu, 'label' => 'File')
    @top.menubar.add('cascade','menu' => @gtp_menu, 'label' => 'GTP')
#@top.menubar.add('cascade','menu' => @game_menu, 'label' => 'Game')


    @edit_sgf_info=true

    @gg_cmd= Defaults.get_default_value('gnugo_cmd')


    open_file(file) if file
  end

  def open_editor
    GoEditor.new(@collection)
  end

  def open_win
    open_win_basic
    open_win_info


    CmdLine.new(@f2, proc { |msg| @talk.putline(msg+"\n",'user_cmd'); send(INFO,msg)}).pack('fill' =>'x','expand'=>'no')

    @cmd2= VTexturedPane.new(@f1,Defaults::DEFS['CommonBackground'],10,5,1,6).pack('side'=>'right','fill'=> 'both','expand'=>'yes')
    opts=TkDefaults::GUI['GnuGo.button.undo'].update( {'command'=> proc {send_undo}})
    TkButton.new(@cmd2,opts).pack('padx'=>5,'pady'=>5)

    opts=TkDefaults::GUI['GnuGo.button.pass'].update( {'command'=> proc {send_pass}})
    TkButton.new(@cmd2,opts).pack('padx'=>5,'pady'=>5)

    opts=TkDefaults::GUI['GnuGo.button.done'].update( {'command'=> proc {send_score}})
    TkButton.new(@cmd2,opts).pack('padx'=>5,'pady'=>5)

    fr= TkFrame.new(@cmd2,TkDefaults::GUI['GnuGo.choose.white'])
    TkLabel.new(fr,'image'=>Defaults::WStones[20]).pack('side'=>'left')
    b=TkMenubutton.new(fr,'textvariable'=>@gg_player_white,'indicatoron'=>'true').pack('side'=>'left')
    m=TkMenu.new(b,'tearoff'=>'false')
    m.add('command','label'=>'Human','command'=> proc {@gg_player_white.value='Human';get_players_defs} )
    m.add('command','label'=>'GnuGo','command'=> proc {@gg_player_white.value='GnuGo';get_players_defs})

    b.configure('menu'=>m)

    fr.pack('pady'=> 10)

    fr= TkFrame.new(@cmd2,TkDefaults::GUI['GnuGo.choose.black'])


    TkLabel.new(fr,'image'=>Defaults::BStones[20]).pack('side'=>'left')
    b=TkMenubutton.new(fr,'textvariable'=>@gg_player_black,'indicatoron'=>'true').pack('side'=>'left')
    m=TkMenu.new(b,'tearoff'=>'false')
    m.add('command','label'=>'Human','command'=> proc {@gg_player_black.value='Human';get_players_defs} )
    m.add('command','label'=>'GnuGo','command'=> proc {@gg_player_black.value='GnuGo';get_players_defs})

    b.configure('menu'=>m)

    fr.pack('pady'=> 10)

    @beginner_help = Defaults.get_default_value('gg_show_beginner_help')
    opts= TkDefaults::GUI['GnuGo.check.beginner'].update(
                                                         'variable' => @beginner_help , 'offvalue' => '0' , 'onvalue' => '1' )
    TkCheckbutton.new(@cmd2,opts).pack('pady'=> 10)

    
  end

  def get_size

    form= Form.new('gg_new_game')
    frame= form.add_frame('GnuGo.frame.new',0,0)
    cols=Defaults.get_default_value('gg_new_game_size')
    form.add_label(frame,'GnuGo.label.new.Columns',0,0)
    form.add_field(frame,'GnuGo.entry.new',cols,'normal',1,0)
    komi=Defaults.get_default_value('gg_new_game_komi')
    form.add_label(frame,'GnuGo.label.new.komi',0,1)
    form.add_field(frame,'GnuGo.entry.komi',komi,'normal',1,1)
    handi=Defaults.get_default_value('gg_new_game_handi')
    form.add_label(frame,'GnuGo.label.new.handi',0,2)
    form.add_field(frame,'GnuGo.entry.handi',handi,'normal',1,2)
    level=Defaults.get_default_value('gg_new_game_level')
    form.add_label(frame,'GnuGo.label.new.level',0,3)
    form.add_field(frame,'GnuGo.entry.level',level,'normal',1,3)

    form.add_label(frame,'GnuGo.label.new.gg_cmd',0,4)
    form.add_field(frame,'GnuGo.entry.gg_cmd',@gg_cmd,'normal',1,4)


    frame= form.add_frame('GoEditor.frame.new',0,1)
    frame.grid('ipadx'=>0, 'ipady'=>0)
    opts=TkDefaults::GUI['GnuGo.button.new.ok'].update('command'=> proc {  form.destroy; new_game(cols.value.to_i,komi.value,handi.value,level.value)})
    ok= TkButton.new(frame,opts).pack('side'=>'left')
    opts=TkDefaults::GUI['GnuGo.button.new.cancel'].update('command'=> proc { form.destroy} )
    TkButton.new(frame,opts).pack('side'=>'left')

    form.bind('Return', proc { ok.configure('state'=>'active','relief'=>'sunken'); Tk.update; ok.invoke })

  end

  def new_game(size,komi,handi,level)

    @size= size
    @collection= SGF.new
    @last_node = Node.new
    @last_node['AP']= 'RubyGo:'+ $version
    @last_node['GM']= '1'
    @last_node['FF']= '4'
    @last_node['DT']= Time.now.strftime '%Y-%m-%d'



    @last_node['SZ']= @size.to_s+':'+@size.to_s

    @player= {'W'=> @gg_player_white.value, 'B'=>@gg_player_black.value}
    @last_node['PW']= @player['W']
    @last_node['PB']= @player['B']
    @last_node['KM']= komi
    @last_node['HA']= handi if handi != '0'

    @gg=nil

    set_root(@last_node)
    @collection.push(@root)
    @last_move = -1

    open_gg(@size,komi,handi,level)

    if handi != '0'
      set_turn('W')
    else
      set_turn('B')
    end

  end

  def open_gg(size,komi,handi,level)
    raise StandardError if @size.nil?
    begin
       cmd= @gg_cmd.value
       cmd += " --level #{level}" if level != ''
       cmd += ' --quiet --mode gtp'
      @gg= IO.popen(cmd ,'r+')

  # send(CONFIG,"new_game") not implemented
      send(CONFIG,"boardsize #{size}")
      send(KOMI, "komi #{komi}")
      send(HANDICAP,"fixed_handicap #{handi}")  if handi != '0'
    rescue
      @talk.putline("Gnugo not running: #{$!}\n",'error')
      @player['W']=@gg_player_black.value='Human'
      @player['W']=@gg_player_white.value='Human'
      set_node_name("both players set to 'Human'")
      @gg=nil
    end

  end

  def open_file(file = nil)
    @player= {'B'=>'SGF','W'=>'SGF'}
    super(file)

    if @root

      @size=@width # @root['SZ'][0]
      open_gg(@size,@root['KM'],@root['HA'].to_s)
      while @current_node.next
        set_node @current_node.next
        if (move= @current_node['W'])
          if move.values[0].empty? or move.values[0]=='zz'
            send(MOVE,'white PASS')
          else
            send(MOVE,"white #{Grid.to_coords(move.col,move.row,@size)}")
          end
        elsif  (move= @current_node['B'])
          if move.values[0].empty? or move.values[0]=='zz'
            send(MOVE,'black PASS')
          else
            send(MOVE, "black #{Grid.to_coords(move.col,move.row,@size)}")
          end
        end
      end
    end
    @last_node= @current_node

    get_players_defs

  end

  def set_node(node)
    set_node_name('')
    super(node)
    @goban.raise_all

    if p= node['W'] or p = node['B']
      if p.move_pass
        str = (p.ident== 'W' ? 'white' : 'black') + ' pass'
        if @one_pass
          @player['B']=@gg_player_black.value='Human'
          @player['W']=@gg_player_white.value='Human'
        else
          @one_pass= true
        end
      else
        str= "(#{p.ident}): #{Grid.to_coords(p.col,p.row,@height)}"
        @one_pass= false
      end
      set_move_label(str)
      set_turn  ( p.ident == 'W' ? 'B' : 'W')
      @w_p_inf.set_pris(@w_pris) if @w_p_inf
      @b_p_inf.set_pris(@b_pris) if @b_p_inf
    end
  end

  def set_turn(color)
    super(color)
    play_turn(color)
  end

  def play_turn(color)

    if @player[color]== 'Human'
      @top.cursor('')
      @goban.drag_stone(color){ |col,row| set_move(col,row) }
    elsif  @player[color] == 'GnuGo' and @gg
      @top.cursor('watch')
      @goban.undrag_stone
      send(GET," genmove_#{gg_color(color)}")
    end
  end

  def get_players_defs
    return if  @player.nil?
    @player['W']= @gg_player_white.value
    @player['B']= @gg_player_black.value
    play_turn(@player_turn)
  end

  def gg_color(color)
    @player_turn== 'B' ? 'black' : 'white'
  end

  def set_move(col,row)
    @move_col= col
    @move_row= row
    if @gg
      send(PUT,"#{gg_color(@player_turn)} #{Grid.to_coords(col,row, @size)}")
    else
      add_move(Coords.to_a(@move_col,@move_row))
    end
  end

  def send(code,cmd)
    return unless @gg

    if cmd
      cmd = code.to_s+ ' '+ cmd
    else
      cmd= code
    end


    @gg.puts(cmd)
    Tk.update
    receive

  end

  def receive

    return unless @gg

    s= @gg.gets
    st=s[0]
    code= s[1..2].to_i
    msg=s[4..-2]

    buf = msg+"\n"

    while (s= @gg.gets ) != "\n"
      buf << s #.chomp
    end

    if st == ??
      @talk.putline(msg+"\n",'error')
      case code
      when PUT
        set_turn(@player_turn)
      end
      return
    end

    case code
    when PUT
      add_move(Coords.to_a(@move_col,@move_row))
    when PASS
      add_move('')
    when GET
      add_move( gg_move(msg))
    when INFO
      @talk.putline(buf)
    when SCORE
      @talk.putline('final score: '+msg+ "\n",'info')
      set_node_name('final score: '+msg)
      @root['RE']=msg
    when UNDO
  # current_node must always be last_node
  # undo is success if we are here
      @player['B']=@gg_player_black.value='Human'
      @player['W']=@gg_player_white.value='Human'

      undo_node
      set_node @current_node.prev
      @last_node= @current_node
      set_node_name("both players set to 'Human'")
    when HANDICAP
      node=Node.new
      msg.split(' ').each { |move|  node['AB']= gg_move(move)}
      push_node(node)
    end

  end

  def gg_move(msg)
    if msg == 'PASS'
      return( '')
    else
      x,y = Move.to_cr(msg)
      col,row =  Grid.to_int(x,y,@size)
      return( Coords.to_a(col,row))
    end


  end

  def send_pass
    send(PASS, "#{gg_color(@player_turn)} PASS")
  end

  def send_score
    send(SCORE, "final_score")
  end

  def send_undo
    if @gg
      send(UNDO, "undo")
    else
      undo_node
      set_node @current_node.prev
      @last_node= @current_node

    end
  end

  def add_move(v)
    node = Node.new
    node[@player_turn]=v
    push_node(node)
  end

  def push_node(node)
    #debug  @last_node.to_s+ " + " + node.to_s

    @last_node.next = node
    node.prev = @last_node
    @last_node = node
    set_node(node)
  end

=begin
  def add_pris(color,col,row,nb)
    super(color,col,row,nb)
    case color
    when ?B
      @w_p_inf.inc_pris
    when ?W
      @b_p_inf.inc_pris
    end

  end

  def del_pris(color,col,row,nb)
    super(color,col,row,nb)
    case color
    when 'B'
      @w_p_inf.dec_pris
    when 'W'
      @b_p_inf.dec_pris
    end

  end
=end

  def put_stone(color,col,row,nb)
    super(color,col,row,nb)
    @goban.show_libs(col,row) if @beginner_help.value == '1'
  end




end


