"""
wxPieTool - wxPython Image Embedding Tool

Copyright 2016 Simon Wu <swprojects@gmx.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA

"""

import base64
import binascii
import multiprocessing
import os
import tempfile
import wx
import importlib.util
from wx.lib.embeddedimage import PyEmbeddedImage


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

PROCESS_COUNT = multiprocessing.cpu_count() - 1

def WritePyImageFile(output_file, pyfiledata):
    """ writes a new pyImages file from pyfiledata """
        
    py_images_file = open(output_file, 'w') # delete any existing file.
    
    """
    Write the relevant header portion and the import statements
    Writes Python statements to the output pyImages file.
    """
    
    py_images_file.write('#' + '-'*69 + '\n\n')
    line = '# This file was generated by %s\n\n' %("wxImage Embedding Tool")
    py_images_file.write(line)
    py_images_file.write('import wx\n')
    py_images_file.write('from wx.lib.embeddedimage import PyEmbeddedImage\n')
    py_images_file.write('\n')
    py_images_file.write('image_index = {}\n')
    py_images_file.write('image_catalog = {}\n')
        
    """
    Writes the Python code to the output pyImages file that both define an image 
    and to be able to generate raw data, wx.Image, wx.Bitmap and wx.Icon objects
    when its pyImmages file is imported by any Python application.
    """
    
    for index in sorted(pyfiledata.keys()):
        values = pyfiledata[index]
        name = values["name"]
        data = values["data"]
        
        py_images_file.write('#' + '-'*69 + '\n\n')
        py_images_file.write('image_catalog["%s"] = PyEmbeddedImage(\n%s\n' % (name, data))
                            
        py_images_file.write('   )\n\n')
        
        # When the PyImages file is imported, 
        # the following dictionary idName value will become a function name.
        py_images_file.write('image_index[%s] = "%s"\n' % (str(index), name))        
        py_images_file.write('\n')
   
    """
    Writes the Get functions at the end of the file
    """
    py_images_file.write('#' + '-'*69 + '\n\n')
    
    # get data function
    py_images_file.write('def GetData(name):\n')
    py_images_file.write('    ')
    py_images_file.write('return image_catalog[name].GetData()\n')
    py_images_file.write('\n')
    
    # scale image function
    py_images_file.write('def ScaleImage(name, width, height):\n')
    py_images_file.write('    ')
    py_images_file.write('image = image_catalog[name].GetImage()\n')
    py_images_file.write('    ')
    py_images_file.write('image = image.Scale(width, height, wx.IMAGE_QUALITY_HIGH)\n')
    py_images_file.write('    ')
    py_images_file.write('return image\n')
    py_images_file.write('\n')
    
    for func_name in ["Image","Bitmap","Icon"]:        
        py_images_file.write('def Get%s(name, width=-1, height=-1):\n' % func_name)
        py_images_file.write('    ')
        py_images_file.write('if (width,height) == (-1,-1):\n')
        py_images_file.write('        ')
        py_images_file.write('return image_catalog[name].Get%s()\n' % func_name)
        py_images_file.write('    ')
        py_images_file.write('else:\n')
        py_images_file.write('        ')
        py_images_file.write('image = ScaleImage(name, width, height)\n')
        py_images_file.write('    ')
        py_images_file.write('image = wx.%s(image)\n' % func_name)
        py_images_file.write('    ')
        py_images_file.write('return image\n')
        py_images_file.write('\n')
        
    py_images_file.close()
    
#end WritePyImageFile def

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

def B64EncodeBinaryData(image_data):
    """
    B64 encodes a binary byte string. Returns a list of lines of strings
    suitable for embedding in a Python file.
    """
    
    # Encode the PNG file's lossless-compressed binary image data into a single, big b64 string.
    encoded_data = binascii.b2a_base64(image_data)[:-1]
    # encoded_data= image_data.encode("base64")
    # encoded_data=image_data
    # Chop the b64 character-encoded encoded_data into manageable 
    # line lengths for writing to a file.
    data_list = []   # b64 linesOfEncPngImgData list.
    while encoded_data:
        line_of_data = encoded_data[:57]     # A chunk length of 72 chars
        encoded_data = encoded_data[57:]     # The remainder of data to be encoded.        
        
        # extract the string from b"<str>"
        line_of_data = line_of_data.decode("utf8")
        line_of_data = '    "%s"' %(line_of_data) 
        
        data_list.append(line_of_data)  
        
    image_data_list = '\n'.join(data_list)
    
    return image_data_list

#end B64EncodeBinaryData def

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

def BitmapToPngFile(bitmap, tmp_file) :
    """
    Save a wx.Bitmap to a PNG file. The contents of this file is intended
    to be b64 encoded in order to finally save it to the output pyImages file.
    """
    
    if bitmap.SaveFile(tmp_file, wx.BITMAP_TYPE_PNG):     # SaveFile() success
        return True
        
    elif wx.Image(bitmap).SaveFile(tmp_file, wx.BITMAP_TYPE_PNG):
        # wx.Bitmap.SaveFile() has failed.
        # Try a different save method.
        return True
        
    else:
        return None
                
#end BitmapToPngFile def

def CreatePngFileData(path) :
    """
    return data of image file, which can than be passed to B64EncodeBinaryData
    """
    
    if not os.path.exists(path):
        return None #"File no longer exists. Cancel import"
        
    try:
        bitmap = wx.Bitmap(path, wx.BITMAP_TYPE_ANY)    
    except:
        return None #"File no longer exists. Cancel import"
        
    # Is image file bad?
    if not bitmap.IsOk():
        return None #"File no longer exists. Cancel import"
    
    # Read the original image file and write it to a new PNG file.
    tmp_file = tempfile.TemporaryFile()
    tmp_file = tmp_file.name # get the path of temporary file 
    # print(dir(tmp_file))
    bmp_to_png = BitmapToPngFile(bitmap, tmp_file)
    if not bmp_to_png:
        print("cannot write to temporary file")
    
    # Encode the PNG file's lossless-compressed binary image data into a single, big b64 string.
    png_file = open(tmp_file, 'rb')
    image_data = png_file.read()
    # b64 = image_data.encode ('base64')
    png_file.close()
    os.remove(tmp_file)
    # print("creating temporary file",tmp_file, image_data )
    from wx.lib.embeddedimage import PyEmbeddedImage
    
    return image_data
    
#end CreatePngFileData def

def GetPyImageData(pyimage):
    """
    Import the embedded_image_file and add its images to image_dict{}.
    The file's existance is expected to have been verified.
    """
    
    file_name, file_ext = os.path.splitext(os.path.basename(pyimage))
    print(file_name, file_ext)        
    
    # import using the full path of the filename
    # """http://stackoverflow.com/questions/67631/how-to-import-a-module-given-the-full-path"""
    try: 
        spec = importlib.util.spec_from_file_location("file_name", pyimage)
        foo = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(foo)
    except:
        print("Failed to load file. Is it a python file?")
        return
        
    # check if the python file is actually a PyImages file.
    try:
        image_index = foo.image_index  # should have been defined
        # image_catalog = foo.image_catalog
        print(image_index.items())
        
        data = {}
        for index, image_name in image_index.items():            
            data[index] = {"name":image_name,
                           "data":foo.GetData(image_name),
                           "bitmap":foo.GetBitmap(image_name)}
                        
    except NameError:
        print("Failed to load file. Is it a valid PyEmbeddedImage File?" )
        return
        
    return data
 
#end GetPyImageData def