#!/usr/bin/env python
#
# File: gmpm/pympm.py
# This file is part of the Gmpm-Project, a graphical MiKTeX Package Manager frontend.
#
# Copyright (C) 2008 Juan Fiol
# This is free software.  You may redistribute copies of it under the terms of
# the GNU General Public License version 2.
# There is NO WARRANTY, to the extent permitted by law.
#
# Written by Juan Fiol <juanfiol@gmail.com>

# Python version of MPM (MikTeX Package Manager)
_ = lambda x : x

import sys

try:
  from os import system, getenv, environ
  import os
  import os.path
  import mpmxml
except ImportError, e:
  print "Missing standard modules", e
  sys.exit(1)

try:
  from mpmpack import Package
  import helper
  from gmpm.constants import CONFIG_LOCATION, SETTINGS_LOCAL_DIR
except ImportError, e:
  print "Missing own modules", e
  sys.exit(1)



class MiKTeX_config(object):
  """ Class used to manage the configuration of a MiKTeX distribution """

  # full_conf is used to keep all configuration parameters (currently MiKTeX + gui)
  full_conf= {} 
  # conf is Used to keep MiKTeX configuration parameters
  conf={}  

  mpmsection   = 'mpm'

  def __init__(self, file=None, config=None):
    """  
    file is configuration file with distribution parameters.
    If not file has been parsed, set and save default ini file

    config is a configuration object.
    Values provided in config overwrite those from ini file
    """

    self.default_ini=os.path.join(getenv('HOME') , SETTINGS_LOCAL_DIR, "gmpm.cfg")
    if not os.access(os.path.join(getenv('HOME') , SETTINGS_LOCAL_DIR), os.F_OK):
      os.mkdir(os.path.join(getenv('HOME') , SETTINGS_LOCAL_DIR))

    self.inifiles=[CONFIG_LOCATION, self.default_ini]
    if file:
      self.inifiles.append(file)

    self.inifiles, self.full_conf= self.load_MiKTeX_config()
    if self.inifiles:
      self.conf= self.full_conf[self.mpmsection]

    default_repositories=["http://ctan.localhost.net.ar/systems/win32/miktex/tm/packages"," ftp://ftp.dante.de/pub/tex/systems/win32/miktex/tm/packages"]

    #     If not file has been parsed, set and save default ini file
    if not self.inifiles:
      self.inifiles=[self.default_ini]
      # Load Default values
      self.conf['http_proxy']= getenv('http_proxy') or getenv('HTTP_PROXY') 
      self.conf['ftp_proxy'] = getenv('FTP_PROXY') or getenv('ftp_proxy')
      self.conf['all_proxy'] = getenv('ALL_PROXY') or getenv('all_proxy')
      self.conf['no_proxy']  = getenv('NO_PROXY') or getenv('no_proxy')
      self.full_conf[self.mpmsection]= self.conf
      # Save ini file
      self.save_MiKTeX_config(self.default_ini)


    # Values provided in config overwrite those from ini file
    if config:  #  Set initially to the values given
      for k,v in config.full_conf[self.mpmsection].iteritems():
        self.conf[k]= v

    # Load Default values
    if not self.conf.has_key('root'):
      self.conf['root']      = '/usr/local/share/texmf'
    if not self.conf.has_key('repository'):
      self.conf['repository']= getenv('MIKTEX_REPOSITORY',
                                      'ftp://ftp.dante.de/pub/tex/systems/win32/miktex/tm/packages ')

    if not self.conf.has_key('repositories'):
      self.conf['repositories']= self.get_attribute('repository')+'\n'  # list of repositories

    for r in default_repositories:
      if self.conf['repositories'].find(r) == -1:
        self.conf['repositories']+= ','+r

    if type(self.conf['repositories']) == str:
      self.conf['repositories']= self.conf['repositories'].replace('\n','').replace('"','').replace("'","").replace('[','').replace("]",'')
      r= self.conf['repositories'].split(',')
      repositories=[]
      for l in r:
        repositories.append(l.strip())
      self.repositories= repositories
    else:
      print '\n\n\nRepository no string !!!!\n\n\n'
      self.repositories= self.get_attribute('repositories')

    self.full_conf[self.mpmsection]=self.conf

      

  def get_attribute(self, atribute):
    if self.conf.has_key(atribute) and self.conf[atribute] != '' and self.conf[atribute] != 'None' :
      return self.conf[atribute]
    return None

  def set_attribute(self, atribute, value):
    self.conf[atribute]=value

  def remove_attribute(self, atribute):
    if self.conf.has_key(atribute):
      del self.conf[atribute] 


  def load_MiKTeX_config(self):
    """ 
    Loads data from configuration file, setting several parameters needed to mantain the distribution.

    Configuration is stored in the dictionary "conf"
    """
    f, c= helper.Load_ini(self.inifiles)
    if c:
      c0={}
      c1= c[self.mpmsection]
      for k,v in c1.iteritems():
        if str(v).strip() != "" and str(v).strip() != "None":
          c0[k]= v
      c[self.mpmsection]= c0
    return f,c

  def save_MiKTeX_config(self, filename=None):
    """
    Save MiKTeX configuration (plus any extra configuration)
    """
    if not filename:
      filename= self.default_ini
    helper.Write_ini(filename, self.full_conf)
    
  def update_repositories(self):
    """
    Dummy implementation: Now call mpm and parses its output. 
    mpm is not working for me, so I have to try something better.
    Probably direct implementation of list-repositories
    """

    comando= 'mpm --quiet --list-repositories > tmp123out.txt'
    helper.run_comando(comando, self.ask_pass_func, True)

    f=open('tmp123out.txt','r')
    l=[]
    for line in f:
      line= line.replace('\n','').strip()
      l.append(line)
    if len(l) > 0:
      self.repositories=l
    f.close()
#     system('rm tmp123out.txt')

  def pick_repository_url(self):
    comando= 'mpm  --pick-repository-url > tmp98.txt'
    helper.run_comando(comando, self.ask_pass_func, True)
    f=open('tmp98.txt','r')
    l=f.read().replace('\n','')
    f.close()
    system('rm -f tmp98.txt')
    return l

class MiKTeX_Distrib(object):
  """ Class to hold information about the state of a MikTeX distribution """

  mpmcommand="mpm "
  valid= True

  def __init__(self, config=None, dict=None, inifile=None):
    """ 
     
    Class implementing all the information and methods to mantain a MikTeX distribution.

    If initialized with a dictionary "dict", it must have:
    key= "name of package"
    value= dict, with dict[property]=val.

    For instance:
      dict['name']="name of package"
    """

    """ Initial values for directories where to find the distribution """
    self.conf= MiKTeX_config(inifile, config=config)   # Reads a file (if it exist)

    self.ask_pass_func= helper.cli_ask_pass_func
    self.conf.ask_pass_func= self.ask_pass_func

    self.set_root_install(self.conf.get_attribute('root'))
    self.set_repository()
    self.set_proxy()

    # Here dict is a dictionary with keys= "name of package", and value= package instance
    # If not dict given load from MikTeX files
    if dict == None:
      self.dist=self.load_miktex_dist()
    else:
      self.dist= dict

    
  def clear_miktex_dist(self):
    for k in self.dist.keys():
      del self.dist[k]

  def load_miktex_dist(self):
    """ Loads a Complete MikTeX distribution and returns a dictionary with
    keys = package_name
    values = dict
    where dict is a dictionary that can be used to initialize a mpm Package
    """
    s={}

    cdir= self.conf.get_attribute('config_dir')
    try:
      f,s= helper.Load_ini(cdir + 'mpm.ini')
      f,s= helper.Load_ini(cdir + 'packages.ini',s)
    except:
      return {}
    t=[]
    for k,v in s.iteritems():

      xmlFile= self.conf.get_attribute('tpm_dir') + k + '.tpm'
      w=mpmxml.PackageXML(xmlFile).Parse()

      for kk,vv in w.iteritems():
        if not s[k].has_key(kk):  # Do not overwrite the information
          s[k][kk]=vv
      #     # CLEANING BECAUSE THERE IS AN UNIDENTIFIED PROBLEM !!!
      if s[k]['name']=="":
        t.append(k)
      else:
        # WARNING:
        # This line appears because for some packages the Name tag 
        # is not the same that the section in the ini file.
        # Following mpm we want the section
        s[k]['name']= k

    for k in t:
      del s[k]

    p={}
    for k,v in s.iteritems():
      p[k]=Package(v)
    return p

  def set_root_install(self, root=None):
    if root:
      conf_dir= os.path.join(root, "miktex/config/")
      tpm_dir=  os.path.join(root,"tpm/packages/")

#     self.valid= os.access(root,os.F_OK) and os.access(conf_dir,os.F_OK) and os.access(tpm_dir,os.F_OK)
    self.valid= os.access(root,os.F_OK)
    if self.valid:
      self.conf.set_attribute('root',root)
      self.conf.set_attribute('tpm_dir', tpm_dir)
      self.conf.set_attribute('config_dir', conf_dir )
      if not os.access(conf_dir,os.F_OK):
        os.makedirs(conf_dir)
      if not os.access(tpm_dir,os.F_OK):
        os.makedirs(tpm_dir)        
      self.permission_ok= os.access(root,os.W_OK) and os.access(conf_dir,os.W_OK) and os.access(tpm_dir,os.W_OK)

    self.update_mpm_cmd_line('--install-root=',self.conf.get_attribute('root'))


  def refresh_list_packages(self):
    self.pending_oper={}
    self.clear_miktex_dist()
    self.dist= self.load_miktex_dist()
    npack= len(self.dist.keys())
    return npack

  def list_pack_names(self):
    d=self.dist.keys()
    d.sort()
    return d

  def list_packs(self):
    packs=[]
    for p in self.list_pack_names():
      packs.append(self.dist[p])
    return packs


  def list(self, dist=None, verbose=False):
    if dist==None:
      dist= self.dist

    if dist=={}:
      return ""

    s=""
    for pack in self.list_packs():
      val=pack.print_values()
#       s+= "%s  %5d %-12d%-18s\n" % val[:-1]
      s+= "%s  %5d %-12d%-18s %s\n" % val
      if verbose:
        s+=  pack.get_property('description') + 2*"\n"
    return s

  def list_installed(self, dist=None, verbose=False):
    if dist==None:
      dist= self.dist

    if dist=={}:
      return ""

    s=""
    for pack in self.list_packs():
      if pack.is_installed():
        val=pack.print_values()
        s+= "%s  %5d %-12d%-18s %s\n" % val
      if verbose:
        s+=  pack.get_property('description') + 2*"\n"
    return s

  
  def list_values(self, dist=None, verbose=False):
    if dist==None:
      dist= self.dist

    if dist=={}:
      return ()
    
    s=[]
    for packs in self.list_packs():
      val=pack.print_values()
      if verbose:
        val.append(pack.get_property('description'))
      s.append(val)
    return s


  def get_info(self, pack, verbose=False):
    try:
      p= self.dist[pack]
      s= p.print_info(verbose=verbose)
      return s
    except:
      return None


  def print_info(self, pack, verbose=False):
    try:
      p= self.dist[pack]
      return p.print_info(verbose=verbose)
    except:
      return "Unknown package"


  def get_package_property(self, name, property):
    try:
      p= self.dist[name]
    except:
      return "Unknown package"
    return p.get_property(property)

  def retrieve_package(self, name):
    try:
      return self.dist[name]
    except:
      return None

  
  def update_db(self):

    # Should be implemented diferently... Not writing to a named file!
    comando= self.mpmcommand + ' --verbose --update-db > tmp1234out.txt 2>&1'
    helper.run_comando(comando, self.ask_pass_func, self.permission_ok)
    try:
      f=open('tmp1234out.txt','r')
      t=f.read()
      resp= 'mpm:' not in t
      f.close()
      system('rm -f tmp1234out.txt')
      return t,resp
    except:
      return "Unknown problem",False

  def update_repositories(self):
    """
    Dummy implementation: Now call mpm and parses its output. 
    mpm is not working for me, so I have to try something better.
    Probably direct implementation of list-repositories
    """
    self.conf.update_repositories()


  def set_onlyone_proxy(self, proxy):
    """ Set the same proxy for all connection types

    Implementation:
    Sets the ALL_PROXY environment variable and unset all the others """

    environ['ALL_PROXY']=proxy
    environ['all_proxy']=proxy
    t=('http_proxy','ftp_proxy')
    for s in t:
      if environ.has_key(s):
        del environ[s]
      if environ.has_key(s.upper()):
        del environ[s.upper()]

    self.conf.set_attribute('all_proxy',proxy)
    self.conf.remove_attribute('http_proxy')
    self.conf.remove_attribute('ftp_proxy')


  def set_proxy(self, proxies= None):
    """
    Set the proxies from the given dictionary.  At the moment we set the environment
    variables if no proxies argument is given. We set the proxies as given by the
    configuration
    """
    if proxies:
      for t,p in proxies.items():
        if p :
          if p!="" and p != 'None':
            self.conf.set_attribute(t.lower(),p)
          else:
            self.conf.remove_attribute(t.lower())

    proxies= ['http_proxy','ftp_proxy','all_proxy','no_proxy']
    for t in proxies:
      p= self.conf.get_attribute(t)
      if p:
        self.conf.set_attribute(t,p)
        environ[t]=p
        environ[t.upper()]=p
      else:
        if environ.has_key(t):
          del environ[t]
        if environ.has_key(t.upper()):
          del environ[t.upper()]

        
  def set_repository(self, repository=None):
    if repository:
      self.conf.set_attribute('repository',repository)
    environ['MIKTEX_REPOSITORY']= self.conf.get_attribute('repository')


  def update_mpm_cmd_line(self, text,value):
    t= self.mpmcommand.find(text)
    if t==-1:
      self.mpmcommand += ' '+text + value
    else:
      self.mpmcommand= self.mpmcommand[:t] + ' '+text + value


  def norm_dist_path(self, Path):
    """ Return the absolute path of a file, replacing texmf by the root-directory """
    int_path= os.path.normpath(Path.replace('texmf',''))

    
  # ########################################################################################
  def search(self, substring, search_desc= 0):
    (S_TITLE,S_DESC, S_FILES, S_FULL)= range(4)
    if self.dist == {}:
      return None

    dic={}
    s=substring.lower()
    fields=['name','title']
    if search_desc == S_DESC or search_desc == S_FULL:
      fields += ['description']

    for pack in self.list_packs():
      found= False
      n= pack.get_property('name').lower()

      for f in fields:
        t= pack.get_property(f).lower()
        found= found or (t.find(s) != -1)

      if search_desc == S_FILES or search_desc == S_FULL:
        for f in pack.get_property('runfiles'):
          found= found or (s in f)

      if found:
        dic[n]=pack

    # Create a new instance and return it
    d= MiKTeX_Distrib(config=self.conf, dict=dic) 
    return d

  def get_package_help(self, pack):
    if type(pack) is str:
      system("mthelp " + pack)
    else:
      system("mthelp " + pack.get_property('name'))

  def install_package(self, pack):
    if type(pack) is str:
      name= pack
      pack= self.retrieve_package(name)
    else:
      name= pack.get_property('name')
    comando= self.mpmcommand.strip() + ' --install='+ name
    print comando
    helper.run_comando(comando, self.ask_pass_func, self.permission_ok)
    # TODO: Parsing of mpm output and return false or true
    return True

  def uninstall_package(self, pack):
    if type(pack) is str:
      name= pack
      pack= self.retrieve_package(name)
    else:
      name= pack.get_property('name')
    comando= self.mpmcommand + ' --uninstall='+ name
    print comando
    helper.run_comando(comando, self.ask_pass_func, self.permission_ok)
    return True


  def update_package(self, pack):
    if type(pack) is str:
      name= pack
    else:
      name= pack.get_property('name')

    comando= self.mpmcommand.strip() + ' --update='+ name + ' | grep reinstalled > tmpv123.dat'
    helper.run_comando(comando, self.ask_pass_func, self.permission_ok)
    os.remove('tmpv123.dat')
    # TODO: Parsing of mpm output and return false or true
    return True

  def install_some(self, filename):
    if os.path.exists(filename):
      comando= self.mpmcommand + ' --install-some='+ filename
      helper.run_comando(comando, self.ask_pass_func, self.permission_ok)
    # TODO: Parsing of mpm output and return false or true
      return True
    else:
      print filename+' does not exist'
    return False

  def update_some(self, filename):
    if os.path.exists(filename):
      comando= self.mpmcommand + ' --update-some='+ filename
      helper.run_comando(comando, self.ask_pass_func, self.permission_ok)
    # TODO: Parsing of mpm output and return false or true
      return True
    else:
      print filename+' does not exist'
    return False
    

  def verify_package(self, pack):
    if type(pack) is str:
      name= pack
    else:
      name= pack.get_property('name')
    comando= self.mpmcommand + ' --verify='+ name + ' 2> /dev/null | grep reinstalled > tmpv123.dat'
    helper.run_comando(comando, self.ask_pass_func, True)
   

    t= os.path.getsize('tmpv123.dat') == 0
    os.remove('tmpv123.dat')
    return t


  def find_all_updates(self):
    packs={}
    for p in self.list_packs():
      if p.is_updatable():
        packs[p.get_property('name')]= p
    return packs




##########################################################################################
##########################################################################################
##########################################################################################

if __name__ == "__main__":
  main()
### Local Variables: 
### tab-width: 2
### END:
