# -*- coding: latin-1 -*-
"""
Rutinas generales de apoyo.
"""

import os
import gc
import atexit
import cPickle
import base64
import datetime
import hashlib
import random
import binascii
import sqlite3
import codecs
import time
import shutil
import collections
from itertools import izip, cycle

#--------------------------------------------------------------------------------
def xor_crypt(data, key):
    """
    http://bytes.com/topic/python/answers/881561-xor-encryption
    Autor = http://bytes.com/profile/247871/darktemp/
    """
    if key :
        return ''.join(chr(ord(x) ^ ord(y)) for (x,y) in izip(data, cycle(key)))
    else :
        return data

#--------------------------------------------------------------------------------
def guardaDIC( dic, fich ) :
    f = open( fich, "w" )
    f.write(base64.encodestring(cPickle.dumps( dic )))
    f.close()
#--------------------------------------------------------------------------------
def guardaVar( fich, v ) :
    f = open( fich, "w" )
    f.write(cPickle.dumps( v ))
    f.close()

#--------------------------------------------------------------------------------
def recuperaDIC( fich ) :
    try :
        f = open( fich, "r" )
        s = f.read()
        f.close()

        dic = cPickle.loads( base64.decodestring( s ) )
    except :
        dic = None
    return dic

#--------------------------------------------------------------------------------
def recuperaVar( fich ) :
    try :
        f = open( fich, "r" )
        s = f.read()
        f.close()

        v = cPickle.loads( s )
    except :
        v = None
    return v

#--------------------------------------------------------------------------------
def dic2txt( dic ) :
    return base64.encodestring(cPickle.dumps( dic )).replace( "\n", "|" )
def txt2dic( txt ) :
    txt = txt.replace( "|", "\n" )
    dic = cPickle.loads( base64.decodestring( txt ) )
    return dic

#--------------------------------------------------------------------------------
def renombraNum( origen ) :
    num = 1
    while os.path.isfile( "%s.%d"%(origen,num) ) :
        num += 1
    os.rename( origen, "%s.%d"%(origen,num) )

#--------------------------------------------------------------------------------

class Almacen :
    pass
#------------------------------------------------------------------------------------------------------
def hoy() :
    df = datetime.datetime.now()
    return df
#------------------------------------------------------------------------------------------------------
def primeraMayuscula( txt ) :
    return txt[0].upper() + txt[1:]

#------------------------------------------------------------------------------------------------------
def huella() :
    m = hashlib.md5()
    m.update(str(random.random())+str(hoy()))
    return m.hexdigest()

#------------------------------------------------------------------------------------------------------
def bloque_envio( texto ) :
    return base64.encodestring(texto+"·%d"%binascii.crc32(texto)).strip()
def bloque_recibe( texto ) :
    txt = base64.decodestring( texto )
    pos = txt.rfind( "·" )
    if pos >= 0 :
        num = int(txt[pos+1:])
        txt = txt[:pos]
        crc = binascii.crc32(txt)
        resp = num == crc
    else :
        resp = False
    return resp, txt

#==========================================================================================================================================
def ponVersion(version) :
    f = open( "version.dat", "wb" )
    f.write( version )
    f.close()
#------------------------------------------------------------------------------------------------------
def leeVersion(versionDefecto) :
    try :
        f = open( "version.dat", "rb" )
        cad = f.read()
        f.close()
        return cad.strip()
    except :
        return versionDefecto
#==========================================================================================================================================

#==========================================================================================================================================
def tamFichero( fichero ) :
    return os.path.getsize( fichero ) if os.path.isfile( fichero ) else -1

#==========================================================================================================================================
def existeFichero( fichero ) :
    return tamFichero( fichero ) >= 0

def copiaFichero( origen, destino ) :
    if existeFichero(origen) :
        if borraFichero( destino ) :
            shutil.copy2( origen, destino )
            return True
    return False
#==========================================================================================================================================
def borraFichero( fichero ) :
    try :
        os.remove( fichero )
    except :
        pass
    return not os.path.isfile( fichero )
#==========================================================================================================================================
def ini2lista( fichero, etiClave = "CLAVE" ) :

    li = []

    if os.path.isfile( fichero) :

        f = open( fichero, "rb" )

        clave = ""
        for linea in f :
            linea = linea.strip()
            if linea :
                if linea.startswith( "[" ) :
                    clave = linea[1:-1]
                    dic = {}
                    li.append( dic )
                    dic[etiClave] = clave
                else :
                    n = linea.find( "=" )
                    if n :
                        clave1 = linea[:n].strip()
                        valor = linea[n+1:].strip()
                        dic[clave1] = valor
        f.close()

    return li
#==========================================================================================================================================
def lista2ini( fichero, lista, etiClave = "CLAVE" ) :
    f = open( fichero, "wb" )
    for dic in lista :
        f.write( "[%s]\n"%dic[etiClave] )
        for k in dic :
            if k != etiClave :
                f.write( "%s=%s\n"%(k,dic[k]) )
    f.close()
#==========================================================================================================================================
#==========================================================================================================================================
def iniBase8dic( fichero ) :

    dic = {}

    if os.path.isfile( fichero) :

        f = codecs.open( fichero, "rb", "utf-8" )

        clave = ""
        for linea in f :
            linea = linea.strip()
            if linea.startswith( "#" ) :
                continue
            if linea :
                n = linea.find( "=" )
                if n :
                    clave = linea[:n].strip()
                    valor = linea[n+1:].strip()
                    dic[clave] = valor
        f.close()

    return dic
#==========================================================================================================================================
def dic8iniBase( fichero, dic ) :
    f = codecs.open( fichero, "wb", "utf-8" )
    for k in dic :
        f.write( "%s=%s\n"%(k,dic[k]) )
    f.close()

#==========================================================================================================================================
def creaCarpeta( carpeta ) :
    try :
        os.mkdir( carpeta )
    except :
        pass
#==========================================================================================================================================

#==========================================================================================================================================
class VarDisk(object) :
    """
    Objeto que almacena variables normalmente que ocupan mucho espacio, en fichero sqlite
    v = VarDisk( fichero )
    v.numero = 1230
    prlk( v.numero )
    v.lista=12,"23", {12:3}
    prlk( v.lista )
    v.cerrar()
    """
    def __init__( self, nomFichero ) :
        siFichero = existeFichero(nomFichero)
        self._conexion = sqlite3.connect( nomFichero )
        self._conexion.text_factory = lambda x: unicode(x, "latin-1", "ignore")
        atexit.register( self.close )

        self.__dicClaves = {}

        if not siFichero :
            sql = "CREATE TABLE datos( CLAVE TEXT PRIMARY KEY, DATO TEXT );"
            self._conexion.cursor().execute( sql )
            self._conexion.commit()
        else :
            sql = "SELECT rowid, clave FROM datos;"
            cursor = self._conexion.cursor()
            cursor.execute( sql )
            li = cursor.fetchall()
            for rowid, clave in li :
                self.__dicClaves[clave] = rowid

    def __setattr__( self, clave, valor ) :
        if "__" in clave :
            object.__setattr__( self, clave, valor )
            return
        cursor = self._conexion.cursor()
        dato = cPickle.dumps( valor )
        liValores = [dato]
        siNuevo = clave not in self.__dicClaves
        if siNuevo :
            sql = "INSERT INTO datos (dato,clave) values(?,?)"
            liValores.append( clave )
        else :
            sql = "UPDATE datos SET dato=? WHERE rowid = %d"%self.__dicClaves[clave]
        cursor.execute( sql, liValores )

        if siNuevo :
            self.__dicClaves[clave] = cursor.lastrowid

        cursor.close()
        self._conexion.commit()

    def __getattr__( self, clave ) :
        if "__" in clave :
            return object.__getattr__( self, clave )
        if clave in self.__dicClaves :
            cursor = self._conexion.cursor()
            sql = "SELECT dato FROM datos WHERE rowid=%d"%self.__dicClaves[clave]
            cursor = self._conexion.cursor()
            cursor.execute( sql )
            cursor.close()
            dato = cursor.fetchone()[0]
            valor = cPickle.loads(str(dato))
            return valor

        else :
            return None

    def __len__( self ) :
        return len(self.__dicClaves)

    def close( self ) :
        self._conexion.close()

#-----------------------------------------------------------------------------------------------------------------------------------------------

class TmpVarDisk( VarDisk ) :
    def __init__( self ) :
        """
        Igual que VarDisk, pero de uso temporal, trabaja sobre un fichero que borra al final
        v = TrashDisk()
        v.hola = 24, 34, 16
        prlk( v.hola )
        prlk( len(v) )
        v.close()
        """
        n = 0
        base = "trash%d.db3"
        while True :
            n += 1
            nomFichero = base%n
            if os.path.isfile(nomFichero) :
                try :
                    os.remove(nomFichero)
                    break
                except :
                    continue
            else :
                break
        self.__nomFichero = os.path.abspath(nomFichero)
        VarDisk.__init__( self, nomFichero )
    def close(self) :
        VarDisk.close( self )
        try :
            os.remove( self.__nomFichero )
        except :
            pass

#==========================================================================================================================================
class LIdisk :
    """
    v = LiKdisk( fichero )
    v.append( dato )
    prlk( len(v) )
    prlk( v[3] )
    v.close()
    """
    def __init__( self, nomFichero ) :

        self.nomFichero = nomFichero
        self.conexion = sqlite3.connect( nomFichero )
        self.conexion.text_factory = lambda x: unicode(x, "latin-1", "ignore")
        atexit.register( self.close )

        try :
            sql = "CREATE TABLE datos( DATO TEXT );"
            self.conexion.cursor().execute( sql )
        except :
            pass

    def append( self, valor ) :
        sql = "INSERT INTO datos( DATO ) VALUES( ? )"
        liValores = [ cPickle.dumps(valor) ]

        cursor = self.conexion.cursor()
        cursor.execute( sql, liValores )
        cursor.close()
        self.conexion.commit()

    def __getitem__( self, id ) :
        sql = "select DATO from datos where ROWID=%d"%(id+1,)
        cursor = self.conexion.cursor()
        cursor.execute( sql )
        dato = cursor.fetchone()
        cursor.close()
        return cPickle.loads(str(dato[0]) )

    def __len__( self ) :
        sql = "select COUNT(DATO) from datos"
        cursor = self.conexion.cursor()
        cursor.execute( sql )
        resp = cursor.fetchone()
        cursor.close()
        return resp[0]

    def close( self ) :
        self.conexion.close()

#------------------------------------------------------------------------------------------------------
#~ import subprocess
#~ class Proceso():

    #~ def __init__( self, exe ) :
        #~ self.setWorkingDirectory ( os.path.abspath(os.path.dirname(exe)) )
        #~ if "critter" in exe.lower() :
            #~ prlk( "crit", exe )
            #~ startupinfo = subprocess.STARTUPINFO()
            #~ startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
            #~ startupinfo.wShowWindow = subprocess.SW_HIDE
            #~ self.popen = p = subprocess.Popen("", executable=exe, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=False, startupinfo=startupinfo)
        #~ else :
            #~ self.popen = p = subprocess.Popen( "", executable=exe, shell=True, \
                                                #~ stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT )
        #~ self.stdin = p.stdin
        #~ self.stdout = p.stdout

    #~ def escribeLinea( self, linea ) :
        #~ self.stdin.write( linea + "\n" )

    #~ def esperaRespuesta( self, x=None ) :
        #~ return self.stdout.readline()

    #~ def terminar( self ) :
        #~ try :
            #~ self.popen.terminate()
        #~ except :
            #~ pass

#------------------------------------------------------------------------------------------------------
def detectCPUs():
    """
    Detects the number of CPUs on a system. Cribbed from pp.
    http://codeliberates.blogspot.com/2008/05/detecting-cpuscores-in-python.html
    Literal : I found this interesting function in a post on Twister and distributed programming by Bruce Eckel. It uses the Python os package to detect the number of CPUs/cores on a machine. Archiving it here for future reference
    """
    # Linux, Unix and MacOS:
    if hasattr(os, "sysconf"):
        if os.sysconf_names.has_key("SC_NPROCESSORS_ONLN"):
            # Linux & Unix:
            ncpus = os.sysconf("SC_NPROCESSORS_ONLN")
            if isinstance(ncpus, int) and ncpus > 0:
                return ncpus
        else: # OSX:
            return int(os.popen2("sysctl -n hw.ncpu")[1].read())
    # Windows:
    if os.environ.has_key("NUMBER_OF_PROCESSORS"):
        ncpus = int(os.environ["NUMBER_OF_PROCESSORS"]);
        if ncpus > 0:
            return ncpus
    return 1 # Default
#------------------------------------------------------------------------------------------------------

#--------------------------------------------------------------------------------
#~ from ctypes import *
#~ from ctypes.wintypes import *
#~ class MEMORYSTATUS(Structure):
    #~ _fields_ = [
                    #~ ('dwLength', DWORD),
                    #~ ('dwMemoryLoad', DWORD),
                    #~ ('dwTotalPhys', DWORD),
                    #~ ('dwAvailPhys', DWORD),
                    #~ ('dwTotalPageFile', DWORD),
                    #~ ('dwAvailPageFile', DWORD),
                    #~ ('dwTotalVirtual', DWORD),
                    #~ ('dwAvailVirtual', DWORD),
                #~ ]
#~ def winmem():
    #~ x = MEMORYSTATUS()
    #~ windll.kernel32.GlobalMemoryStatus(byref(x))
    #~ return x

#==========================================================================================================================================
class DicDisk(object) :
    """
    Objeto que almacena variables en formato diccionario
    dd = DicDisk( fichero )
    dd[clave] = objeto python
    clave in dd ->
    dd.cerrar()

    dd = DicDisk("borrar.db")
    prlk( len(dd) )
    prlk( "hola" in dd, dd["hola"] )

    # dd["hola"] = [12, 34, 34]
    class A :
        def __init__( self ) :
            self.valor = ["222",32]

    # dd["oo"] = A()

    g = dd["oo"]
    prlk( g.valor )

    dd.close()

    """

    def __init__( self, nomFichero, tabla="datos" ) :
        self._conexion = sqlite3.connect( nomFichero )
        self._conexion.text_factory = lambda x: unicode(x, "latin-1", "ignore")
        atexit.register( self.close )

        try :
            sql = "CREATE TABLE %s( CLAVE TEXT PRIMARY KEY, DATO TEXT );"%tabla
            self._conexion.cursor().execute( sql )
            self._conexion.commit()
        except :
            pass

        self.dic = {}
        self.tabla = tabla
        self.leeTodo()

    def leeTodo( self ) :
        cursor = self._conexion.cursor()
        sql = "SELECT clave,dato FROM %s"%self.tabla
        cursor.execute( sql )
        li = cursor.fetchall()
        for clave, dato in li :
            self.dic[clave] = dato
        cursor.close()

    def __contains__( self, clave ) :
        return clave in self.dic

    def __setitem__( self, clave, valor ) :
        cursor = self._conexion.cursor()
        dato = cPickle.dumps( valor )
        liValores = [dato,clave]
        siYaEsta = self.__contains__(clave)
        if siYaEsta :
            sql = "UPDATE %s SET dato=? WHERE clave = ?"%self.tabla
        else :
            sql = "INSERT INTO %s (dato,clave) values(?,?)"%self.tabla
        cursor.execute( sql, liValores )
        cursor.close()
        self._conexion.commit()
        self.dic[clave] = dato

    def __getitem__( self, clave ) :
        if clave in self.dic :
            return cPickle.loads(str(self.dic[clave]))
        else :
            return None

    def __len__( self ) :
        return len(self.dic)

    def close( self ) :
        self._conexion.close()

    def keys( self ) :
        return self.dic.keys()

    def get( self, clave, default ) :
        if clave in self.dic :
            return cPickle.loads(str(self.dic[clave]))
        else :
            return default

#==========================================================================================================================================
class DicDiskCache(object) :
    """
    Objeto que almacena variables en formato diccionario
    dd = DicDisk( fichero )
    dd[clave] = objeto python
    clave in dd ->
    dd.cerrar()

    dd = DicDisk("borrar.db")
    prlk( len(dd) )
    prlk( "hola" in dd, dd["hola"] )

    # dd["hola"] = [12, 34, 34]
    class A :
        def __init__( self ) :
            self.valor = ["222",32]

    # dd["oo"] = A()

    g = dd["oo"]
    prlk( g.valor )

    dd.close()

    """

    def __init__( self, nomFichero, tabla="datos", maxCache = 256 ) :
        self._conexion = sqlite3.connect( nomFichero )
        self._conexion.text_factory = lambda x: unicode(x, "latin-1", "ignore")
        atexit.register( self.close )
        self.maxCache = maxCache
        self.cache = collections.OrderedDict()

        try :
            sql = "CREATE TABLE %s( CLAVE TEXT PRIMARY KEY, DATO TEXT );"%tabla
            self._conexion.cursor().execute( sql )
            self._conexion.commit()
        except :
            pass

        self.dic = {}
        self.tabla = tabla
        self.leeClaves()

    def leeClaves( self ) :
        cursor = self._conexion.cursor()
        sql = "SELECT clave FROM %s"%self.tabla
        cursor.execute( sql )
        li = cursor.fetchall()
        for clave, in li :
            self.dic[clave] = True
        cursor.close()

    def __contains__( self, clave ) :
        return clave in self.dic

    def __setitem__( self, clave, valor ) :
        cursor = self._conexion.cursor()
        dato = cPickle.dumps( valor )
        liValores = [dato,clave]
        siYaEsta = self.__contains__(clave)
        if siYaEsta :
            sql = "UPDATE %s SET dato=? WHERE clave = ?"%self.tabla
        else :
            sql = "INSERT INTO %s (dato,clave) values(?,?)"%self.tabla
        cursor.execute( sql, liValores )
        cursor.close()
        self._conexion.commit()
        self.dic[clave] = True

        self.cache[clave] = valor
        if len(self.cache) > self.maxCache :
            del self.cache[self.cache.keys[0]]

    def __getitem__( self, clave ) :
        if clave in self.dic :

            if clave in self.cache :
                return self.cache[clave]

            cursor = self._conexion.cursor()
            sql = "SELECT dato FROM %s WHERE clave= ?"%self.tabla
            liValores = [clave,]
            cursor.execute( sql, liValores )
            li = cursor.fetchone()
            cursor.close()

            valor = cPickle.loads(str(li[0]))

            self.cache[clave] = valor
            if len(self.cache) > self.maxCache :
                del self.cache[self.cache.keys[0]]

            return valor

        else :
            return None

    def __len__( self ) :
        return len(self.dic)

    def close( self ) :
        self._conexion.close()

    def keys( self, siOrdenados = False, siReverse = False ) :
        li = self.dic.keys()
        return sorted(li, reverse = siReverse) if siOrdenados else li

    def get( self, clave, default ) :
        if clave in self.dic :
            return self.__getitem__(clave)
        else :
            return default

#==========================================================================================================================================
class DicDiskBlob(object) :

    def __init__( self, nomDB, tabla ) :
        self._conexion = sqlite3.connect( nomDB )
        atexit.register( self.close )

        cursor = self._conexion.cursor()
        cursor.execute("pragma table_info(%s)"%tabla)
        liCampos = cursor.fetchall()

        if not liCampos :
            sql = "CREATE TABLE %s( CLAVE TEXT PRIMARY KEY, DATO BLOB );"%tabla
            cursor.execute( sql )
            self._conexion.commit()
            cursor.close()

        self.dic = {}
        self.tabla = tabla
        self.leeClaves()

    def leeClaves( self ) :
        cursor = self._conexion.cursor()
        sql = "SELECT clave FROM %s"%self.tabla
        cursor.execute( sql )
        li = cursor.fetchall()
        for clave, in li :
            self.dic[clave] = True
        cursor.close()

    def __contains__( self, clave ) :
        return clave in self.dic

    def __setitem__( self, clave, wav ) :
        cursor = self._conexion.cursor()
        dato = sqlite3.Binary(wav)
        liValores = [dato,clave]
        if self.__contains__(clave) :
            sql = "UPDATE %s SET dato=? WHERE clave = ?"%self.tabla
        else :
            sql = "INSERT INTO %s (dato,clave) values(?,?)"%self.tabla
            self.dic[clave] = True
        cursor.execute( sql, liValores )
        cursor.close()
        self._conexion.commit()

    def __getitem__( self, clave ) :
        if clave in self.dic :
            cursor = self._conexion.cursor()
            sql = "SELECT dato FROM %s WHERE clave= ?"%self.tabla
            liValores = [clave,]
            cursor.execute( sql, liValores )
            li = cursor.fetchone()
            cursor.close()

            return li[0]
        else :
            return None

    def __len__( self ) :
        return len(self.dic)

    def close( self ) :
        self._conexion.close()

    def keys( self ) :
        return self.dic.keys()

    def get( self, clave, default ) :
        if clave in self.dic :
            return self.__getitem__(clave)
        else :
            return default

#==========================================================================================================================================
def dirRelativo( dr ) :
    if dr :
        nr = os.path.relpath(dr)
        if not nr.startswith( ".." ) :
            dr = nr
    else :
        dr = ""
    return dr

#==========================================================================================================================================
class ListaNumerosImpresion :
    def __init__( self, txt ) :
        # Formas
        #   1. <num>            1, <num>, 0
        #   2. <num>-           2, <num>, 0
        #   3. <num>-<num>      3, <num>,<num>
        #   4. -<num>           4, <num>, 0
        self.lista = []
        if txt :
            txt = txt.replace( "--", "-" ).replace(",,", ",")

            for bloque in txt.split(",") :

                if bloque.startswith( "-" ) :
                    num = bloque[1:]
                    if num.isdigit() :
                        self.lista.append( (4,int(num)) )

                elif bloque.endswith( "-" ) :
                    num = bloque[:-1]
                    if num.isdigit() :
                        self.lista.append( (2,int(num)) )

                elif "-" in bloque :
                    li = bloque.split("-")
                    if len(li) == 2 :
                        num1, num2 = li
                        if num1.isdigit() and num2.isdigit() :
                            i1 = int(num1)
                            i2 = int(num2)
                            if i1 <= i2 :
                                self.lista.append( (3,i1,i2) )

                elif bloque.isdigit() :
                    self.lista.append( (1,int(bloque)) )

    def siEsta( self, pos ) :
        if not self.lista :
            return True

        for patron in self.lista :
            modo = patron[0]
            i1 = patron[1]
            if modo == 1 :
                if pos == i1 :
                    return True
            elif modo == 2 :
                if pos >= i1 :
                    return True
            elif modo == 3 :
                i2 = patron[2]
                if i1 <= pos <= i2 :
                    return True
            elif modo == 4 :
                if pos <= i1 :
                    return True

        return False

#==========================================================================================================================================

def speed() :
    t = time.clock()
    for x in xrange(100000) :
        for i in xrange(10) :
            oct(i)
        gc.enable()
    return time.clock()-t
