#    Gedit haiku plugin v0.1
#    Copyright (C) 2007 Jason Copenhaver <jcopenha@typedef.org>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 
#    02110-1301  USA


# Extras
# The syllable counting part comes from 
# http://www.oblomovka.com/code/haiku/haiku which is a 
# python port of the code found at 
# http://search.cpan.org/src/GREGFAST/Lingua-EN-Syllable-0.251/Syllable.pm
# I've updated the syllable counting, but it still misses some words


import gedit
import gtk
import gobject
import re

class haikuPlugin( gedit.Plugin):

  def __init__(self):
    gedit.Plugin.__init__(self)
    self._instances = {}

  def activate(self, window):
    self._instances[window] = haiku(window)
    self._instances[window].activate()
 
  def deactivate(self, window):
    self._instances[window].deactivate()
  
  def update_ui(self, window):
    self._instances[window].update_ui()
      
class haiku:
  SubSyl = [
       'cial',
       'tia',
       'cius',
       'cious',
       'giu',                     # belgium!
       'ion',
       'iou',
       'sia$',
       '.ely(\s|$)+',             # absolutely! (but not ely!)
       '[^dteio]ed(\s|$)+',       
       'ioed(\s|$)+',
       '[dtkm]es(\s|$)+'
      ]

  AddSyl = [ 
       'ia',
       'riet',
       'dien',
       'iu',
       'io',
       'ii',
       '[aeiouym]bl(\s|$)+',        # -Vble, plus -mble
       '[aeiou]{3}',                # agreeable
       '^mc',
       'ism$',                      # -isms
       '([^aeiouy])\1l(\s|$)+',     # middle twiddle battle bottle, etc.
       '[^l]lien',                  # alien, salient [1]
           '^coa[dglx].',           # [2]
       '[^gq]ua[^auieo]',           # i think this fixes more than it breaks
       'dnt(\s|$)+',                # couldn't
       'pl(\s|$)+',                 # apple - after being munged
      ]
  def __init__(self, window):
    self.handler_ids = []
    self.window = window
    self.popupwindow = gtk.Window(gtk.WINDOW_POPUP)
    self.label = gtk.Label("0 0 0")
    self.popupwindow.add(self.label)
    self.Connected = False
    return
  
  def activate(self):
    view = self.window.get_active_view()
    if type(view) == gedit.View and not self.Connected:
      buf = view.get_buffer()
      handler_id = buf.connect ( 'changed', self.changed)
      self.handler_ids.append((handler_id, buf))
      handler_id = view.connect ( 'cut-clipboard', self.cut_paste_event)
      self.handler_ids.append((handler_id, view))
      handler_id = view.connect ( 'paste-clipboard', self.cut_paste_event)
      self.handler_ids.append((handler_id, view))
      handler_id = self.window.connect ( 'window-state-event', self.window_state_event)
      self.handler_ids.append((handler_id, self.window))
      handler_id = self.window.connect ( 'configure-event', self.configure_event)
      self.handler_ids.append((handler_id, self.window))
      self.Connected = True

  def deactivate(self):
    self.popupwindow.hide_all()
    for (handler_id, window) in self.handler_ids:
      window.disconnect(handler_id)

  def update_ui(self):
    self.activate()
    self.popupwindow.hide_all()

  # The following 3 events all move the current cursor/ window
  # so we should hide our popup until they start typing again
  def window_state_event(self, event, param):
    self.popupwindow.hide_all()

  def configure_event(self, event, param):
    self.popupwindow.hide_all()

  def cut_paste_event(self, pointer):
    self.popupwindow.hide_all()


  # all the fun stuff is in here
  def changed(self, buf):
    # get current position and iter
    insert = buf.get_insert()
    current_iter = buf.get_iter_at_mark(insert)

    # start the haiku at most 2 lines above where
    # we are currently editing
    first_line_index = current_iter.get_line() - 2
    if first_line_index < 0: 
      first_line_index = 0

    line_index = [ first_line_index, first_line_index + 1, 
                   first_line_index + 2, first_line_index + 3,
                   first_line_index + 4, first_line_index + 5]

    # make sure all of our line indices are 
    # actually in the GtkTextBuffer
    line_count = buf.get_line_count()
    for x in range(6):
      if line_index[x] == current_iter.get_line():
        current_line_index_idx = x
      if line_index[x] > (line_count - 1):
        line_index[x] = (line_count - 1)

    # go through and grab each line of text
    lines = [ "", "", "", "", ""]
    for x in range(5):
      start_iter = buf.get_iter_at_line(line_index[x])
      end_iter = buf.get_iter_at_line(line_index[x+1])
      if start_iter.equal(end_iter):
        end_iter = buf.get_end_iter()
        lines[x] = buf.get_text(start_iter, end_iter)
        break
      lines[x] = buf.get_text(start_iter, end_iter).rstrip('\n')

    # find start of the haiku, which should be the
    # line after the first blank line above the current
    # line being edited 
    start_index = 0
    for x in [current_line_index_idx, 
              current_line_index_idx -1, 
              current_line_index_idx -2]:
      if x >= 0 and len(lines[x]) != 0:
        start_index = x
      else:
        break

    num_sy = [0, 0, 0]
    for x in range(3):
      index = start_index + x
      if index < 5:
        if len(lines[index]) == 0:
          num_sy[x] = 0
        else:
          num_sy[x] = self.guess_sy_count(lines[index])
      else:
        num_sy[x] = 0

    view = self.window.get_active_view()
    rect = view.get_iter_location(buf.get_end_iter())
    window = gtk.TEXT_WINDOW_TEXT
    x, y = view.buffer_to_window_coords(window, rect.x, rect.y)
    x, y = view.translate_coordinates(self.window, x, y)
    label_str = `num_sy[0]` + " " + `num_sy[1]` + " " + `num_sy[2]`
    self.label.set_text(label_str)
    root_x, root_y = self.window.get_position()
    # couple of magic numbers because 
    # I don't know GTK well enough to get the border sizes
    self.popupwindow.move(root_x+x+rect.width+25,root_y+y+rect.height+15)
    self.popupwindow.show_all()

  def guess_sy_count(self, line):
    mungedline = re.sub('\'','',line.lower())
    mungedline = re.sub(r'e\b',r' ',mungedline)
    # constlines added for things like "me", "he", "she"
    constlines = re.split('[\W]+',mungedline)
    constlines = [ x for x in constlines if (len(x) == 1 or len(x) == 2)]
    constlines = [ x for x in constlines if not re.search(r'[aeiouy]',x)]
    #print "mungedline: " + mungedline
    splitline = re.split(r'[^aeiouy]+', mungedline)
    splitline = [ x for x in splitline if (x != '') ] # hmm
    syllables = 0
    for i in self.SubSyl:
        if re.search(i,mungedline):
            syllables -= 1
    #print "syllables after subtraction: " + `syllables`
    for i in self.AddSyl:
        if re.search(i,mungedline):
            syllables += 1
    #print "syllables after addition: " + `syllables`
    if len(mungedline) == 1: 
      syllables =+ 1
      #print "syllables after length: " + `syllables`
    else:
      syllables += len(splitline)
      #print "syllables after vowel groups: " + `syllables`
    syllables += len(constlines)
    #print "syllables after const check: " + `syllables`
    if syllables == 0: syllables = 1
    #print "syllables after zero check: " + `syllables`
    return syllables
