""" Converter Deskbar
A plugin for the Gnome deskbar applet that converts from one thing to another.
Stuart Langridge, http://www.kryogenix.org/

Licenced under the GNU GPL, version 2.0

Changes:
  2008-02-11 by Johannes Buchner:
  - Added 2.20-compatibility
  - used CopyToClipboard from Calculator

"""

from gettext import gettext as _

import urllib, os, re, subprocess
import gtk
import deskbar
from deskbar.handlers.actions.CopyToClipboardAction import CopyToClipboardAction

import deskbar.core.Utils
import deskbar.interfaces.Module
import deskbar.interfaces.Match


CONVERTER_ICON = [
"16 16 8 1",
"       c None",
".      c #000000",
"+      c #0000B0",
"@      c #00B000",
"#      c #B00000",
"$      c #B0FFB0",
"%      c #FFB0B0",
"&      c #FFFFFF",
"                ",
"    .           ",
"   .%.  +++     ",
"  .%%#.  &&+    ",
" .%%%##.   +&   ",
"  .%%#.&  +++   ",
"   .%.&    +&&  ",
"    .&      &   ",
"           .    ",
"          .$.   ",
"         .$$@.  ",
"        .$$$@@. ",
"         .$$@.& ",
"          .$.&  ",
"           .&   ",
"                "
]


HELP_TEXT = _("""Converter requires the GNU units program. 
Download from http://www.gnu.org/software/units/units.html
 or install your distribution's package ('units' in Ubuntu).""")
 
 
r_normal = re.compile("""
^                          # start string
\s*                        # optional whitespace
(?P<number>[0-9.]+)        # a number
\s*                        # optional whitespace
(?P<unit1>[^\s]+)          # unit1
\s+ in \s+                 # " in " (with compulsory whitespace)
(?P<unit2>[^\s]+)          # unit2
\s*                        # optional whitespace
$                          # end""", re.VERBOSE | re.IGNORECASE)

r_currency = re.compile("""
^                          # start string
\s*                        # optional whitespace
(?P<unit1>[£\$]+)          # unit1, a $ or a £
\s*                        # optional whitespace
(?P<number>[0-9.]+)        # a number
\s+ in \s+                 # " in " (with compulsory whitespace)
(?P<unit2>[^\s]+)          # unit2
\s*                        # optional whitespace
$                          # end""", re.VERBOSE | re.IGNORECASE)

CURRENCIES = {
  "GBP": ["pound", "pounds", "£"],
  "USD": ["dollar", "dollars", "$"],
}


def _on_more_information(x):
  deskbar.Utils.more_information_dialog(None,_("Getting GNU units"),HELP_TEXT)

def _check_requirements(*args):
  if not True in [os.path.isfile(os.path.join(x,'units')) 
                  for x in os.environ["PATH"].split(":")]:
    return (deskbar.Handler.HANDLER_HAS_REQUIREMENTS,
            "Converter requires the GNU units program", 
            _on_more_information)
  return (deskbar.Handler.HANDLER_IS_HAPPY, None, None)

HANDLERS = ["ConverterModule"]

class ConverterAction (CopyToClipboardAction):

    def __init__ (self, text, answer):
        CopyToClipboardAction.__init__ (self, answer, answer)
        self.text = text
    
#    def get_verb(self):
#        return _("Copy <b>%(name)s</b> to clipboard")

    def get_name(self, text = None):
        """Because the text variable for history entries contains the text
        typed for the history search (and not the text of the orginal action),
        we store the original text seperately."""
        result = CopyToClipboardAction.get_name (self, text)
        result["origtext"] = self.text
        return result

class ConverterMatch (deskbar.interfaces.Match):

    def __init__ (self, text, answer):
        deskbar.interfaces.Match.__init__ (self, name = text,
                icon = "gtk-add", category = "calculator")
        self.answer = answer
        self.add_action (ConverterAction (text, answer))

    def get_hash (self):
        return self.answer


class ConverterModule(deskbar.interfaces.Module):
  INFOS = {"icon": deskbar.core.Utils.load_icon ("gtk-add"),
     "name": _("Converter"),
     "description": _("Convert one quantity to another"),
     "version" : "2.2", 
     "categories" : { "calculator" : { "name" : _("Calculator") }}}
  
  def __init__(self):
    deskbar.interfaces.Module.__init__ (self)
    self._icon = gtk.gdk.pixbuf_new_from_xpm_data(CONVERTER_ICON)

  def parse_query(self,q):
    # check if query has one of the following formats:
    # "2 <unit1s> in <unit2>"
    # "2<unit1s> in <unit2>"
    # "<special_unit1>2 in <unit2>"
    # where special_unit1 is a $ or a £, to handle currency conversions
    normal = r_normal.match(q)
    if normal:
      d = normal.groupdict()
      return d
    else:
      currency = r_currency.match(q)
      if currency:
        d = currency.groupdict()
        return d

  def run_query(self,q):
    items = self.parse_query(q)
    if items:
      # First, try shelling out to the units program to do the conversion
      try:
        r = subprocess.Popen("units -t '%(number)s %(unit1)s' '%(unit2)s'" % items, 
                             shell=True, stdout=subprocess.PIPE)
        oline = r.stdout.readlines()[0].strip()
        if oline.startswith('Unknown unit'): raise "Unknown unit"
        if oline.startswith('conformability error'): raise "Conformability"
        return "%s %s = %s %s" % (items['number'],items['unit1'], 
                                  oline, items['unit2'])
      except:
        # It failed. Fix up currencies
        for curcode, curnames in CURRENCIES.items():
          for curname in curnames:
            if items['unit1'] == curname: items['unit1'] = curcode
            if items['unit2'] == curname: items['unit2'] = curcode
        try:
          r = subprocess.Popen("units -t '%(number)s %(unit1)s' '%(unit2)s'" % items, 
  	                           shell=True, stdout=subprocess.PIPE)
          oline = r.stdout.readlines()[0].strip()
          if oline.startswith('Unknown unit'): raise "Unknown unit"
          if oline.startswith('conformability error'): raise "Conformability"
          return "%s %s = %.2f %s" % (items['number'],items['unit1'], 
                                      float(oline), items['unit2'])
        except:
          return None
    
  def query(self, qstring, defres=None):
    ret = self.run_query(qstring)
    
    if ret:
      self._emit_query_ready (qstring, [ConverterMatch(qstring.strip(), ret)])
      return ret
    else:
      return []
      

