#!/usr/bin/python3

# tg.py - A simple Turtle Graphics that exports a PGM.
# By: Pedro Izecksohn
# Version: 2014-Jul-22 21:00
# License: Keep this source-code in Python and don't remove my name from this file.

import math
import sys

class Point:
  def __init__ (self, x, y):
    self.x = x
    self.y = y
  def __str__ (self):
    return ('('+str(self.x)+', '+str(self.y)+')')
class PGM:
  def __init__ (self, width, height):
    self.width = width
    self.height = height
    self.ba = bytearray (width*height)
    self.comment = None
  def __getitem__ (self, pos):
    return ba[pos]
  def __setitem__ (self, pos, value):
    #print ('pos = '+str(pos))
    self.ba[pos] = value
  def turn (self, point, value):
    try:
      self [int(point.x)+(int(point.y)*self.width)] = int(value)
    except IndexError as ie:
      print ('IndexError: '+str(point))
  def write (self, f):
    if (None == self.comment):
      self.comment = ''
    s = ('P5\n# '+self.comment+'\n'+str(self.width)+' '+str(self.height)+'\n255\n')
    f.write (bytes(s, encoding='ascii'))
    f.write (self.ba)

class Screen:
  def __init__ (self, width, height):
    self.width = width
    self.height = height
    self.pgm = PGM (width, height)
  def turn (self, point, color):
    real = Point (point.x*self.width, point.y*self.height)
    #print (str(point.x)+', '+str(point.y)+' = '+str(real.x)+', '+str(real.y))
    self.pgm.turn (real, color)
  def comment (self, s):
    if (None == self.pgm.comment): self.pgm.comment = s

class Command:
  def __init__ (self, line):
    self.char = None
    i = 0
    while (i < len(line)):
      c = line[i]
      #print ('i = '+str(i)+' c = '+c)
      if (c.isspace()):
        #print ('It is a space.')
        i += 1
        continue
      self.char = c
      i += 1
      while (i < len(line)):
        #print ('I\'m inside the second while.')
        c = line[i]
        if (c.isspace()):
          i += 1
          continue
        try:
          self.float = float (line[i:])
        except:
          self.aux = line[i:]
        break
      break
  def __str__ (self):
    s = (self.char+' ')
    if hasattr (self, 'float'):
      s += str(self.float)
      return s
    if hasattr (self, 'aux'):
      s += self.aux
    return s

# Returns a list of commands.
def readCode (f):
  code = []
  while True:
    line = f.readline()
    if (len(line)):
      line = line[0:len(line)-1]
      command = Command (line)
      #print ('I read the command: '+str (command))
      code.append (command)
      continue
    return code

class Frame:
  def __init__ (self, raddr, ntimes):
    self.raddr = raddr
    self.ntimes = ntimes

class Turtle:
  def __init__ (self, screen):
    self.screen = screen
    self.position = Point (0.5, 0.5) # Its the center of the screen.
    self.angle = -(math.pi/2) # radians
    self.drawing = True
    self.color = 255
    self.step_length = 0.001
  def __str__ (self):
    return ('<Turtle position='+str(self.position)+' degrees='+str(math.degrees(self.angle))+' />')
  def penDown (self):
    self.drawing = True
    self.screen.turn (self.position, self.color)
  def step (self):
    self.position = Point (self.position.x + (math.cos(self.angle)*self.step_length),\
                           self.position.y + (math.sin(self.angle)*self.step_length))
    if self.drawing: self.screen.turn (self.position, self.color)
  def forward (self, distance):
    while (distance>0):
      self.step()
      distance -= self.step_length
  def executeCode (self, code):
    stack = []
    index = -1
    while True:
      index += 1
      try:
        command = code [index]
      except IndexError: # Replaces an if.
        return
      #print ('index = '+str(index)+'\tcommand = '+str(command))
      if (command.char == 'A'):
        self.angle = math.radians(command.float)
        continue
      if (command.char == 'X'):
        self.position.x = command.float
        continue
      if (command.char == 'Y'):
        self.position.y = command.float
        continue
      if (command.char == 'F'):
        self.forward (command.float)
        continue
      if (command.char == 'T'):
        self.angle += math.radians(command.float)
        continue
      if (command.char == 'U'):
        self.drawing = False
        continue
      if (command.char == 'D'):
        self.penDown()
        continue
      if (command.char == 'R'):
        frame = Frame (index, command.float)
        stack.append (frame)
        continue
      if (command.char == 'E'):
        try:
          frame = stack.pop()
        except IndexError:
          print ('The command index '+str(index)+' is an E without a corresponding R .')
          sys.exit (2)
        if frame.ntimes > 1:
          frame.ntimes -= 1
          index = frame.raddr
          stack.append (frame)
        continue
      if (command.char == 'C'):
        self.color = command.float
        continue
      if (command.char == '#'):
        self.screen.comment (command.aux)
        continue
      if (command.char == 'P'):
        self.step_length = command.float
        continue
      if (command.char == 'B'):
        print (str(self))
        continue

def printUsage():
  print ('cat file.tg | ./tg.py width height file.pgm')
  sys.exit (2)

# main:
try:
  screen = Screen (int(sys.argv[1]), int(sys.argv[2]))
  fname = sys.argv[3]
except:
  printUsage()
try:
  f = open (fname, 'xb')
except FileExistsError as fee:
  print (str (fee))
  sys.exit (2)
turtle = Turtle (screen)
code = readCode (sys.stdin)
turtle.executeCode (code)
screen.pgm.write (f)
