# -*- coding: iso-8859-1 -*-
from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0)


# CAPITAL !!!!
import sys, os
sys.path.append(os.path.join(os.getcwd(), 'modules', 'scicalc'))

try:
    from API import *
    from GUI import *

except: #  (Psyco n'est pas compatible avec WxGeometrie 0.104+)
    try:
        import psyco;
        psyco.full()  # Accélération de l'exécution du code Python    
    except:
        pass


from tools.imports import *

from lib import *
import base.formater as formater
html = lazyModule('wx.html')

if wx.Platform == '__WXMSW__':
  iewin = lazyModule('wx.lib.iewin')


class SciCalcMenuBar(MenuBar):
  def __init__(self, panel):
    MenuBar.__init__(self, panel)
    self.ajouter("Fichier", ["quitter"])
    self.ajouter("Affichage", ["onglet"])
    self.ajouter("Outils",
      [u"Mémoriser le résultat", u"Copie le résultat du calcul dans le presse-papier, afin de pouvoir l'utiliser ailleurs.", "Ctrl+M", None],
      [u"Options", u"Paramètres de configuration", "Ctrl+O", self.panel.OnConfig])
    self.ajouter("?",
      [u"Aide", u"Index de l'aide en ligne", "F1", self.panel.help],
      [u"Encyclopédie", u"Index de l'encyclopédie en ligne", "Ctrl+F1", self.panel.wiki],
      [u"Historique des versions", u"Historique des versions", "Ctrl+H", self.panel.OnHistoSciCalc],
      [u"A propos...", "%s v %s" % (SciCalc.__titre__, version.getVersion()) + " (c) 2005 - License CeCILL", "Ctrl+A", self.panel.OnAbout],)

#------------------------------------------------------------------------------
class SciCalc(Panel_simple):
  __titre__ = "SciCalc"

  #----------------------------------------------
  def __init__(self, app):

    Panel_simple.__init__(self, app)

    self.application = app
    param.loadConfig()
    debug(param.debug, 'include path : [' + str(sys.path) + ']')
    debug(param.debug, 'scicalc_dir : [' + param.scicalc_dir + ']')

    self.createControls()
    self.optimizeSize()

    self.entree.Bind(wx.EVT_CHAR, self.EvtChar)
    wx.EVT_SIZE(self, self.OnSize)

    self.historique = [] # historique des entrées (ce qui a été saisi)
    self.position = -1
    self.entree.SetFocus()
    self.dernier_resultat = "" # dernier resultat, sous forme de chaine formatee pour l'affichage
    self.derniers_resultats = [] # liste des résultats, sous forme de valeurs numériques

  #----------------------------------------------
  def createControls(self):
    """
    Création des différents contrôles
    """
    # Ligne de commande
    self.entrees = wx.BoxSizer(wx.HORIZONTAL)
    self.entree = wx.TextCtrl(self, size = (-1, -1), style=wx.TE_PROCESS_ENTER)
    self.entrees.Add(self.entree, 1, wx.ALL | wx.GROW, 5)

    # "Ruban", affichage des résultats
    self.corps = wx.BoxSizer(wx.HORIZONTAL)
    self.resultats = wx.TextCtrl(self, size = (-1, -1), style = wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH)
    self.corps.Add(self.resultats, 1, wx.ALL | wx.EXPAND, 5)

    # Fenêtre d'aide
    if wx.Platform == '__WXMSW__':
      self.aide = iewin.IEHtmlWindow(self, -1, size=wx.Size(-1, -1), style = wx.NO_FULL_REPAINT_ON_RESIZE)
    else:
      self.aide = html.HtmlWindow(self, -1, size=wx.Size(-1, -1))
    self.corps.Add(self.aide, 1, wx.ALL | wx.EXPAND, 5)

  #----------------------------------------------
  def optimizeSize(self):
    """
    Optimisation de la taille de la frame en fonction des contrôles
    """
    # Mise en forme finale
    self.sizer = wx.BoxSizer(wx.VERTICAL)
    self.sizer.Add(self.entrees, 0, wx.ALL | wx.EXPAND, 5)
    self.sizer.Add(self.corps, 1, wx.ALL | wx.EXPAND, 5)
    self.SetSizer(self.sizer)
    self.Fit()

  #----------------------------------------------
  def OnSize(self, evt):
    """
    Gestion de l'évènement OnSize
    """
    size = self.application.GetSize()
    point = self.sizer.GetPosition()
    self.sizer.SetDimension(point.x, point.y, size.width, size.height)

  #----------------------------------------------
  def OnHelp(self, evt):
    """
    Gestion de l'évènement OnHelp (id=601)
    """
    self.help(topic='all')

  #----------------------------------------------
  def OnWiki(self, evt):
    """
    Gestion de l'évènement OnWiki (id=602)
    """
    self.wiki(topic='')

  #----------------------------------------------
  def OnHistoSciCalc(self, evt):
    """
    Gestion de l'évènement OnHistoSciCalc (id=604)
    """
    path = os.path.split(__file__)[0]
    filename = os.path.join(path, 'histo', 'main.html')
    webbrowser.open(filename)

  #----------------------------------------------
  def OnConfig(self, evt):
    """
    Gestion de l'évènement OnAbout (id=603)
    """
    from base.config import MyConfigScreen

    myConfig = MyConfigScreen(self)
    myConfig.show()
    param.loadConfig()

  #----------------------------------------------
  def OnAbout(self, evt):
    """
    Gestion de l'évènement OnAbout (id=603)
    """
    from base.about import MyAboutScreen

    myAbout = MyAboutScreen(self)
    myAbout.show()

  #----------------------------------------------
  def OnDelResult(self, evt):
    """
    Gestion de l'évènement OnDelResult
    """
    self.derniers_resultats = [] # liste des résultats, sous forme de valeurs numériques
    self.resultats.Clear()

  #----------------------------------------------
  def OnDelHisto(self, evt):
    """
    Gestion de l'évènement OnDelHisto
    """
    self.historique = [] # historique des entrées (ce qui a été saisi)
    self.position = -1

  #----------------------------------------------
  def OnCopyClipboard(self, event = None):
    """
    Copie le dernier résultat dans le presse papier
    """
    self.clipBoard=wx.Clipboard()
    if self.clipBoard.Open():
      self.clipBoard.AddData(wx.TextDataObject(self.dernier_resultat))
    self.clipBoard.Close()

  #----------------------------------------------
  def help(self, topic=None):
    """
    help(topic)

    Affiche l'aide de la fonction / classe / objet passé en paramètre.

    Exemple :
    help(quadratic) -> affiche l'aide de la fonction quadratic (dans SciCalc)
    help(circle) -> affiche l'aide de la classe circle (dans SciCalc)
    help(all) -> affiche (dans le navigateur par défaut) la liste des fonctions que l'on peut utiliser avec SciCalc
    """
    path = os.path.split(__file__)[0]
    if topic == 'all' or topic == '':
      filename = os.path.join(path, 'help', 'main.html')
      webbrowser.open(filename)
      return

    if topic == None:
      topic = self.help

    h = Help.Help()
    d = h.dir(topic)
    if d == '':
      filename = os.path.join(path, 'help', 'main.html')
      webbrowser.open(filename)
    else:
      filename = os.path.join(path, 'help', d, topic + '.html')
      if wx.Platform == '__WXMSW__':
        self.aide.LoadUrl(filename)
      else:
        self.aide.LoadPage(filename)

  #----------------------------------------------
  def wiki(self, topic=None):
    """
    wiki(topic)

    Affiche dans le navigateur par défaut de nombreuses informations sur la fonction / classe / objet passé en paramètre.
    """
    path = os.path.split(__file__)[0]
    if topic == '':
      filename = os.path.join(path, 'wiki', 'main.html')
      webbrowser.open(filename)
    else:
      try:
        h = Wiki.Wiki()
        d = h.dir(topic)
        if d == '':
          filename = os.path.join(path, 'wiki', 'main.html')
        else:
          filename = os.path.join(path, 'wiki', d, topic + '.html')
        webbrowser.open(filename)
      except NameError, WindowsError:
        filename = os.path.join(path, 'wiki', 'main.html')
        webbrowser.open(filename)


  #----------------------------------------------
  def ans(self, n = -1):
    """
    ans(...)

    Retourne le calcul n°n de la liste des résultats.
    Si n vaut -1 (valeur par défaut), c'est le dernier qui est retourné.

    Exemples :
    ans() -> le dernier
    ans(2) -> le deuxième
    si res = array([ 6.,  5.])
      ans()[1] -> res : 5.0
    """
    if self.derniers_resultats:
      if n == -1:
        return self.derniers_resultats[len(self.derniers_resultats)-1]
      else:
        return self.derniers_resultats[n-1]
    self.message += " ans(): aucun calcul anterieur."
    return 0

  #----------------------------------------------
  def evaluer(self, calcul):
    """
    Evalue l'expression passée en paramètre
    """
    debug(param.debug, "\n" + "expression : %s" % calcul)
    try:
      # L'appel à l'aide en ligne est traitée à part du processus normal d'évaluation des expressions
      l = re.split("^(help[(])([a-zA-Z0-9]*)([)])", calcul)
      if len(l) >= 3:
        if l[1] == 'help(':
          self.help(l[2])
          return;

      # L'appel à l'encyclopédie en ligne est traitée à part du processus normal d'évaluation des expressions
      l = re.split("^(wiki[(])([a-zA-Z]*)([)])", calcul)
      if len(l) >= 3:
        if l[1] == 'wiki(':
          self.wiki(l[2])
          return;

      # Pour multiplier par 2 le dernier resultat, il suffit de taper "*2", le ans() s'insere avant.
      if calcul[0] in "*/^%+":
        calcul = "ans()" + calcul

      # On remplace toutes les occurences de ans(...) par self.ans(...)
      # Ex : <espace>ans()*2*ans()+radians(180)+ans() --> 2*self.ans()+radians(180)+self.ans()
      calcul = ' ' + calcul
      l = re.finditer("(\Wans[(])([0-9]*)([)])", calcul)
      iter = 0
      for x in l:
        pos = x.span()
        calcul = calcul[0:pos[0] + (iter * 5)] + x.group().replace("ans(", "self.ans(") + calcul[pos[1] + (iter * 5):]
        iter += 1

      # Pour le cas où ans est devant.
      if calcul[0:3] == "ans(":
        calcul = "self." + calcul

      # Ferme automatiquement les parentheses.
      parentheses = "({[", ")}]"
      for i in range(3):
        correction = parentheses[1][i]*(calcul.count(parentheses[0][i])-calcul.count(parentheses[1][i]))
        calcul = calcul + correction
        if correction:
          self.message(" Attention, il manque des parentheses \"" + parentheses[1][i] + "\".")

      debug(param.debug, "expression re-evaluee: %s" % calcul)
      resultat = eval(calcul)
    except NameError, (text):
      self.message("Fonction non définie: %s" % text)
      raise
    except ZeroDivisionError, (text):
      self.message("Division par 0: %s" % text)
      raise
    except:
      self.message("Calcul impossible.")
      sys.excepthook(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])
      raise
    else:
      return resultat

  #----------------------------------------------
  def affiche(self, event = None):
    """
    Affiche le résultat de l'opération
    """
    entree = self.entree.GetValue()

    # Pour le cas où l'on ne saisisse que help / wiki / ans sans les parenthèses
    if entree in ('help', 'wiki', 'ans'):
      entree = entree + '()'

    try:
      resultat = self.evaluer(entree)
      self.historique.append(entree)
      self.position = len(self.historique) - 1
      if type(resultat) == type(None):
        return

      self.derniers_resultats.append(resultat)
      if type(resultat) != type(None):
        res = formater.Formater(resultat)
        self.dernier_resultat = str(res)
        debug(param.debug, "résultat : %s" % res)
        self.resultats.AppendText("--- " + str(len(self.derniers_resultats)) +
          " ----------------------------------------------\n" + \
          "expr : " + entree + "\n" + \
          "res : " + str(res) + "\n\n")
      self.message("")
      self.entree.Clear()
      longueur = len(self.resultats.GetValue())
      self.resultats.SetInsertionPoint(longueur)
      self.resultats.SetFocus()
      self.entree.SetFocus()
    except:
      self.entree.SetFocus()
      if param.debug:
        raise

  #----------------------------------------------
  def EvtChar(self, evt):
    """
    Gestionnaire des évènements clavier
    """
    code = evt.GetKeyCode()

    if code == 13:
      self.affiche()
    elif code == 317:   # on remonte dans l'historique des entrees
      if self.position >= 0:
        self.entree.SetValue(self.historique[self.position])
        self.position = self.position - 1
        self.entree.SetInsertionPoint(len(self.entree.GetValue()))
    elif code == 319:   # on redescend dans l'historique vers les entrees recentes
      if self.position < len(self.historique) - 1:
        if self.position == (len(self.historique) - 2):
          self.entree.Clear()
        else:
          self.entree.SetValue(self.historique[self.position + 2])
          self.entree.SetInsertionPoint(len(self.entree.GetValue()))
        self.position = self.position + 1
    else:
      evt.Skip()
