import bpy
from bpy_extras.io_utils import ImportHelper

import sys
import os
import json
#import psd_tools
from pathlib import Path
from time import time
from . import core
from . file_utils import install_module



def print_progress(progress, min=0, max=100, barlen=50, prefix='', suffix='', line_width=80):
    total_len = max - min
    progress_float = (progress - min) / total_len
    bar_progress = int(progress_float * barlen) * '='
    bar_empty = (barlen - len(bar_progress)) * ' '
    percentage = ''.join((str(int(progress_float * 100)), '%'))
    progress_string = ''.join((prefix, '[', bar_progress, bar_empty, ']', ' ', percentage, suffix))[:line_width]
    print_string = ''.join((progress_string, ' ' * (line_width - len(progress_string))))
    print(print_string, end='\r')


def export_psd(psd_file, output=None, scale=0.5):
    '''
    export_psd(string psd_file) -> list layers, list bboxes, tuple image_size, string png_dir

        Reads psd_file and exports all top level layers to png's.
        Returns a list of all the layer objects, the image size and
        the png export directory.

        string psd_file - the filepath of the psd file
    '''

    psd_tools = install_module('psd_tools', 'psd-tools')

    # def get_layers(layer, all_layers=[]):
    #     if not layer.is_group():
    #         return
    #     for sub_layer in reversed(layer):  # reversed() since psd_tools 1.8
    #         all_layers.append(sub_layer)
    #         get_layers(sub_layer, all_layers=all_layers)
    #     return all_layers

    def get_dimensions(layer, bbox):
        if bbox is not None:
            # print('with bbox')
            # pp(bbox)

            x = layer.bbox[0] + bbox[0]
            y = layer.bbox[1] + bbox[1]
            width = bbox[2] - bbox[0]
            height = bbox[3] - bbox[1]

        else:
            # print('layer bbox')
            # pp(layer.bbox[:])
            x = layer.bbox[0]
            y = layer.bbox[1]
            width = layer.bbox[2] - x
            height = layer.bbox[3] - y
            # print('x', x)
            # print('y', y)
            # print('width', width)
            # print('height', height)

        return x, y, width, height

    def export_layers_as_png(layers, output, crop=False, scale=0.5):
        output = Path(output)
        box = layers.viewbox
        all_layers = []
        
        for i, layer in enumerate(layers):
            # if (layer.is_group() or (not self.hidden_layers and not layer.is_visible())):
            #     continue
            layer.visible = True
            if layer.is_group() and 'GUIDE' in layer.name:
                for l in layer:
                    #print(l.name, l.visible)
                    l.visible = True

            name = layer.name
            norm_name = core.norm_str(name, padding=2) # Gadget normstr
            print('name: ', name)
            png_output = (output/norm_name).with_suffix('.png')

            print('Layer Output', png_output)

            prefix = '  - exporting: '
            suffix = ' - {}'.format(layer.name)
            print_progress(i+1, max=(len(layers)), barlen=40, prefix=prefix, suffix=suffix, line_width=120)


            # if self.clean_name:
            #     name = bpy.path.clean_name(layer.name).rstrip('_')
            # else:
            #     name = layer.name.replace('\x00', '')
            
            # name = name.rstrip('_')
            # if self.layer_index_name:
            #     name = name + '_' + str(i)

            # composite return a PIL object
            
            if crop:
                # get pre-crop size

                # layer_image = layer.topil()
                layer_image = layer.composite(viewport=box)
                bbox = layer_image.getbbox()
                ## TODO layer bbox might be completely off (when it's a group)

                image = layer.composite()# This crop the image

            else:
                image = layer.composite(viewport=box, force=True)# This crop to canvas size before getting bbox ??
                bbox = None

            if not image:
                continue

            ## optimisation reduce size
            #if scale != 1 :
            image = image.resize((image.width // int(1/scale), image.height // int(1/scale)))


            try:
                image.save(str(png_output))
            except Exception as e:
                print(e)

            # if crop:
            #     layer_box_tuple = get_dimensions(layer, bbox)
            # else:
            #     layer_box_tuple = None
            lbbox = get_dimensions(layer, bbox)
            
            all_layers.append({'name': name, 'layer_bbox': lbbox, 'bbox': bbox, 'index': i, 'path': f'./{png_output.name}'})

            ## No crop for now

            # try:
            #     layer_image = layer.topil()
            # except ValueError:
            #     print("Could not process layer " + layer.name)
            #     bboxes.append(None)
            #     continue
            
            # if layer_image is None:
            #     bboxes.append(None)
            #     continue
            
            # ## AUTOCROP
            # if self.crop_layers:
            #     bbox = layer_image.getbbox()
            #     bboxes.append(bbox)
            #
            #     layer_image = layer_image.crop(bbox)
            # else:
            #     bboxes.append(None)
            # layer_image.save(png_file)

        return all_layers


    print(f'Exporting: {psd_file}')

    psd_file = Path(psd_file)
    if not output:
        # export relative to psd location
        output = psd_file.parent / 'render' # f'{psd_file.stem}_pngs'
    
    output.mkdir(exist_ok = True)
    psd = psd_tools.PSDImage.open(psd_file)

    ## get all layer separately
    # layers = get_layers(psd)
    # bboxes = export_layers_as_png(layers, png_dir)

    ## export the main image (PSD, MAIN, COMPOSITE ?)
    image = psd.composite()
    org_image_size = [image.width, image.height]

    image = image.resize((image.width // 2, image.height // 2))
    image.save(str((output / 'main.png')))

    ## export top level layer passing directly psd
    all_layers = export_layers_as_png(psd, output, crop=False, scale=scale)
    bb = psd.bbox
    image_size = (bb[2] - bb[0], bb[3] - bb[1])

    # return ([l.name for l in psd], bboxes, image_size, output)
    return (all_layers, image_size, org_image_size, output)


def export_psd_bg(psd_fp, scale=0.5):
    '''Export layers of the psd, create and return a json file
    psd_fp :: filepath to the psd file
    scale :: output resolution of the layers (ex: 0.5 means divided by 2)
    '''

    t0 = time()
    psd_fp = Path(psd_fp)

    ## FIXME: Choose how to handle this nomenclature
    output = psd_fp.parent / 'render'
    if scale >= 1:
        output = psd_fp.parent / 'render_hd'

    # print('Folder Output', output)

    ## note: No output passed create 'render' folder aside psd automatically
    layers, image_size, org_image_size, png_dir = export_psd(psd_fp, output, scale=scale)
    json_fp = png_dir/'setup.json'

    setup_dic = {
        'psd': f'../{psd_fp.name}' ,'layers': layers, 'image_size': image_size, 
        'org_image_size' : org_image_size,
                }
    
    ## Dump data to json file

    json_file = png_dir / 'setup.json'

    # json.dump(str(json_file), setup_dic, ensure_ascii=False)
    json_file.write_text(json.dumps(setup_dic, indent=4, ensure_ascii=False), encoding='utf8')

    print(f'Exported in : {time() - t0:.2f}s')
    return json_fp

class BPM_OT_export_psd_layers(bpy.types.Operator, ImportHelper):
    bl_idname = "bpm.export_psd_layers"
    bl_label = "Export Psd Layers"
    bl_description = "Export psd layers from a psd file in a render folder"
    bl_options = {"REGISTER", "INTERNAL"}

    # path_to_pal : bpy.props.StringProperty(name="paht to palette", description="path to the palette", default="")
    @classmethod
    def poll(cls, context):
        return context.object and context.object.type == 'GREASEPENCIL'

    filename_ext = '.psd'

    filter_glob: bpy.props.StringProperty(default='*.psd', options={'HIDDEN'} )
    
    filepath : bpy.props.StringProperty(
        name="File Path", 
        description="File path used for import", 
        maxlen= 1024)

    resolution_scale: bpy.props.FloatProperty(
        name='Resolution Scale',
        default=1.0,
        description='Export resolution ratio. Ex: 0.5 export at half resolution\
            \nusefull for non-def lightweight backgrounds in Blender',
        options={'HIDDEN'})

    def execute(self, context):
        
        json_fp = export_psd_bg(self.filepath, scale=self.resolution_scale)
        if not json_fp:
            self.report({'ERROR'}, f'Problem when exporting image for: {self.filepath}')
            return {'CANCELLED'}
        
        self.report({'INFO'}, f'BG exported, Json at: {json_fp}')
        return {"FINISHED"}


classes = (
    BPM_OT_export_psd_layers,
)

def register():
    for cls in classes:
        bpy.utils.register_class(cls)

def unregister():
    for cls in reversed(classes):
        bpy.utils.unregister_class(cls)