# Shoegoo: Logo in Shoes

BOARD_WIDTH = 500
BOARD_HEIGHT = 500
CONTROLS_WIDTH = 180

class ShoeGooer
  class << self; attr_accessor :box; end
  def self.setup(box, turtle)
    self.box = box
    self.box.turtle = Goo.turtle = turtle
    @@buff = []
    @@depth = 0
    self.box.draw
  end
  def self.read(text)
    if text =~ /^(def |repeat )/
      @@buff << text
      @@depth += 1
    elsif @@depth == 0
      Goo.instance_eval(text)
    else
      @@buff << text
      if text =~ /^end/
        @@depth -= 1
        if @@depth == 0
          Goo.instance_eval(@@buff.join("\n"))
          @@buff = []
        end
      end
    end
    self.box.draw
    text
  end
  def self.erase
    @@buff = []
    self.box.turtle.reset
    self.box.draw
  end
end

class Goo
  class << self; attr_accessor :turtle; end
  def self.repeat(times, &block)
    times.times {block.call}
  end
  def self.move(steps)
    self.turtle.move(steps)
  end
  def self.turn(degrees)
    self.turtle.turn(degrees / 180.0 * Math::PI)
  end
  def self.left(degrees)
    turn(-degrees.to_f)
  end
  def self.right(degrees)
    turn(degrees.to_f)
  end
end

class Turtle
  attr_accessor :x, :y, :points
  def initialize(x, y)
    @x, @y = x, y
    @dir = [0.0, 1.0]
    @points = [[@x, @y]]
  end
  def reset
    @x, @y = BOARD_WIDTH / 2, BOARD_HEIGHT / 2
    @dir = [0.0, 1.0]
    @points = [[@x, @y]]
  end
  def draw
    $app.stroke(rgb(0, 1.0, 0))
    # π turn
    v = [-@dir[1], @dir[0]]
    # turn - 5 units
    p1 = [@x - 5 * v[0], @y - 5 * v[1]]
    # turn + 5 units
    p2 = [@x + 5 * v[0], @y + 5 * v[1]]
    # forward 10 units
    p3 = [@x + 10 * @dir[0], @y + 10 * @dir[1]]
    $app.line(p1[0], p1[1], p2[0], p2[1])
    $app.line(p2[0], p2[1], p3[0], p3[1])
    $app.line(p3[0], p3[1], p1[0], p1[1])
  end
  def move(dist)
    @x += dist * @dir[0]
    @y += dist * @dir[1]
    @points << [@x, @y]
  end
  def turn(r)
    @dir = [@dir[0] * Math.cos(r) - @dir[1] * Math.sin(r),
            @dir[0] * Math.sin(r) + @dir[1] * Math.cos(r)]
  end
  def each_line
    for i in (0...@points.size-1)
      yield [@points[i][0], @points[i][1], @points[i+1][0], @points[i+1][1]]
    end
  end
end

class GooeyBox
  attr_accessor :turtle
  def initialize
    @canvas = $app.flow(:margin => 0, :top => 0, :left => 0, :width => BOARD_WIDTH, :height => BOARD_HEIGHT)
  end
  def draw
    @canvas.clear do
      $app.background("#333333")
      $app.stroke(rgb(0, 0.8, 0))
      @turtle.each_line { |l| $app.line(*l) }
      @turtle.draw
    end
  end
end

class ControlPanel
  def self.setup
    $app.stack :top => 0, :left => BOARD_WIDTH, :width => CONTROLS_WIDTH, :margin => 10, :margin_right => $app.gutter do
      @@log = $app.edit_box :font => "Courier 10px", :width => CONTROLS_WIDTH - 20, :height => CONTROLS_WIDTH - 50, :margin_right => $app.gutter
      $app.para "\nTurtle command:" # spacing ick!
      $app.flow do
        @@input = $app.edit_line :width => CONTROLS_WIDTH - 70, :height => 25, :margin => 5
        $app.button("Go", :width => 45, :height => 25, :margin => 5) do
          @@log.text += ShoeGooer.read(@@input.text) + "\n"
          @@input.text = ''
        end
      end
      $app.button("Erase", :width => 60, :height => 25, :margin => 5) { ShoeGooer.erase; @@log.text = "" }
      $app.para "\nHints:"
      $app.para <<-HINTS, :font => "Courier 10px"
move 50
right 45
repeat 90 do
  right 4
  move 4
end
def square size
  repeat 4 do
    move size
    right 90
  end
end
square 50
@x = 5
repeat 10 do
  square @x
  right 36
  @x += 10
end
HINTS
    end
  end
end

Shoes.app :width => BOARD_WIDTH + CONTROLS_WIDTH, :height => BOARD_HEIGHT do
  $app = self
  ControlPanel.setup
  ShoeGooer.setup(GooeyBox.new, Turtle.new(250,250))
  $app.keypress {|key| alert(key)}
end
