# -*- coding: utf-8 -*-

# Copyright (c) 2003 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Package implementing various functions/classes needed everywhere within eric3. 
"""

import os
import sys
import re
import string
import fnmatch

from qt import QRegExp, QString

coding_re = re.compile(r"coding[:=]\s*([-\w_.]+)")
UTF8_BOM = '\xef\xbb\xbf'

def get_coding(text):
    """
    Function to get the coding of a text.
    
    Arguments
    
        text -- text to inspect (string)
        
    Returns
    
        coding string
    """
    for l in text.splitlines()[:2]:
        m = coding_re.search(l)
        if m:
            return m.group(1)
    return None

def decode(text):
    """
    Function to decode a text.
    
    Arguments
    
        text -- text to decode (string)
        
    Returns
    
        decoded text
    """
    try:
        if text.startswith(UTF8_BOM):
            # UTF-8 with BOM
            return unicode(text[3:], 'utf-8'), 'utf-8-bom'
        coding = get_coding(text)
        if coding:
            return unicode(text, coding), coding
    except (UnicodeError, LookupError):
        pass
    # Assume Latin-1
    return unicode(text, "latin-1"), 'latin-1-guessed'

def encode(text, orig_coding):
    """
    Function to encode a text.
    
    Arguments
    
        text -- text to encode (string)
        
        orig_coding -- type of the original coding (string)
        
    Returns
    
        encoded text
    """
    if orig_coding == 'utf-8-bom':
        return UTF8_BOM + text.encode("utf-8")
    # Try saving as ASCII
    try:
        return text.encode('ascii')
    except UnicodeError:
        pass
    # Try declared coding spec
    coding = get_coding(text)
    try:
        if coding:
            return text.encode(coding)
    except (UnicodeError, LookupError):
        # Error: Declared encoding is incorrect
        pass
    try:
        if orig_coding == 'latin-1-guessed':
            return text.encode('latin-1')
    except UnicodeError:
        pass
    # Save as UTF-8 without BOM
    return text.encode('utf-8')
    
def normcasepath(path):
    """
    Function returning a path, that is normalized with respect to its case and references.
    
    Arguments
    
        path -- file path (string)
    """
    return os.path.normcase(os.path.normpath(path))
    
def normabspath(path):
    """
    Function returning a normalized, absolute path.
    
    Arguments
    
        path -- file path (string)
    """
    return os.path.abspath(path)
    
def normjoinpath(a, *p):
    """
    Function returning a normalized path of the joined parts passed into it.
    
    Arguments
    
        a -- first path to be joined (string)
        
        p -- variable number of path parts to be joind (string)
    """
    return os.path.normpath(os.path.join(a, *p))
    
def normabsjoinpath(a, *p):
    """
    Function returning a normalized, absolute path of the joined parts passed into it.
    
    Arguments
    
        a -- first path to be joined (string)
        
        p -- variable number of path parts to be joind (string)
    """
    return os.path.abspath(os.path.join(a, *p))
    
def isinpath(file):
    """
    Function to check for an executable file.
    
    Arguments
    
        file -- filename of the executable to check (string)
    
    Returns
    
        flag to indicate, if the executable file is accessible
        via the searchpath defined by the PATH environment variable.
    """
    if os.path.isabs(file):
        return os.access(file, os.X_OK)
        
    path = os.getenv('PATH')
    
    # environment variable not defined
    if path is None:
        return 0
        
    dirs = string.split(path, os.pathsep)
    for dir in dirs:
        if os.access(os.path.join(dir, file), os.X_OK):
            return 1
            
    return 0

def samepath(f1, f2):
    """
    Function to compare two paths.
    
    Arguments
    
        f1 -- first path for the compare (string)
        
        f2 -- second path for the compare (string)
    
    Returns
    
        flag indicating whether the two paths represent the
        same path on disk.
    """
    if f1 is None or f2 is None:
        return 0
        
    if normcasepath(f1) == normcasepath(f2):
        return 1
        
    return 0
        
def direntries(path, filesonly=0, pattern=None, followsymlinks=1):
    """
    Function returning a list of all files and directories.
    
    Arguments
    
        path -- root of the tree to check
        
        filesonly -- flag indicating that only files are wanted
        
        pattern -- a filename pattern to check against
        
        followsymlinks -- flag indicating whether symbolic links
            should be followed

    Returns
    
        list of all files and directories in the tree rooted
        at path. The names are expanded to start with path. 
    """
    if filesonly:
        files = []
    else:
        files = [path]
    entries = os.listdir(path)
    for entry in entries:
        if entry in ['CVS', '.svn']:
            continue
            
        fentry = os.path.join(path, entry)
        if pattern and \
        not os.path.isdir(fentry) and \
        not fnmatch.fnmatch(entry, pattern):
            # entry doesn't fit the given pattern
            continue
            
        if os.path.isdir(fentry):
            if os.path.islink(fentry) and not followsymlinks:
                continue
            files += direntries(fentry, filesonly, pattern)
        else:
            files.append(fentry)
    return files

def parseOptionString(s):
    """
    Function used to convert an option string into a list of options.
    
    Arguments
    
        s -- option string (string or QString)
        
    Returns
    
        list of options (list of strings)
    """
    olist = []
    rx = QRegExp(r"""\s([^\s]+|"[^"]+"|'[^']+')""")
    
    qs = QString(s)
    if not qs.startsWith(' '):
        # prepare the  string to fit our pattern
        qs = qs.prepend(' ')
        
    pos = rx.search(qs)
    while pos != -1:
        cs = str(rx.cap(1))
        if cs.startswith('"') or cs.startswith("'"):
            cs = cs[1:-1]
        olist.append(cs)
        pos += rx.matchedLength()
        pos = rx.search(qs, pos)
        
    return olist

def getPythonLibPath():
    """
    Function to determine the path to Pythons library.
    
    Returns
    
        path to the Python library (string)
    """
    pyFullVers = string.split(sys.version)[0]

    vl = string.split(re.findall("[0-9.]*",pyFullVers)[0],".")
    major = vl[0]
    minor = vl[1]

    pyVers = major + "." + minor
    pyVersNr = int(major) * 10 + int(minor)

    if sys.platform == "win32":
        libDir = sys.prefix + "\\Lib"
    else:
        libDir = sys.prefix + "/lib/python" + pyVers
        
    return libDir
    
def getPythonVersion():
    """
    Function to get the Python version (major, minor) as an integer value.
    
    Returns
    
        An integer representing major and minor version number (integer)
    """
    return sys.hexversion >> 16
    
def win32_Kill(pid):
    """
    Function to provide an os.kill equivalent for Win32.
    
    Arguments
    
        pid -- process id
    """
    import win32api
    handle = win32api.OpenProcess(1, 0, pid)
    return (0 != win32api.TerminateProcess(handle, 0))
