require 'package.rb'

AVOID_W   = 254
BLOCKED_W = 255
NORTH = 1
WEST  = 2
EAST  = 4
SOUTH = 8

$bases = {}

def notify_leave(pos)
  $sp.unset_info(pos,ROBOT_FL)
end

def notify_arrived(pos)
  $sp.set_info(pos,ROBOT_FL)
end

class Robot
  attr_reader   :id
  attr_reader   :pos
  attr_accessor :move_id
  attr_accessor :last_move
  attr_reader   :num_packs
  
  def initialize(id)
    @id = id
    @pos = -1
    @num_packs = 0
  end

  def has_cargo
    @num_packs > 0
  end
  
  def x
    @pos % $width
  end
  
  def y
    @pos / $width
  end

  def set_x(val)
    @pos = val
  end
  
  def set_y(val)
    @pos += val * $width
  end

  def north
    @pos += $width
    @last_move = NORTH
    notify_leave(@pos)
  end
  
  def south
    @pos -= $width
    @last_move = SOUTH
    notify_leave(@pos)
  end
  
  def west
    @pos -= 1
    @last_move = WEST
    notify_leave(@pos)
  end
  
  def east
    @pos += 1 
    @last_move = EAST
    notify_leave(@pos)
  end

  def pick(pid) 
    @num_packs += 1
  end
  
  def drop(pid)
    @num_packs -= 1 if @num_packs > 0
    $sp.set_info(@pos,DROP_FL) if @pos > 0
  end

  def kill
    notify_leave(@pos)
    @pos = -1
    nil # Returns nil
  end

  def put(io,cap,fuel)
    io.puts "Robot #{@id} packs:#{@carrying}"
  end
end

class PlayerRobot < Robot
  attr_reader    :carrying
  attr_accessor  :heading
  attr_accessor  :cap
  attr_accessor  :dests

  # id:: game robot id
  def initialize(id)
    super(id)
    @cargo = {}
    @cargo_by_dest = {}
    @heading = -1
    @cap = 0
    @carrying = 0
  end

  def pick(pid) 
    super(pid)
    if( package = $cur_packages[pid] )
      @carrying += package.weight
      @cargo[package.id] = package 
      (@cargo_by_dest[package.dest] ||= []) << package
    end
  end

  def drop(pid)
    super(pid)
    if p=@cargo.delete(pid)
      a = @cargo_by_dest[p.dest]
      a.delete(p)
      @cargo_by_dest.delete(p.dest) if(a.size < 1)
      w = p.weight
      @carrying -= w 
    end
  end
  
  def dests
    @cargo_by_dest.keys
  end

  def drop_largest
    a = @cargo_by_dest[@pos]
    best_p = nil
    best_w = 0
    if a
      a.each do |p|
        best_w,best_p = p.weight,p if p && (p.weight > best_w)
      end
    end
#   log "No package to drop here..." if !best_p 
    best_p && "1 Drop #{best_p.id}"
  end
  
  def living
    @pos >= 0
  end

  def enough_cargo
    @cap < 2*@carrying
  end
  
  def local_cap
    return @cap-@carrying
  end
  
  def put(io,cap,fuel)
    io.puts "Robot #{@id} #{@carrying}/#{cap} fuel:#{fuel}"
    for k,v in @cargo
      if v 
        io.puts "   package #{v.id} #{v.weight} (#{v.xdest},#{v.ydest})"
      end
    end
  end
  
  def drop_destinated
  end
end

class RobotMgr
  attr_reader :robots

  def initialize(my_id)
    @robots = {} # maybe should be a hash
    @my_id = my_id
    @move_id = 0
    @count = 0
    @banned = {}
  end

  def each(&block)
    for i,r in @robots
      block.call(r) if r
    end
  end

  def add_robot(id)
    if id == @my_id
      $player = r  = PlayerRobot.new(id)
      r.cap = $cap
    else
      r = Robot.new(id)
    end
    @count += 1
    @added += 1
    r
  end

  def size
    @count
  end

  def banned?(id)
    @banned[id]
  end

  def parse(line)
    return false unless line
    @last_count = @count
    @added = 0
    @count = 0
    move_id = @move_id += 1
    living = false
    banned = @banned
    check_ban = false
    line.split("#").each do |cmd|
      if( (cmd = cmd.split).length > 0 )
        id = cmd.shift.to_i
        if ! banned[id]
          living ||= (id == @my_id)
          @count += 1 if(@robots[id])
          check_ban = @robots[id] = add_robot(id) unless @robots[id]
          if robot = @robots[id]
            robot.move_id = move_id
            action = nil
            cmd.each do |c|
              case c
              when 'N'
                robot.north
              when 'W'
                robot.west
              when 'E'
                robot.east
              when 'S'
                robot.south
              when 'X'
                action = :set_x
              when 'Y'
                action = :set_y
              when 'P'
                action = :pick
              when 'D'
                action = :drop
              else
                robot.send(action,c.to_i)
              end
            end
            notify_arrived(robot.pos) if( id != @my_id )
              
            if check_ban
              if $sp[robot.pos] >= BLOCKED_W
                banned[id] = true
                @robots[id].kill
                @robots.delete(id)
              end
            end
          end
        end
      end
    end # parse

    if living && (@count != @last_count+@added) # I live but some robots died...
      # Remove dead robots from the array of robots...
      @robots.delete_if do |id,r|
        !(r && (r.move_id == move_id)) # Is robot r occured in the last turn...?
      end
    end

    return living # Returns true if the client is still living...
  end
  
  def check_all
    @robots.delete_if do |i,r|
      if $world[check_ban.x,check_ban.y] >= BLOCKED_W
        @banned[id] = true
        @robots.delete(i)
      end
    end
  end
end

	
