text: # This Script:
  #   - supports building mapping by mode
  # Valid Modes:
  #   - Point
  #   - Line (horizontal or vertical)
  #       - asks for the number of points in the line
  #       - Naming Schema:
  #           - Vertical Lines:
  #               - Top-Bottom = A-Z
  #           - Horizontal Lines:
  #               - Left-Right = A-Z
  #   - Rectangle
  #   - Grid
  #       - Name Schema:
  #           - Columns: 0-N
  #           - Rows: A-Z
  
  #################### TODO ###################
  # name points before they get sent to the database
  
  import Tkinter
  import math
  import requests
  import json
  from PIL import Image, ImageTk
  from sys import exit
  from db.db import SERVER_URL, DEBUG
  import ntpath
  import argparse
  import sys
  from server_interface import ServerInterface
  
  server_interface = None
  APP = {}  # will hold global information for TKinter app
  DENSITY = 30 # default density of grids
  
  def file_name(file_path):
      head, tail = ntpath.split(file_path)
      return tail or ntpath.basename(head)
  
  def get_floor_information():
      """ Get the name of the building and floor information from the user
          If a non-integer floor number is entered, keep asking"""
  
      building_name = raw_input("Building: ")
      floor_number = -1
      while True:
          try:
              floor_number = int(raw_input("Floor Number: "))
              break
          except ValueError:
              print >> sys.stderr, "Not a number. Try again"
  
      return building_name, floor_number
  
  
  def get_floor_identifier(image_file):
      """ retrieves floor identifier from server """
      global server_interface
      building_name, floor_number = get_floor_information()
      get_payload = {
          'building_name__iexact': building_name.lower(),
          'floor_number': floor_number
      }
  
      worked, data = server_interface.get_single_item(ServerInterface.FLOOR,
                                                      get_payload)
  
      if not worked:
          with open(image_file, 'rb') as image:
              post_payload = {
                  'floor_number': floor_number,
                  'building_name': building_name
              }
  
              files = {
                  'image': image
              }
  
              data = server_interface.post_with_files(ServerInterface.FLOOR,
                                                      post_payload,
                                                      files)
      return data['resource_uri']
  
  #--------------------------------
  # Object code here
  #--------------------------------
  
  class Location(object):
      """ used for posting to the server """
  
      def __init__(self, point, name, verbose):
          self.point = point
          self.name = name
          self.verbose = verbose
  
      def save(self, floor_identifier):
          global server_interface
          payload = {
              'verbose_name': self.verbose,
              'short_name': self.name,
              'x_coordinate': self.point.x,
              'y_coordinate': self.point.y,
              'floor': floor_identifier
          }
  
          for i in xrange(4):
              payload['direction'] = i * 90
              server_interface.post(ServerInterface.LOCATION, payload)
  
  class Point(object):
      """ Point Object, for simple (x,y) """
      def __init__(self, x, y):
          self.x = x
          self.y = y
  
  #--------------------------------
  # Object code for TKinter drawing
  #--------------------------------
  
  class DrawPoint(object):
      """ wrapper for Points when drawing"""
      def __init__(self, point):
          self.point = point
  
      def points(self):
          """ Returns the point to be drawn for a point """
          return self.point
  
  class Rectangle(object):
      def __init__(self, point_list):
          p1 = point_list[0]
          p2 = point_list[1]
  
          self.XL = min(p1.x, p2.x)
          self.XR = max(p1.x, p2.x)
          self.YT = min(p1.y, p2.y)
          self.YB = max(p1.y, p2.y)
  
      def points(self):
          """ Returns the corners of a rectangle """
          return [Point(self.XL, self.YT),
                  Point(self.XR, self.YT),
                  Point(self.XR, self.YB),
                  Point(self.XL, self.YB)]
  
  #--------------------------------
  # Draw Functions Below
  #--------------------------------
  
  def cornersAndCenter(rect):
      """ Returns the points to be drawn for a rectangle """
      return [Point(rect.XL, rect.YT),
              Point(rect.XR, rect.YT),
              Point(rect.XR, rect.YB),
              Point(rect.XL, rect.YB),
              Point((rect.XL + rect.XR) // 2, (rect.YB + rect.YT) // 2)]
  
  def line(rect):
      """ Returns the points to be drawn for a line """
      num_points = int(raw_input('Number of points in line: '))
  
      width = rect.XR - rect.XL
      height = rect.YB - rect.YT 
  
      if width > height: 
          # horizontal line
          x = rect.XL
          y = (rect.YT + rect.YB) // 2
          x_step = width // (num_points - 1)
          y_step = 0
      else: 
          # vertical line
          x = (rect.XL + rect.XR) // 2
          y = rect.YT
          x_step = 0
          y_step = height // (num_points - 1)
  
      points = []
      for i in range(num_points):
          points.append(Point(x + (i * x_step), y + (i * y_step)))
  
      return points
  
  def grid(rect):
      """ Returns the points to be drawn for a grid """
      width = rect.XR - rect.XL
      height = rect.YB - rect.YT 
  
      num_across = max(1, int(math.ceil(width // DENSITY)))
      num_high = max(1, int(math.ceil(height // DENSITY)))
      x_justification = ((width - (DENSITY * (num_across - 1))) / 2) + rect.XL
      y_justification = ((height - (DENSITY * (num_high - 1))) / 2) + rect.YT
  
      points = []
      for x in range(num_across):
          for y in range(num_high):
              x_coord = x_justification + (DENSITY * x)
              y_coord = y_justification + (DENSITY * y)
              points.append(Point(x_coord, y_coord))
      return points
  
  #--------------------------------
  # TKinter Application Code Below
  #--------------------------------
  
  def initializeAPP(image_path):
      """ Initializes data for app and binds tkinter buttons """
      global APP
  
      image = Image.open(image_path)
      width, height = image.size[0], image.size[1]
  
      APP['window'] = Tkinter.Tk()
      APP['frame'] = Tkinter.Frame(APP['window'])
      image_tk = ImageTk.PhotoImage(image)
  
      APP['canvas'] = Tkinter.Canvas(APP['frame'], width=width, height=height)
      APP['canvas'].create_image(width // 2, height // 2, image=image_tk)
  
      APP['dims'] = {'w': width, 'h': height}
      APP['buttons'] = getButtons()
      APP['locations'] = []
      APP['points'] = []
      APP['canvas_list'] = []
      APP['mode'] = {'type': Rectangle, 'vertices': 2}
      APP['draw'] = cornersAndCenter
      packAndRunApp()
  
  def getButtons():
      """ Returns dict of buttons; will be added to APP object """
      global APP
      buttons = {'point_btn': Tkinter.Button(APP['frame'], text="Point", command=point_mode),
                 'line_btn': Tkinter.Button(APP['frame'], text="Line", command=line_mode),
                 'rectangle_btn': Tkinter.Button(APP['frame'], text="Rectangle", command=rectangle_mode),
                 'grid_btn': Tkinter.Button(APP['frame'], text="Grid", command=grid_mode),
                 'log_btn': Tkinter.Button(APP['frame'], text="Log", command=log)}
      return buttons
  
  def packAndRunApp():
      """ packs tkinter interface at startup of app """
      global APP
  
      APP['frame'].pack()
      APP['canvas'].pack()
  
      APP['buttons']['point_btn'].pack(side='left')
      APP['buttons']['line_btn'].pack(side='left')
      APP['buttons']['rectangle_btn'].pack(side='left')
      APP['buttons']['grid_btn'].pack(side="left")
  
      APP['canvas'].bind("<Button-1>", handle_click)
      APP['window'].mainloop()
  
  def draw_point(p, color):
      """ draws a point at the coordinates with the specified color """
      global APP
  
      radius = 5  # point radius
      new_canvas = APP['canvas'].create_oval(
          p.x - radius, p.y - radius, p.x + radius, p.y + radius, fill=color)
  
      APP['points'].append(p)
      APP['canvas_list'].append(new_canvas)
  
  def draw_rectangle(rectangle, outline_color):
      """ draws a rectangle at the coordinates with the specified color """
      global APP
  
      corners = rectangle.points()
      p1 = corners[0]
      p2 = corners[2]
  
      new_canvas = APP['canvas'].create_rectangle(
          p1.x, p1.y, p2.x, p2.y, outline=outline_color, width=2)
  
      APP['canvas_list'].append(new_canvas)
  
  def handle_click(click):
      """ Adds a point to the canvas; if there are enough points, allows logging """
      global APP
      point = Point(click.x, click.y)
  
      num_points = len(APP['points']) + 1
  
      if num_points > APP['mode']['vertices']:
          reset()
          APP['buttons']['log_btn'].pack_forget()
      elif num_points == APP['mode']['vertices']:
          APP['buttons']['log_btn'].pack(side='left')
  
      draw_point(point, 'blue')
  
  def log():
      """ generates the rest of the shape for drawing; asks for confirmation if input
          is correct.  """
      global APP
  
      APP['canvas'].unbind("<Button-1>")
  
      # name = raw_input("Short name: ")
      # verbose = raw_input("Verbose name: ")
  
      shape = APP['mode']['type'](APP['points'])
      reset()
  
      if APP['mode']['type'] == Rectangle:
          draw_rectangle(shape, 'green')
          for p in APP['draw'](shape):
              draw_point(p, 'green')
      else:
          draw_point(shape.points, 'red')
  
      # command = raw_input("Confirm Points? [Y/N]")
      command = 'Y'
      if command.upper() == 'Y':
          reset()
  
          draw_rectangle(shape, 'red')
          for p in APP['draw'](shape):
              draw_point(p, 'red')
  
          APP['locations'] += shape.points()
          APP['points'] = []
          APP['canvas_list'] = []
      else:
          reset()
  
      APP['buttons']['log_btn'].pack_forget()
      APP['canvas'].bind("<Button-1>", handle_click)
  
  def reset():
      """ removes unlogged points from screen """
      global APP
  
      for cid in APP['canvas_list']:
          APP['canvas'].delete(cid)
  
      APP['points'] = []
      APP['canvas_list'] = []
  
  def point_mode():
      """ resets points; sets APP's draw function to point mode""" 
      global APP
      APP['mode'] = {'type': DrawPoint, 'vertices': 1}
      APP['draw'] = points
      reset()
  
  def rectangle_mode():
      """ resets points; sets APP's draw function to rectangle mode""" 
      global APP
      APP['mode'] = {'type': Rectangle, 'vertices': 2}
      APP['draw'] = cornersAndCenter
      reset()
  
  def line_mode():
      """ resets points; sets APP's draw function to line mode""" 
      global APP
  
      APP['mode'] = {'type': Rectangle, 'vertices': 2}
      APP['draw'] = line
      reset()
  
  def grid_mode():
      """ resets points; sets APP's draw function to grid mode""" 
      global APP
      global DENSITY 
  
      APP['mode'] = {'type': Rectangle, 'vertices': 2}
      APP['draw'] = grid
      if DENSITY == 0:
          DENSITY = raw_input("Density: ")
      reset()
  
  #################################
  
  def main(image_file, debug, username='', password=''):
      """ opens up server interface; finds floor id; initializes and runs app"""
      global server_interface
  
      if not debug:
          server_interface = ServerInterface(username, password)
          floor_identifier = get_floor_identifier(image_file)
  
      initializeAPP(image_file)
  
      if not debug:
          command = raw_input("Save Points? [Y/N] ")
          if command == 'Y':
              for loc in locations:
                  loc.save(floor_identifier)
  
  if __name__ == '__main__':
      parser = argparse.ArgumentParser()
      parser.add_argument('image_file', help='The image to work with')
      parser.add_argument('username', help='Your username for the server')
      parser.add_argument('password', help='Your password for the server')
      parser.add_argument('-d', '--debug', action='store_true', help='Debug')
  
      args = parser.parse_args()
      main(args.image_file, args.debug, args.username, args.password)
  