import Tkinter as tk
import tkMessageBox
import tkSimpleDialog
import owners
import collections
import sys
import functions
import urllib2
import subprocess

"""
This program is a utility providing easy editing in a graphical interface to 
the owner configuration file for the web crawler, thus automating any 
syntactical modifications to ensure their correctness. The tool itself offers
some basic options of modifying the settings and displays them in an 
easy-to-understand fashion.

The application itself is mostly self-contained within this file. The only 
exceptions to this are two function calls:
  * owners.gen_urls()           in the initialisation step
  * functions.check_one_url     in the check_entry() helper method
"""

#---------------------------------------------------------------------------#
# PARAMETER DECLARATIONS

owner_config_file = "owners.cwl"        # The path to the config file
def_own_message = "<default owner>"     # Message to display for owners that
                                        # do not own any URLs, and are 
                                        # therefore set as default owners
new_own_message = "<add new owner>"     # Message to display for adding owners
owner_container_width = 50              # The width for the two containers 
                                        # showing owner and URL names
button_width = 15                       # The width for the buttons

#---------------------------------------------------------------------------#
# INITIALISATION

# Data storage - reading and storing information from the config file upon
# initialisation
#
# Requires owners.gen_urls()                                        
owns = owners.gen_urls(owner_config_file)
if len(owns.keys()) == 0:
  print "Config file error!"
  sys.exit()
named_owns = {}
for owner in owns.keys():
  pos = owner.find(":")
  if pos != -1:
    newname = owner[pos+1:].strip() + " (" + owner[:pos].strip() + ")"
    named_owns[newname] = owner[:pos]
    owns[owner[:pos]] = owns[owner]
    del owns[owner]
  else:
    named_owns[owner] = owner
    
# Name update - uses finger to update some names that might be available from
# given e-mails that lack an attached name
for owner in owns.keys():               #OPTIMIZATION consider opening file once
  chkowner = owner.split("@")[0]
  try:
    queryres = subprocess.check_output(["finger", chkowner])
  except OSError as ose:
    break
  namepos = queryres.find("Name:")
  if namepos == -1:
    continue
  queryres = queryres[namepos+len("Name:"):queryres.find("\n", namepos)]
  newname = owner + " (" + queryres + ")"
  named_owns[newname] = owns[owner]
  conffile = open(owner_config_file, "a+")
  ownpos = conffile.read().find(owner)
  conffile.seek(ownpos+len(owner))
  conffile.write(":%s" % (queryres))
  conffile.close()
    
#---------------------------------------------------------------------------#
# APPLICATION SETTINGS

class App(object):
  def __init__(self, master):
    frame = tk.Frame(master)
    frame.grid()

    # Owners OptionList
    self.owner = tk.StringVar(frame)
    self.owner.set(iter(named_owns.keys()).next())
    self.owner.trace("w", self.list_update)
    self.opts = named_owns.keys()
    self.opts.append(new_own_message)
    self.ownopts = tk.OptionMenu(master, self.owner, *self.opts)
    self.ownopts.config(width=owner_container_width - 5)
    self.ownopts.grid(row=1, column=0)
    
    # URL display ListBox
    self.urllist = tk.Listbox(frame, selectmode=tk.MULTIPLE)
    self.list_update(self, self.owner.get())
    self.urllist.bind("<Double-Button-1>", self.edit_entry)
    self.urllist.config(width=owner_container_width)
    self.urllist.grid(row=0, column=0, rowspan=5, pady=4)

    # QUIT Button
    self.quitbutton = tk.Button(frame, text="DONE", command=frame.quit)
    self.quitbutton.config(width=button_width)
    self.quitbutton.grid(row=4, column=1)
    
    # EDIT Button
    self.editbutton = tk.Button(frame, text="Edit URL", command=self.edit_entry)
    self.editbutton.config(width=button_width)
    self.editbutton.grid(row=1, column=1)
    
    # ADD URL Button
    self.addbutton = tk.Button(frame, text="Add URL", command=self.add_entry)
    self.addbutton.config(width=button_width)
    self.addbutton.grid(row=0, column=1)

    # DEL URL Button
    self.delbutton = tk.Button(frame, text="Delete URLs", command=self.del_entry)
    self.delbutton.config(width=button_width)
    self.delbutton.grid(row=2, column=1)

    # DEL OWNER Button
    self.delownbutton = tk.Button(frame, text="Remove owner", command=self.del_owner)
    self.delownbutton.config(width=button_width)
    self.delownbutton.grid(row=3, column=1)
    
  def list_update(self, *args):
    'Update the URL list with the new data'
    if self.owner.get() == new_own_message:
      self.add_owner(self)
      return
    self.urllist.delete(0, tk.END)
    urls = owns[named_owns[self.owner.get()]]
    if len(urls) == 0:
      self.urllist.insert(tk.END, def_own_message)
    else:
      for url in urls:
        self.urllist.insert(tk.END, url)
        
  def edit_entry(self, *args):
    'Edit Button method - edits selected URL in the URL list'
    if len(self.urllist.curselection()) != 1:
      tkMessageBox.showerror("Invalid selection", "Only one URL must be selected in order to edit it!")
    else:
      owner = named_owns[self.owner.get()]      
      url = self.urllist.get(self.urllist.curselection())
      if url == def_own_message:
        tkMessageBox.showerror("Invalid selection", "Cannot edit default ownership!")
      else:
        newurl = tkSimpleDialog.askstring("Edit URL", "Please enter the new URL:", initialvalue=url)
        if newurl:
          if tkMessageBox.askyesno("Confirm", "Changing %s to %s. Proceed?" % (url, newurl)):
            if not check_entry(newurl):
              return
            conffile = open(owner_config_file, "r")
            linebuf = conffile.readlines()
            conffile.close()
            conffile = open(owner_config_file, "w")
            for line in linebuf:
              if not line.startswith(owner):
                conffile.write(line)
              else:
                pos = line.find(url)
                while line.find("|", pos) != -1 and line[pos+len(url)] != "|":
                  pos = line.find(url, pos+1)
                conffile.write(line[:pos] + newurl + line[pos+len(url):])
            conffile.close()
            owns[owner].remove(url)
            owns[owner].add(newurl)
            self.list_update()
        
  def add_entry(self, *args):
    'Add Button method - adds a new URL for the current owner'
    owner = named_owns[self.owner.get()]
    ownername = self.owner.get()[self.owner.get().find("(")+1:self.owner.get().find(")")]
    newurl = tkSimpleDialog.askstring("Add new URL", "Please enter the new URL for %s (%s):" % (ownername, owner), initialvalue="www.")
    if newurl:
      if tkMessageBox.askyesno("Confirm", "Adding new URL %s for %s. Proceed?" % (newurl, owner)):
        if not check_entry (newurl):
          return
        conffile = open(owner_config_file, "r")
        linebuf = conffile.readlines()
        conffile.close()
        conffile = open(owner_config_file, "w")
        for line in linebuf:
          if not line.startswith(owner):
            conffile.write(line)
          else:
            conffile.write(line.rstrip() + "|" + newurl + "\n")
        conffile.close()
        owns[owner].add(newurl)
        self.list_update()
    else:
      tkMessageBox.showwarning("Invalid entry", "No URL has been provided. Ignoring...")
          
  def del_entry(self, *args):
    'Delete Button method - deletes the currently selected URLs'
    owner = named_owns[self.owner.get()];
    conffile = open(owner_config_file, "r")
    linebuf = conffile.readlines()
    conffile.close()
    conffile = open(owner_config_file, "w")
    for line in linebuf:
      if not line.startswith(owner):
        conffile.write(line)
      else:
        for index in self.urllist.curselection():
          url = self.urllist.get(index)
          upos = line.find(url)
          while line.find("|", upos) != -1 and line[upos+len(url)] != "|":
            upos = line.find(url, upos+1)
          lpos = upos-1
          while line[lpos] != "|":
            lpos =- 1
          line = line[:lpos] + line[upos+len(url):]
          owns[owner].remove(url)
        conffile.write(line)
    conffile.close()
    self.list_update()
      
    
  def update_owners(self, *args):
    'Update the owner options menu and sets it to the first entry'
    optionsmenu = self.ownopts["menu"]
    optionsmenu.delete(0, tk.END)
    for owner in named_owns.keys():
      optionsmenu.add_command(label=owner, command=lambda value=owner:self.owner.set(value))
    optionsmenu.add_command(label=new_own_message, command=lambda value=new_own_message:self.owner.set(value))
    if args[0] is not None:
      self.owner.set(named_owns.keys()[args[0]])
    
    
  def add_owner(self, *args):
    'Add owner option method - adds a new owner and updates the option list'
    newown = tkSimpleDialog.askstring("Add new owner", "Please enter the new owner's e-mail:")
    newownname = tkSimpleDialog.askstring("Add new owner", "Please enter the new owner's name:")
    if tkMessageBox.askyesno("Confirm", "Adding new owner %s with e-mail %s. Proceed?" % (newownname, newown)):
      conffile = open(owner_config_file, "a")
      conffile.write("%s:%s\n" % (newown, newownname))
      conffile.close()
      owns[newown] = set()
      named_owns[newownname + " (" + newown + ")"] = newown
      self.update_owners(0)
      
  def del_owner(self, *args):
    'Remove owner button method - removes the current owner and updates list'
    owner = named_owns[self.owner.get()]
    ownername = self.owner.get()[self.owner.get().find("(")+1:self.owner.get().find(")")]
    if tkMessageBox.askyesno("Confirm", "Removing %s with e-mail %s and all associated owned URLs. Are you sure you wish to continue?" % (ownername, owner)):
      conffile = open(owner_config_file, "r")
      linebuf = conffile.readlines()
      conffile.close()
      conffile = open(owner_config_file, "w")
      for line in linebuf:
        if not line.startswith(owner):
          conffile.write(line)
      del owns[owner]
      del named_owns[self.owner.get()]
      self.update_owners(0)
    
#---------------------------------------------------------------------------#
# HELPER METHODS

    
def default_check():            #TODO break if one default?
  ''' Checks if there isn't only one default owner and issues a warning
  
  Reads the config file and checks the number of owners that don't have any
  URLs attached to them. An ideal situation would be exactly one person (the
  e-mail sending module can only handle one default owner anyway). Issues a
  warning if there are none or more than one default owners
  '''
  no_def = True
  for urls in owns.values():
    if len(urls) == 0:
      if not no_def:
        if tkMessageBox.askyesno("Multiple default owners", "There have been multiple owners set as default. Are you sure you wish to exit?"):
          root.destroy()
        break
      no_def = False
  if no_def:
    if tkMessageBox.askyesno("Undefined default owner", "No default owner has been defined! Are you sure you wish to exit?"):
      root.destroy()
      
def check_entry (url):
  ''' Cursory URL validity check
  
  Simply calls the check_one_url function to quickly verify whether the URL
  supplied is valid, by sending a request and checking the response. Issues
  a warning if the URL could not be verified, but still allows the user to 
  proceed adding the given value.
  
  Requires functions.check_one_url()
  '''
  if not url.startswith("http://"):
    url = "http://" + url
  if not functions.check_one_url (url, [], [], False):
    if not tkMessageBox.askyesno("Unverified URL", "The URL given (%s) was verified, but wasn't found to be valid. Continue?" % (url)):
      return False
  return True    
  
#---------------------------------------------------------------------------#
# APPLICATION START
    
root = tk.Tk()
root.title("Crawler owner editing utility")
#root.protocol("WM_DELETE_WINDOW", default_check)

app = App(root)

# Center the window on screen
root.withdraw()
root.update_idletasks()
x = (root.winfo_screenwidth() - root.winfo_reqwidth()) / 2
y = (root.winfo_screenheight() - root.winfo_reqheight()) / 2
root.geometry("+%d+%d" % (x, y))
root.deiconify()

root.mainloop()
