#!/usr/bin/env python
# -*- coding: utf-8 -*-

# This file is part of Pymetrick.  The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.

"""Modulo para el tratamiento de imagenes"""

try:
        from pymetrick.version import __author__, __version__, __license__, __copyright__
except ImportError:
    raise ImportError(
        'Failed to find (autogenerated) version.py '
    )

__date__ = '2012-09-21'
__credits__ = ''
__text__ = 'Utilidades con imagenes'
__file__ = 'image.py'

#--- CHANGES ------------------------------------------------------------------
# 2012-09-21 v0.01 PL: - First version
# 2016-08-10 v0.02 PL: - bug fixes

# Nota:
# En linux antes de instalar Pillow : sudo yum install python-devel libjpeg-devel zlib-devel
#                                     pip install Pillow=4.0.0     o    easy_install Pillow

import sys
# sin bytecode
sys.dont_write_bytecode = True
from PIL import Image

import base64
import os, string
import hashlib
import math, operator
import qrcode
import logging
import logging.handlers

import urllib.request                # python 3

IMAGE_ALLOWED_EXTENSIONS = ['.bmp', '.png', '.jpg', 'jpeg', '.gif']

# Check delopment code
DEBUG = False


'''
DEBUG - debug message
INFO - info message
WARNING - warn message
ERROR - error message
CRITICAL - critical message
'''
LOG_LEVEL_VALUE = {'DEBUG':10,'INFO':20,'WARNING':30,'ERROR':40,'CRITICAL':50}
if str(os.environ.get('PYMETRICK_LOG_LEVEL',None)).upper() in LOG_LEVEL_VALUE:
    LOG_LEVEL = LOG_LEVEL_VALUE[str(os.environ.get('PYMETRICK_LOG_LEVEL')).upper()]
else:
    LOG_LEVEL = 30
LOG_FILENAME = '-'.join([os.path.abspath(__file__).split(os.sep)[len(os.path.abspath(__file__).split(os.sep))-1],])[:-3]
LOG = logging.getLogger(LOG_FILENAME)

if 'LD_LIBRARY_PATH' in list(os.environ.keys()):
    # CGI environment
    sys.stdout = sys.stderr
    logging.basicConfig(stream = sys.stderr, level=LOG_LEVEL, format='%(filename)s[line:%(lineno)d] - %(levelname)s - %(message)s')
else:
    # not CGI environment
    logging.basicConfig(stream=sys.stderr)
    hdlr = logging.handlers.RotatingFileHandler(filename=LOG_FILENAME+'.log',mode='a', encoding='utf-8', maxBytes=1048576, backupCount=3)
    formatter = logging.Formatter('%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s - %(message)s')
    hdlr.setFormatter(formatter)
    LOG.addHandler(hdlr)
    LOG.setLevel(LOG_LEVEL)

def image_size(__path__):
    '''Comprueba la dimension de la imagen'''
    try:
        __size__=list()
        #__path__ = __path__.lower()
        if os.path.exists(__path__):
            if __path__ and (__path__[-4:].lower() in IMAGE_ALLOWED_EXTENSIONS):
                im = Image.open(__path__)
                #width, height = im.size
                __size__=im.size
        return __size__
    except Exception as e:
        LOG.error('Error image.image_size %s in line %s' % (e,format(sys.exc_info()[-1].tb_lineno)))

def image_modify(**kwargs):
    '''@parameters
       file_path : path and file name
       size : list or tuple with values
       quality : number for quality
       dpi : resolution ( horizontal, vertical)
       source_file : opath and name of source file
    '''
    try:
        if kwargs.get('dpi',None):
            _dpi = kwargs['dpi']
        else:
            _dpi = [300,300]
            
        if kwargs.get('file_path',None) and os.path.exists(kwargs['file_path']):
            if kwargs['file_path'] and (kwargs['file_path'][-4:].lower() in IMAGE_ALLOWED_EXTENSIONS):
                _img = Image.open(kwargs['file_path'])
                _img_width, _img_height = _img.size
                _img_relation = _img_width/_img_height
                if kwargs.get('size',None) and isinstance(kwargs['size'],(list,tuple)):
                    if (kwargs['size'][0]/_img_relation) <= kwargs['size'][1]:
                        _img = _img.resize((int(kwargs['size'][0]), int(kwargs['size'][0]/_img_relation)), Image.ANTIALIAS)
                    else:
                        _img_relation = _img_height/_img_width
                        _img = _img.resize((int(kwargs['size'][1]/_img_relation),int(kwargs['size'][1])), Image.ANTIALIAS)
                        
                if kwargs.get('source_file',None) and kwargs['source_file'][-4:] in IMAGE_ALLOWED_EXTENSIONS:
                    format = kwargs['source_file'][-4:].replace('.','').upper().replace('JPG','JPEG')
                    if kwargs.get('quality',None) and kwargs['quality'] != 0:
                        _img.save(kwargs['source_file'], format, dpi=_dpi, quality=int(kwargs['quality']), optimize=True, progressive=True)
                    else:
                        _img.save(kwargs['source_file'], format, dpi=_dpi, quality=100, optimize=True, progressive=True)
                    strip_metadata(kwargs['source_file'])
                    return True
        return False
    except Exception as e:
        LOG.error('Error image.image_resize %s in line %s' % (e,format(sys.exc_info()[-1].tb_lineno)))
        return False

def strip_metadata(__path__,newImage=None):
    '''Elimina EXIF data de imagen
       __path__ = direccion completa de la imagen a tratar
       newImage = si la imagen debe tener una nueva denominacion
    '''
    if os.path.isfile(__path__):
        directory, filename = os.path.split(__path__)
        image = Image.open(__path__)
        data = list(image.getdata())
        image_without_exif = Image.new(image.mode, image.size)
        image_without_exif.putdata(data)
        if len(directory)>0:
            directory += os.sep

        if newImage is not None:
            image_without_exif.save(directory + newImage)
        else:
            image_without_exif.save(directory + filename)


def image2html(__path__):
    '''Devuelve una imagen en base64 como un string de forma que
       se utiliza como imagen embebida en css o html

       El formato para html es :
       <img alt="explicacion" width="57" height="57" src="data:image/png;base64,_string_"/>
       las opciones de data son image/png, image/jpeg, image/gif

       en CSS :
       #header-bd{
       width:12px;
       height:38px;
       background-image:url("data:image/jpeg;base64,_string_");
       background-repeat:no-repeat;
       }

       @return
       dict con datos de imagen
       with
       height
       type = "data:image/?;base64"
       image = imagen en formato base64
    '''
    try:
        #__path__ = __path__.lower()
        im_data = dict()
        if os.path.exists(__path__):
            if __path__ and (__path__[-4:].lower() in IMAGE_ALLOWED_EXTENSIONS):
                '''Dimension de imagen'''
                im_size = image_size(__path__)
                '''Tipo de imagen'''
                im_ext = __path__[-4:].replace('.','')
                '''Cadena de imagen en base64'''
                im_img = image2base64(__path__)

                im_data['width'] = im_size[0]
                im_data['height'] = im_size[1]
                im_data['type'] = 'data:image/%s;base64,' % im_ext
                im_data['image'] = im_img
                return im_data
        return im_data
    except Exception as e:
        LOG.error('Error image.image2html %s in line %s' % (e,format(sys.exc_info()[-1].tb_lineno)))

def image2base64(__path__, blocksize=65536):
    '''Convierte un fichero de imagen en base64 extendido
       @Parameters
           __path__ : direccion donde se encuentra el fichero de imagen
       @Return
           file_name$string_base64
    '''
    try:
        #__path__ = __path__.lower()
        if os.path.exists(__path__):
            if __path__ and (__path__[-4:].lower() in IMAGE_ALLOWED_EXTENSIONS):
                with open(__path__, "rb") as img_file:
                    # codificar imagen como base64
                    img_string = ''
                    for block in iter(lambda: img_file.read(),b''):
                        img_string = base64.b64encode(block)
                    return img_string.decode()
                img_file.close()
            else:
                return ''
        else:
            return ''
    except Exception as e:
        LOG.error('Error image.image2base64 %s in line %s' % (e,format(sys.exc_info()[-1].tb_lineno)))



def image_download(url, fname):
    """Descarga una imagen desde una url y se puede renombrar.
       download('http://nadadenada.com/coche.png',
       '/img/coche_2.png')
    """
    try:
        furl = urllib.request.urlopen(request)
        f = file(fname,'wb')
        f.write(furl.read())
        f.close()
    except Exception as e:
        LOG.error('Error image.image_download %s in line %s' % (e,format(sys.exc_info()[-1].tb_lineno)))

'''* Convertir imagen a gris   im = im.convert ("L")
   * Muestra imagen con im.show()
   * Funcionas basicas
   im.format, im.size,   im.mode
    JPEG      (384, 423) RGB
   * Obetener el valor de un pixel
   im.getpixel((10,20))
   (254, 254, 254)
   '''

def image_checksums(__path__, blocksize=65536):
    """obtener el hash de una imagen para identificarla de forma unica
       esta firma de imagen es util para comparar si la imagen es la misma o diferente a otras
    """
    try:
        #__path__ = __path__.lower()
        im_hash = ''
        if __path__ and (__path__[-4:].lower() in IMAGE_ALLOWED_EXTENSIONS):
            hash = hashlib.md5()
            with open(__path__, "rb") as img:
                for block in iter(lambda: img.read(blocksize), b""):
                    hash.update(block)
            return hash.hexdigest()
        else:
            return ''
    except Exception as e:
        LOG.error('Error image.image_checksums %s in line %s' % (e,format(sys.exc_info()[-1].tb_lineno)))

def image_compare(file1,file2):
    """Comprueba si las imagenes son iguales o diferentes
       return 0 => iguales
       return <>0 => diferentes
    """
    i1 = Image.open(file1)
    i2 = Image.open(file2)
    if i1.mode == i2.mode and i1.size == i2.size:
        h1 = i1.histogram()
        h2 = i2.histogram()
        rms = math.sqrt(reduce(operator.add,map(lambda a,b: (a-b)**2, h1, h2))/len(h1))
        return rms
    else:
        return 1000.1

"""
   Metadatos de imagenes   hex(ord(n))
   jpeg     0xFF, 0xD8 	Start Of Image            0xFF, 0xD9 End Of Image
"""

def image_qrcode(__path__,__data__):
    try:
        img = qrcode.make(__data__)
        f = open(__path__, "wb")
        img.save(f)
        f.close()
        return
    except Exception as e:
        LOG.error('Error image.image_qrcode %s in line %s' % (e,format(sys.exc_info()[-1].tb_lineno)))


if __name__ == "__main__":
    print ('''copyright {0}'''.format( __copyright__))
    print ('''license {0}'''.format( __license__))
    print ('''version {0}'''.format( __version__))
    if len(sys.argv) < 2:
        sys.stderr.write("for help use -h o --help")
    elif sys.argv[1]=='-h' or sys.argv[1]=='--help':
        print ('''Utilidades con imagenes''')
        print ('''image_size(__path__) Comprueba las dimensiones de la imagen.\n''')
        print ('''image_resize(__path__,__factor__=1,__resized_path__='') Convierte fichero de imagen a un tamaño diferente, ademas es posible cambiar el tipo de imagen de png, jpeg o gif a otro cuando se renombra el fichero de imagen resultante.\n''')
        print ('''strip_metadata(image_path,newImage=None) Elimina los metadatos EXIF de una imagen, guardando en el mismo nombre de imagen o bien en otra imagen nueva.\n''')
        print ('''image2html('logo.png') Devuelve una imagen en base64 como un string de forma que se utiliza como imagen embebida en css o html.\n''')
        print ('''image2base64('logo.png') Devuelve una imagen en base64 extendido  ( file_name$string_base64).\n''')
        print ('''image_download('http://nadadenada.com/coche.png', '/img/coche_2.png') Descarga una imagen desde una url y permite renombrar el fichero.\n''')
        print ('''image_checksums('logo.png') Devuelve un hash como un string de forma que se puede comparar con otras imagenes \n''')
        print ('''image_qrcode('/home/content.png'logo.png') Devuelve datos/informacion en formato qrcode como imagen png \n''')


