from encodings import utf_8
import bpy
# import bpy_extras
from bpy_extras.object_utils import world_to_camera_view # as cam_space
from mathutils import Vector
from pathlib import Path
import json
from . import fn

'''
def Export_AE_2d_position_json_data():
    scn = bpy.context.scene
    cam = scn.objects.get('anim_cam')
    if not cam:
        print('Active camera not "anim_cam"')
        cam = scn.camera

    rx = scn.render
    rx, ry = rd.resolution_x, rd.resolution_y

    targets = [o for o in bpy.context.selected_objects if o.type != 'CAMERA']

    for ob in targets:
        # get an idea of the scale relative to image res ? (too complex since it's not even the right resolution...)
        pos = world_to_camera_view(scn, scn.objects['anim_cam'], ob.matrix_world.to_translation())[:-1]
        pix_pos = Vector((pos[0]*rx, (1-pos[1])*ry))
'''


# Unused (old func that might not be usefull at all...)
def correct_shift(vec, cam):
    resX = bpy.context.scene.render.resolution_x
    resY = bpy.context.scene.render.resolution_y
    ratio = resX/resY
    shiftX = 2*cam.data.shift_x
    shiftY = 2*cam.data.shift_y

    if ratio<1:
        return vec - Vector((shiftX*(1/ratio), shiftY, 0))
    elif ratio>1:
        return vec - Vector((shiftX, shiftY*ratio, 0))
    else:
        return vec - Vector((shiftX, shiftY, 0))

def export_AE_objects_position_keys():
    '''Export keys as paperclip to paste in after'''

    scn = bpy.context.scene
    result = {}
    print(f'Exporting 2d position (scene range: {scn.frame_start} - {scn.frame_end})')
    for fr in range(scn.frame_start,scn.frame_end + 1):

        print(f'frame: {fr}')
        scn.frame_set(fr)

        for o in bpy.context.selected_objects:
            if not result.get(o.name):
                result[o.name] = []
            proj2d = world_to_camera_view(scn, scn.camera, o.matrix_world.to_translation()) # + Vector((.5,.5,0))

            # proj2d = correct_shift(proj2d, scn.camera) # needed ?
            x = (proj2d[0]) * scn.render.resolution_x
            y = -(proj2d[1]) * scn.render.resolution_y + scn.render.resolution_y

            result[o.name].append((fr,x,y))

    for name,value in result.items():

        txt = fn.get_ae_keyframe_clipboard_header(scn)

        for v in value:
            txt += '\t%s\t%s\t%s\t0\t\n'%(v[0],v[1],v[2]) # add 0 for Z (probably not needed)

        txt += '\n\nEnd of Keyframe Data\n' # keyframe txt footer

        blend = Path(bpy.data.filepath)
        keyfile = blend.parent / 'render' / f'pos_{name}.txt'
        keyfile.parent.mkdir(parents=False, exist_ok=True)

        print(f'exporting keys for {name} at {keyfile}')

        ## save forcing CRLF terminator (DOS style, damn windows)
        ## in case it's exported from linux
        with open(keyfile, 'w', newline='\r\n') as fd:
            fd.write(txt)


class GPEXP_OT_export_keys_to_ae(bpy.types.Operator):
    bl_idname = "gp.export_keys_to_ae"
    bl_label = "Export 2D Position To AE"
    bl_description = "Export selected objects positions as text file containing key paperclip for AfterEffects layers"
    bl_options = {"REGISTER"}

    @classmethod
    def poll(cls, context):
        return context.selected_objects

    def execute(self, context):
        export_AE_objects_position_keys()
        return {"FINISHED"}


def export_anim_cam_position(camera=None, context=None):
    context = context or bpy.context
    scn = context.scene

    camera = camera or bpy.data.objects.get('anim_cam')
    if not camera:
        return 'Abort: No "anim_cam" found!'

    text = fn.get_ae_keyframe_clipboard_header(scn)
    for i in range(scn.frame_start, scn.frame_end + 1):
        scn.frame_set(i)
        center = fn.get_cam_frame_center_world(camera)
        coord = fn.get_coord_in_cam_space(scn, scn.camera, center, ae=True)
        text += f'\t{i}\t{coord[0]}\t{coord[1]}\t\n'

    text += '\n\nEnd of Keyframe Data\n' # Ae Frame ending

    blend = Path(bpy.data.filepath)
    keyfile = blend.parent / 'render' / f'anim_cam_pos.txt'
    keyfile.parent.mkdir(parents=False, exist_ok=True)

    print(f'Exporting anim cam positions keys at: {keyfile}')
    with open(keyfile, 'w') as fd:
        fd.write(text)

class GPEXP_OT_export_cam_keys_to_ae(bpy.types.Operator):
    bl_idname = "gp.export_cam_keys_to_ae"
    bl_label = "Export Camera 2D Position To AE"
    bl_description = "Export anim cam positions as text file containing key paperclip for AfterEffects layers"
    bl_options = {"REGISTER"}

    @classmethod
    def poll(cls, context):
        return context.selected_objects

    def execute(self, context):
        cam = None
        if context.object and context.object.type == 'CAMERA' and context.object != context.scene.camera:
            cam = context.object

        err = export_anim_cam_position(camera=cam, context=context)
        if err:
            self.report({'ERROR'}, err)
            return {"CANCELLED"}
        return {"FINISHED"}

class GPEXP_OT_fix_overscan_shift(bpy.types.Operator):
    bl_idname = "gp.fix_overscan_shift"
    bl_label = "Fix Cam Shift Value With Overscan"
    bl_description = "(Gp render operator) change shift values to re-center overscan"
    bl_options = {"REGISTER"}

    # @classmethod
    # def poll(cls, context):
    #     return context.object

    init_rx : bpy.props.IntProperty(name='pre-overscan res x')
    init_ry : bpy.props.IntProperty(name='pre-overscan res y')

    def invoke(self, context, event):
        # if not context.object:
        #     self.report({'ERROR'}, 'No object selected')
        #     return {'CANCELLED'}

        self.use_selection = False
        if context.active_object and context.active_object.type == 'CAMERA' and context.active_object != context.scene.camera:
            self.use_selection = True
            self.cam_ob = context.active_object
        else:
            self.cam_ob = context.scene.camera

        self.init_rx = context.scene.render.resolution_x
        self.init_ry = context.scene.render.resolution_y

        return context.window_manager.invoke_props_dialog(self)

    def draw(self, context):
        layout = self.layout

        if self.use_selection:
            col = layout.column()
            col.label(text=f'Camera "{self.cam_ob.name}" selected', icon='INFO')
            col.label(text='Change in shifts will apply on this one', icon='BLANK1')

        col = layout.column()
        col.label(text='Overscan res (current)')
        col.label(text=f'{context.scene.render.resolution_x} x {context.scene.render.resolution_y}')

        col = layout.column()
        col.label(text='Enter Initial (pre-overscan) resolution:')
        row = col.row(align=True)
        row.prop(self, 'init_rx', text='')
        row.prop(self, 'init_ry', text='')

    def execute(self, context):
        cam = self.cam_ob.data

        ratio_x = self.init_rx / context.scene.render.resolution_x
        ratio_y = self.init_ry / context.scene.render.resolution_y

        if ratio_x == 1 and ratio_y == 1:
            self.report({'ERROR'}, 'Same init and overscan resolution, nothing to change')
            return {'CANCELLED'}

        if ratio_x != 1:
            if fn.has_keyframe(cam, 'shift_x'):
                fcu = cam.animation_data.action.fcurves.find('shift_x')
                for k in fcu.keyframe_points:
                    k.co.y = k.co.y * ratio_x
            else:
                if cam.shift_x != 1:
                    cam.shift_x = cam.shift_x * ratio_x

        if ratio_y != 1:
            if fn.has_keyframe(cam, 'shift_y'):
                fcu = cam.animation_data.action.fcurves.find('shift_y')
                for k in fcu.keyframe_points:
                    k.co.y = k.co.y * ratio_y
            else:
                if cam.shift_y != 1:
                    cam.shift_y = cam.shift_y * ratio_y

        return {"FINISHED"}


# ui panel
class GPEXP_PT_extra_gprender_func(bpy.types.Panel):
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "output"
    bl_label = "GP Render Extras"
    bl_parent_id = "RENDER_PT_format" if (3, 0, 0) <= bpy.app.version else "RENDER_PT_dimensions"
    bl_options = {'DEFAULT_CLOSED'}
    # COMPAT_ENGINES = {'CYCLES', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}

    # def draw_header(self, context):
    #     overscan = context.scene.camera_overscan
    #     self.layout.prop(overscan, "RO_Activate", text="")

    def draw(self, context):
        layout = self.layout
        col = layout.column()
        col.operator("gp.fix_overscan_shift")
        col.operator("gp.export_keys_to_ae")
        col.operator("gp.export_cam_keys_to_ae")



# def overcan_shift_fix_ui(self, context):
#     layout = self.layout
#     layout.operator("gp.fix_overscan_shift")


classes=(
GPEXP_OT_export_keys_to_ae,
GPEXP_OT_export_cam_keys_to_ae,
GPEXP_OT_fix_overscan_shift,
GPEXP_PT_extra_gprender_func
)

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

    # if hasattr(bpy.types, 'RENDER_PT_overscan'):
    #     bpy.types.RENDER_PT_overscan.append(overcan_shift_fix_ui)


def unregister():
    # if hasattr(bpy.types, 'RENDER_PT_overscan'):
    #     bpy.types.RENDER_PT_overscan.remove(overcan_shift_fix_ui)

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