from moviepy.editor import *
import numpy as np
# import cairocffi
# import gizeh
# must also install cairo and sudo installation
# and also pipwin if u use mac
# pip install pipwin
#
# pipwin install cairocffi
# also if you get an error with installing:
# https://stackoverflow.com/questions/68275857/urllib-error-urlerror-urlopen-error-ssl-certificate-verify-failed-certifica
# https://www.csestack.org/macbook-m1-oserror-no-library-called-cairo-pango-was-found/ (for macs)?
# laksjdfklajsdklfjaklsdfjasdOMGGGDSUGHHHHHHHHH


# moviepy uses a 3-D H-W-3 numpy array to represent each pixel RGB

def wipe_away(vid, speed=1, start=0,
             left=False, top=True):
    '''
    :param vid: video file name
    :param speed: how fast the wipe effect is in seconds
    :param horizontal: wipes top down, default false
    :param vertical: wipes left right, default true
    :return: returns a moviepy clip
    '''
    assert(isinstance(vid, VideoClip))
    print("OK")
    if left:
        top = False
    assert(left or top)
    # this is a simple wipe, so we can break the video into three separate parts
    # the FIRST is just the original clip from the very start to the beginning
    # of the wipe, and the SECOND clip is the actual transition
    clip_original = vid.subclip(0, start)
    clip_transition = vid.subclip(start, start+speed)

    clip_size = clip_transition.size

    def wipe_y(get_frame, t):
        frame = np.copy(get_frame(t))
        for row in range(int((t / speed) * clip_size[1])):
            frame[row] = np.zeros((clip_size[0], 3), dtype=int)
            # if you wipe horizontally away and immediately end the video,
            # the video player might stop like a frame before it finishes and
            # it'll look like an unfinished wipe, but to that we say
            # too bad!!!!
        return frame

    def wipe_x(get_frame, t):
        frame = np.copy(get_frame(t))
        for col in range(int((t / speed) * clip_size[0])):
            frame[:, col] = np.zeros((clip_size[1], 3), dtype=int)
        return frame

    if left:
        clip_transition = clip_transition.fl(wipe_x)
    elif top:
        clip_transition = clip_transition.fl(wipe_y)

    final_clip = concatenate_videoclips([clip_original, clip_transition])
    return final_clip

def wipe_into(vid, speed=1, start=0,
             left=False, top=True):
    if left:
        top = False
    assert(left or top)
    # this is a simple wipe, so we can break the video into three separate parts
    # the FIRST is just the original clip from the very start to the beginning
    # of the wipe, and the SECOND clip is the actual transition
    clip_original = VideoFileClip(vid).subclip(0, start)
    clip_transition = VideoFileClip(vid).subclip(start, start+speed)

    clip_size = clip_transition.size

    def wipe_y(get_frame, t):
        frame = np.copy(get_frame(t))
        for row in range(int((t / speed) * clip_size[1]), clip_size[1]):
            frame[row] = np.zeros((clip_size[0], 3), dtype=int)
        return frame


    def wipe_x(get_frame, t):
        frame = np.copy(get_frame(t))
        for col in range(int((t / speed) * clip_size[0]), clip_size[0]):
            frame[:, col] = np.zeros((clip_size[1], 3), dtype=int)
        return frame

    if left:
        clip_transition = clip_transition.fl(wipe_x)
    elif top:
        clip_transition = clip_transition.fl(wipe_y)

    final_clip = concatenate_videoclips([clip_original, clip_transition])
    return final_clip

# wipe-aways, wipe-into, wipe-transition, circle-transition,

def wipe_transition(vid1, vid2,
             speed=1, start=0,
             horizontal=False, vertical=True,
             borderWidth=0):
    '''
    :param vid1: video 1 file name
    :param vid2: video 2 file name
    :param speed: how fast the wipe effect is in seconds
    :param horizontal: wipes top down, default false
    :param vertical: wipes left right, default true
    :param borderWidth: how large border is
    :return: returns a moviepy clip
    '''
    # this is a simple wipe, so we can break the video into three separate parts
    # the first is just the original clip from the very start to the beginning
    # of the wipe

    # the second clip is the actual transition

    # and the third clip is just the second clip, starting from however long
    # it took the wipe
    clip1Original = VideoFileClip(vid1).subclip(0, start)
    clip2Original = VideoFileClip(vid2, has_mask=True).subclip(speed)

    clip1Transition = VideoFileClip(vid1).subclip(start, start + speed)
    clip2Transition = VideoFileClip(vid2, has_mask=True).subclip(0, speed)

    clipSize = clip1Transition.size
    clip2Size = clip2Transition.size
    print(clip2Size)
    print(clipSize[0])

    def wipe_y(get_frame, t):
        frame1 = np.copy(get_frame(t))
        frame2 = np.copy(clip2Transition.get_frame(t))
        for row in range(int((t/speed) * clipSize[1])):
            frame1[row] = frame2[row]
        return frame1

    clipTransition = clip1Transition.fl(wipe_y)

    final_clip = concatenate_videoclips([clip1Original,
                                         clipTransition,
                                         clip2Original])
    return final_clip

# def wipe_complicated():
#     return

def circle_wipe_away(vid, speed=1, start=0):
    # this will use gizeh to overlay a drawing frame by frame until the circle
    # completely expands
    clipOriginal = VideoFileClip(vid, has_mask=True).subclip(0, start)
    clipTransition = VideoFileClip(vid, has_mask=True).subclip(start,
                                                               start+speed)
    # clipTransition.write_videofile("temporary.mp4",
    #                            temp_audiofile="temp-audio.m4a", remove_temp=True,
    #                            codec="libx264", audio_codec="aac")
    clipSize = clipOriginal.size
    height = clipSize[1]
    width = clipSize[0]
    # print(height, width)
    # print(frameTest.shape)

    def make_frame(t):
        surface = np.zeros((height, width, 3))
        # print(surface.shape)
        radius = (t/speed)*max(height, width)
        for row in range(height):
            for col in range(width):
                distance = ((width/2 - col)**2 + (height/2 - row)**2)**0.5
                if distance > radius:
                    surface[row][col] = clipTransition.get_frame(t)[row][col]
        # surface = gizeh.Surface(width=clipSize[1], height=clipSize[0])
        # radius = (t/speed)*max(surface.width, surface.height)
        # circle = gizeh.circle(r=radius, xy=[surface.width / 2,
        #                                     surface.height / 2],
        #                       fill=(0, 0, 0))
        # circle.draw(surface)

        # return surface.get_npimage(transparent=True)
        return surface

    overlay = VideoClip(make_frame, duration=speed)
    final_audio = clipTransition.audio
    overlay.set_audio(final_audio)
    final_clip = concatenate_videoclips([clipOriginal, overlay])
    return final_clip

def circle_wipe_into(vid, speed=1, start=0):
    # this will use gizeh to overlay a drawing frame by frame until the circle
    # completely expands
    clipOriginal = VideoFileClip(vid, has_mask=True).subclip(0, start)
    clipTransition = VideoFileClip(vid, has_mask=True).subclip(start,
                                                               start+speed)
    # clipTransition.writeVideoFile("temporary.mp4",
    #                            temp_audiofile="temp-audio.m4a", remove_temp=True,
    #                            codec="libx264", audio_codec="aac")
    clipSize = clipOriginal.size
    height = clipSize[1]
    width = clipSize[0]
    # print(height, width)
    frameTest = clipTransition.get_frame(0)
    # print(frameTest.shape)

    def make_frame(t):
        surface = np.zeros((height, width, 3))
        # print(surface.shape)
        radius = max(height, width) - (t/speed)*max(height, width)
        for row in range(height):
            for col in range(width):
                distance = ((width/2 - col)**2 + (height/2 - row)**2)**0.5
                if distance < radius:
                    surface[row][col] = clipTransition.get_frame(t)[row][col]
        # surface = gizeh.Surface(width=clipSize[1], height=clipSize[0])
        # radius = (t/speed)*max(surface.width, surface.height)
        # circle = gizeh.circle(r=radius, xy=[surface.width / 2,
        #                                     surface.height / 2],
        #                       fill=(0, 0, 0))
        # circle.draw(surface)

        # return surface.get_npimage(transparent=True)
        return surface

    overlay = VideoClip(make_frame, duration=speed)
    final_audio = clipTransition.audio
    overlay.set_audio(final_audio)
    # return overlay
    final_clip = concatenate_videoclips([clipOriginal, overlay])
    return final_clip

def circle_wipe_into_complex(vid, speed=1, start=0, function=None):
    if function == None:
        function = lambda t, speed: t/speed
    clipOriginal = VideoFileClip(vid, has_mask=True).subclip(0, start)
    clipTransition = VideoFileClip(vid, has_mask=True).subclip(start,
                                                               start+speed)
    # clipTransition.writeVideoFile("temporary.mp4",
    #                            temp_audiofile="temp-audio.m4a", remove_temp=True,
    #                            codec="libx264", audio_codec="aac")
    clipSize = clipOriginal.size
    height = clipSize[1]
    width = clipSize[0]
    # print(height, width)
    frameTest = clipTransition.get_frame(0)
    # print(frameTest.shape)

    def make_frame(t):
        surface = np.zeros((height, width, 3))
        # print(surface.shape)
        radius = max(height, width) - function(t, speed)*max(height, width)
        for row in range(height):
            for col in range(width):
                distance = ((width/2 - col)**2 + (height/2 - row)**2)**0.5
                if distance < radius:
                    surface[row][col] = clipTransition.get_frame(t)[row][col]
        # surface = gizeh.Surface(width=clipSize[1], height=clipSize[0])
        # radius = (t/speed)*max(surface.width, surface.height)
        # circle = gizeh.circle(r=radius, xy=[surface.width / 2,
        #                                     surface.height / 2],
        #                       fill=(0, 0, 0))
        # circle.draw(surface)

        # return surface.get_npimage(transparent=True)
        return surface

    overlay = VideoClip(make_frame, duration=speed)
    final_audio = clipTransition.audio
    overlay.set_audio(final_audio)
    # return overlay
    final_clip = concatenate_videoclips([clipOriginal, overlay])
    return final_clip



def main():
    clip = VideoFileClip("outtake1.mp4")
    clip = wipe_away(clip, speed=2, start=14,left=True)
    clip.write_videofile("pleasework.mp4",
                               temp_audiofile="temp-audio.m4a", remove_temp=True,
                               codec="libx264", audio_codec="aac")

    # clip = wipe_transition("teleport1.mp4", "teleport2.mp4", speed=0.2, start=2.3)
    # clip1 = wipe_transition("teleport3.mp4", "teleport4.mp4", speed=0.2, start=2.3)
    # clip.write_videofile("tempA.mp4",
    #                            temp_audiofile="temp-audio.m4a", remove_temp=True,
    #                            codec="libx264", audio_codec="aac")
    # clip1.write_videofile("tempB.mp4",
    #                            temp_audiofile="temp-audio.m4a", remove_temp=True,
    #                            codec="libx264", audio_codec="aac")
    #
    # clip_final = wipe_transition("tempA.mp4", "tempB.mp4", speed=0.2, start=5)
    #
    # clip_final.write_videofile("teleportation.mp4",
    #                            temp_audiofile="temp-audio.m4a", remove_temp=True,
    #                            codec="libx264", audio_codec="aac")
    # clip_final = circle_wipe_away("circles.mp4", speed=1, start=1.5)
    #
    # clip_final.write_videofile("circlesFinal.mp4",
    #                            temp_audiofile="temp-audio.m4a", remove_temp=True,
    #                            codec="libx264", audio_codec="aac", fps=24)
    # clip_final = circle_wipe_into("circle-wipein.mp4", speed=1.2, start=1.5)
    #
    # clip_final.write_videofile("circle-wipeinFinal.mp4",
    #                            temp_audiofile="temp-audio.m4a", remove_temp=True,
    #                            codec="libx264", audio_codec="aac", fps=24)
    # def f(t, speed):
    #     if 0 <= t < 1:
    #         return 4*t/speed * 0.9
    #     elif 1 <= t < 2.5:
    #         return 0.9
    #     else:
    #         return 0.9 + (4*(t-2.5)/speed)*0.1
    # clip_final = circle_wipe_into_complex("medical-license.mp4", speed=4,
    #                                       start=16, function=f)
    #
    # clip_final.write_videofile("medical-license-final.mp4",
    #                            temp_audiofile="temp-audio.m4a", remove_temp=True,
    #                            codec="libx264", audio_codec="aac", fps=24)
    # return


main()