# Context menu option to create new sequences from selected Clips in the Bin View.
# If Clips are named as Stereo left and right then one sequence will be created with left and right tagged tracks.

import hiero.core
from PySide6 import QtGui, QtWidgets
from PySide6.QtWidgets import QSizePolicy, QDialogButtonBox
import re
import os
import trace

class NewSequenceFromSelectionAction(QtGui.QAction):

    class NewSequenceFromSelection(QtWidgets.QDialog):

        def __init__(self,  trackItem,  parent=None):
            super(NewSequenceFromSelectionAction.NewSequenceFromSelection, self).__init__(parent)
            self.setWindowTitle("New Sequence")
            self.setSizeGripEnabled(False)
            self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
            self.setContentsMargins(15, 10, 10, 0)
            self.layout = QtWidgets.QVBoxLayout()
            self.layout.setContentsMargins(20, 20, 20, 20)

            self.groupBox = QtWidgets.QGroupBox()
            self.groupLayout = QtWidgets.QFormLayout()
            self.groupBox.setLayout(self.groupLayout)

            self.radioSingle = QtWidgets.QRadioButton("Single Sequence", self.groupBox)
            self.radioSingle.setToolTip("Create a single sequence.\
                                        \nAll clips will be stacked on separate layers starting at frame 0\
                                        \nunless Sequential Layers is checked.")
            self.radioSingle.setChecked(True)
            self.radioMultiple = QtWidgets.QRadioButton("Multiple Sequences", self.groupBox)
            self.radioMultiple.setToolTip("Create multiple sequences, one for each clip.\
                                          \nIf 'Use Timecode' is checked each sequence will start at the clip timecode.\
                                          \nOtherwise each sequence will start at the default sequence start.")
            self.groupLayout.setWidget(0, QtWidgets.QFormLayout.SpanningRole, self.radioSingle)
            self.groupLayout.setWidget(1, QtWidgets.QFormLayout.SpanningRole, self.radioMultiple)

            self.spacerItem = QtWidgets.QSpacerItem(15, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
            self.groupLayout.setItem(2, QtWidgets.QFormLayout.LabelRole, self.spacerItem)

            self.sequenceLayers = QtWidgets.QCheckBox("Sequential Layers", self.groupBox)
            self.sequenceLayers.setChecked(True)
            self.sequenceLayers.setToolTip("Place the selected items on one track back to back if Single Sequence is selected.\nOtherwise items are stacked on separate tracks.")
            self.groupLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.sequenceLayers)

            self.includeAudio = QtWidgets.QCheckBox("Include Audio", self.groupBox)
            self.includeAudio.setChecked(True)
            self.includeAudio.setToolTip("Include Audio Tracks from clips containing audio (R3D, QuickTime).")
            self.groupLayout.setWidget(3, QtWidgets.QFormLayout.SpanningRole, self.includeAudio)

            self.useTimecode = QtWidgets.QCheckBox("Use Timecode", self.groupBox)
            self.useTimecode.setChecked(False)
            self.useTimecode.setToolTip("Place clips on the timeline using timecode from the clip's metadata.")
            self.useTimecode.clicked.connect(self.useTimecodeChanged)
            self.groupLayout.setWidget(4, QtWidgets.QFormLayout.SpanningRole, self.useTimecode)

            self.timecodeHorizontalLayout = QtWidgets.QHBoxLayout()
            self.timecodeHorizontalLayout.setSpacing(-1)
            self.timecodeHorizontalLayout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
            self.timecodeHorizontalLayout.setObjectName("timecodeHorizontalLayout")

            self.TCspacerItem = QtWidgets.QSpacerItem(15, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
            self.groupLayout.setItem(5, QtWidgets.QFormLayout.LabelRole, self.TCspacerItem)

            self.timecodeOptionsLabel = QtWidgets.QLabel()
            self.timecodeOptionsLabel.setText("Clips Without Timecode:")
            self.timecodeOptionsLabel.setToolTip("Choose how to handle clips with no inherent timecode.")
            self.timecodeOptionsLabel.setEnabled(self.useTimecode.isChecked() & self.radioSingle.isChecked())

            self.timecodeHorizontalLayout.addWidget(self.timecodeOptionsLabel)
            self.timecodeHorizontalLayout.setStretch(1, 40)
            self.timecodeOptions = QtWidgets.QComboBox()
            self.timecodeOptions.setToolTip("Choose how to handle clips with no inherent timecode:\
                                             \n-Add Stacked will create new tracks for each clip.\
                                             \n-Add To Single Track will lay each clip back to back on one track.\
                                             \n-Ignore will ignore these clips")

            self.timecodeOptions.currentIndexChanged.connect(self.timecodeOptionsChanged)
            self.groupLayout.setWidget(5, QtWidgets.QFormLayout.SpanningRole, self.timecodeOptions)
            self.timecodeOptions.addItem("Add Stacked")
            self.timecodeOptions.addItem("Add To Single Track")
            self.timecodeOptions.addItem("Ignore")
            self.timecodeOptions.setEnabled(self.useTimecode.isChecked() & self.radioSingle.isChecked())
            self.timecodeHorizontalLayout.addWidget(self.timecodeOptions)

            self.groupLayout.setLayout(5, QtWidgets.QFormLayout.SpanningRole, self.timecodeHorizontalLayout)

            self.dividerline1 = QtWidgets.QFrame()
            self.dividerline1.setFrameShape(QtWidgets.QFrame.HLine)
            self.dividerline1.setFrameShadow(QtWidgets.QFrame.Sunken)
            self.groupLayout.setWidget(6, QtWidgets.QFormLayout.SpanningRole, self.dividerline1)

            self.horizontalLayout = QtWidgets.QHBoxLayout()
            self.horizontalLayout.setSpacing(-1)
            self.horizontalLayout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
            self.horizontalLayout.setObjectName("formatHorizontalLayout")
            self.formatLabel = QtWidgets.QLabel()
            self.formatLabel.setText("Format:")
            self.horizontalLayout.addWidget(self.formatLabel)
            self.formatChooser = hiero.ui.FormatChooser()
            self.formatChooser.setToolTip("Choose the format for a New Sequence created from Multiple Selections.")
            self.formatChooser.formatChanged.connect(self.formatChanged)
            self.horizontalLayout.addWidget(self.formatChooser)
            self.horizontalLayout.setStretch(1, 40)
            self.groupLayout.setLayout(7, QtWidgets.QFormLayout.SpanningRole, self.horizontalLayout)

            self.horizontalLayout2 = QtWidgets.QHBoxLayout()
            self.horizontalLayout2.setSpacing(-1)
            self.horizontalLayout2.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
            self.horizontalLayout2.setObjectName("framerateHorizontalLayout")
            self.framerateLabel = QtWidgets.QLabel()
            self.framerateLabel.setText("Frame Rate:")
            self.horizontalLayout2.addWidget(self.framerateLabel)
            self.fpsChooser = QtWidgets.QComboBox()
            self.fpsChooser.setToolTip("Choose the framerate for a New Sequence created from Multiple Selections.")
            self.fpsChooser.currentIndexChanged.connect(self.fpsChanged)
            self.horizontalLayout2.addWidget(self.fpsChooser)
            self.horizontalLayout2.setStretch(1, 40)
            self.spacerItem2 = QtWidgets.QSpacerItem(175, 20, QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
            self.horizontalLayout2.addItem(self.spacerItem2)
            self.groupLayout.setLayout(8, QtWidgets.QFormLayout.SpanningRole, self.horizontalLayout2)

            self.dividerline2 = QtWidgets.QFrame()
            self.dividerline2.setFrameShape(QtWidgets.QFrame.HLine)
            self.dividerline2.setFrameShadow(QtWidgets.QFrame.Sunken)
            self.groupLayout.setWidget(9, QtWidgets.QFormLayout.SpanningRole, self.dividerline2)

            for fps in hiero.core.defaultFrameRates():
                if fps.is_integer():
                    self.fpsChooser.addItem(str(int(fps)))
                else:
                    self.fpsChooser.addItem(str(fps))

            # Add the standard ok/cancel buttons, default to ok.
            self.buttonBox = QtWidgets.QDialogButtonBox(QDialogButtonBox.StandardButton.Cancel | QtWidgets.QDialogButtonBox.StandardButton.Ok)
            self.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Ok).setText("Ok")
            self.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Ok).setAutoDefault(True)
            self.buttonBox.accepted.connect(self.accept)
            self.buttonBox.rejected.connect(self.reject)
            self.groupLayout.addWidget(self.buttonBox)

            self.setLayout(self.groupLayout)
            self.layout.addWidget(self.groupBox)

            self.radioMultiple.clicked.connect(self.sequenceOptionChanged)
            self.radioSingle.clicked.connect(self.sequenceOptionChanged)

            firstclip = hiero.ui.activeView().selection()[0]
            try:
                format = firstclip.activeItem().format()
                self.formatChooser.setCurrentFormat(format)
            except:
                print("Could not set format")

            try:
                fps = firstclip.activeItem().framerate()
                for i in range(self.fpsChooser.count()):
                    if str(self.fpsChooser.itemText(i)) == str(fps):
                        self.fpsChooser.setCurrentIndex(i)
            except:
                self.fpsChooser.setCurrentIndex(6)
                print("Could not set framerate")

        def formatChanged(self):
            format = self.formatChooser.currentFormat()

        def fpsChanged(self):
            fps = self.fpsChooser.currentText()

        def sequenceOptionChanged(self):
            if self.radioMultiple.isChecked():
                self.sequenceLayers.setEnabled(False)
                self.formatChooser.setEnabled(False)
                self.fpsChooser.setEnabled(False)
                self.formatLabel.setEnabled(False)
                self.framerateLabel.setEnabled(False)
            if self.radioSingle.isChecked():
                self.sequenceLayers.setEnabled(True)
                if self.formatChooser.count() > 0:
                    self.formatChooser.setEnabled(True)
                    self.fpsChooser.setEnabled(True)
                    self.formatLabel.setEnabled(True)
                    self.framerateLabel.setEnabled(True)
            self.useTimecodeChanged()

        def useTimecodeChanged(self):
            if self.useTimecode.isChecked() and self.radioSingle.isChecked():
                self.timecodeOptions.setEnabled(True)
                self.timecodeOptionsLabel.setEnabled(True)
            else:
                self.timecodeOptions.setEnabled(False)
                self.timecodeOptionsLabel.setEnabled(False)

        def timecodeOptionsChanged(self):
            timecodeOption = self.timecodeOptions.currentText()
            if timecodeOption == "Ignore":
                self.ignoreZeroStart = True
                self.singleTrackZero = False
            if timecodeOption == "Add Stacked":
                self.ignoreZeroStart = False
                self.singleTrackZero = False
            if timecodeOption == "Add To Single Track":
                self.singleTrackZero = True

    def __init__(self):
        QtGui.QAction.__init__(self, "New Sequence from Selection", None)
        self.triggered.connect(self.doit)
        hiero.core.events.registerInterest((hiero.core.events.EventType.kShowContextMenu, hiero.core.events.EventType.kBin), self.eventHandler)
        hiero.core.events.registerInterest(hiero.core.events.EventType.kSelectionChanged, self.eventHandler)

    def addClipToSequence(self, sequence, clip, start, trackindex, useTimecode=True, includeAudio=True):
        '''Add a clip to a sequence on track number @trackindex starting at frame @start.
           Optionally add linked audio if it exists (i.e. QuickTime or R3D).
        '''
        if useTimecode:
            if clip.mediaSource().hasVideo():
                clipfps = clip.framerate()
                sequencefps = sequence.framerate()
                if clipfps.toInt() != sequencefps.toInt():
                    clipStart = self.convertStartTimecode(clip.timecodeStart(), clipfps.toInt(), sequencefps.toInt())
                    if clipStart < sequence.timecodeStart():
                        start = sequence.timecodeStart() - clipStart
                    else:
                        start = clipStart - sequence.timecodeStart()
                else:
                    if clip.timecodeStart() < sequence.timecodeStart():
                        start = sequence.timecodeStart() - clip.timecodeStart()
                    else:
                        start = clip.timecodeStart() - sequence.timecodeStart()
            else:
                videoClip = None

            if clip.mediaSource().hasAudio() and includeAudio:
                linkedItems = []
                for i in range(clip.numAudioTracks()):
                    audioTrackName = "Audio " + str(i+1)
                    if not self.trackExists(sequence, audioTrackName):
                        newAudioTrack = sequence.addTrack(hiero.core.AudioTrack(audioTrackName))
                    else:
                        newAudioTrack = self.trackExists(sequence, audioTrackName)
                    audioClip = newAudioTrack.addTrackItem(clip, i, start)
                    linkedItems.append(audioClip)
                    if videoClip:
                        audioClip.link(videoClip)
                    else:
                        if len(linkedItems) > 0:
                            audioClip.link(linkedItems[0])
        else:
            try:
                if trackindex == len(sequence.videoTracks()):
                    sequence.addTrack(hiero.core.VideoTrack("Video " + str(trackindex+1)))
                videoClip = sequence.videoTrack(trackindex).addTrackItem(clip, start)
            except:
                print("Failed to add clip")

    def newSequence(self, selection):
        '''Create a new sequence from a selection of clips
           @return hiero.core.Sequence, hiero.core.Bin
        '''
        if isinstance(selection[0], hiero.core.BinItem):
            clip = selection[0].activeItem()
        elif isinstance(selection[0], hiero.core.Clip):
            clip = selection[0]

        bin = clip.binItem().parentBin()
        sequence = hiero.core.Sequence(selection[0].name())

        if clip.mediaSource().hasVideo():
            if clip.mediaSource().metadata()["foundry.source.framerate"]:
                fps = clip.mediaSource().metadata()["foundry.source.framerate"]
            else:
                fps = clip.framerate()
            sequence.setFramerate(hiero.core.TimeBase.fromString(str(fps)))
            sequence.setFormat(clip.format())

            for i in range(clip.numVideoTracks()):
                sequence.addTrack(hiero.core.VideoTrack("Video " + str(i+1)))
                try:
                    videoClip = sequence.videoTrack(i).addTrackItem(clip, 0)
                except:
                    print("Failed to add clip")
        else:
            videoClip = None

        if clip.mediaSource().hasAudio():
            linkItems = []
            for i in range(clip.numAudioTracks()):
                audioTrackName = "Audio " + str( i+1 )
                if not self.trackNameExists(sequence, audioTrackName):
                    newAudioTrack = sequence.addTrack(hiero.core.AudioTrack(audioTrackName))
                else:
                    newAudioTrack = self.trackNameExists(sequence, audioTrackName)
                audioClip = newAudioTrack.addTrackItem(clip, i, 0)
                linkItems.append(audioClip)
                if videoClip:
                    audioClip.link(videoClip)
                else:
                    if len(linkItems) > 0:
                        audioClip.link(linkItems[0])

        return sequence, bin

    def newStereoSequence(self, leftClip, rightClip, baseName, useTimecode):
        '''Create a stereo sequence with Left/Right tagged tracks.
           Eventually when full stereo support is implemented this can be changed.
           @return hiero.core.Sequence
        '''
        bin = leftClip.binItem().parentBin()
        fps = leftClip.framerate()
        sequence = hiero.core.Sequence(baseName)
        sequence.setFramerate(hiero.core.TimeBase.fromString(str(fps)))
        sequence.setFormat(leftClip.format())
        trackLeft = hiero.core.VideoTrack("Left")
        trackRight = hiero.core.VideoTrack("Right")
        sequence.addTrack(trackLeft)
        sequence.addTrack(trackRight)

        if useTimecode:
            start = leftClip.timecodeStart()
        else:
            start = 0

        videoLeft = trackLeft.addTrackItem(leftClip, start)
        videoRight = trackRight.addTrackItem(rightClip, start)

        newStartTC = self.earliestTimecode(sequence)
        sequence.setTimecodeStart(newStartTC)
        sequence.clearRange(0, newStartTC-1, True)

        leftTag = hiero.core.findProjectTags(hiero.core.projects(hiero.core.Project.kStartupProjects)[0], "Left")
        rightTag = hiero.core.findProjectTags(hiero.core.projects(hiero.core.Project.kStartupProjects)[0], "Right")
        trackLeft.addTag(leftTag[0])
        trackRight.addTag(rightTag[0])

        return sequence

    def findStereoPairs(self, selection):
        ''' Look for matching stereo pairs in the selection and return a list.
            Also return the remaining unpaired clips as a list.
            @return list, list
        '''
        stereoPairs = []
        singleFiles = []
        leftrx = re.compile("(.*)(_L(eft)?)$", re.IGNORECASE)
        rightrx = re.compile("(.*)(_R(ight)?)$", re.IGNORECASE)

        for item in selection:
            clip = item.activeItem()

            if not leftrx.match(clip.name()) and not rightrx.match(clip.name()):
                singleFiles.append(clip)

            if leftrx.match(clip.name()):
                baseName = leftrx.match(clip.name()).groups()[0]
                if len(stereoPairs) == 0:
                    stereoPairs.append([clip])
                else:
                    pairMatch = False
                    for i in range(len(stereoPairs)):
                        for item in stereoPairs[i]:
                            if baseName in item.name():
                                stereoPairs[i].append(clip)
                                pairMatch = True
                                break
                    if not pairMatch:
                        stereoPairs.append([clip])

            if rightrx.match(clip.name()):
                baseName = rightrx.match(clip.name()).groups()[0]
                if len(stereoPairs) == 0:
                    stereoPairs.append([clip])
                else:
                    pairMatch = False
                    for i in range(len(stereoPairs)):
                        for item in stereoPairs[i]:
                            if baseName in item.name():
                                stereoPairs[i].append(clip)
                                pairMatch = True
                                break
                    if not pairMatch:
                        stereoPairs.append([clip])

        # Python doesn't like you to remove things from a list while iterating
        # over that same list so we add those items to a new list and then remove
        unmatchedPairs = []

        for pair in stereoPairs:
            if len(pair) == 1:
                unmatchedPairs.append(pair)

        for pair in unmatchedPairs:
            singleFiles.append(pair[0])
            stereoPairs.remove(pair)

        return stereoPairs, singleFiles

    def earliestTimecode(self, sequence):
        '''Find the earliest timeline timecode used in a sequence.
           @return long
        '''
        sequenceTimecodes = []
        for track in sequence:
            for item in track:
                sequenceTimecodes.append(item.timelineIn())

        earliest = sorted(sequenceTimecodes)[0]
        return earliest

    def convertStartTimecode(self, start, sourcerate, destrate):
        '''Convert Start Timecode from @sourcerate to @destrate
           @return destination start timecode in frames
        '''
        # hiero.core.TimeBase.convert should do this, but it doesn't do it correctly
        # so we do it manually here
        startHMSF = hiero.core.Timecode.framesToHMSF(start, sourcerate, False)
        destFrames = hiero.core.Timecode.HMSFToFrames(destrate, False, int(startHMSF[0]), int(startHMSF[1]), int(startHMSF[2]), int(startHMSF[3]))
        return destFrames

    def clipInfo(self, clip, fps):
        '''Get a clip object and framerange
           @return dict
        '''
        if clip.activeItem().mediaSource().hasVideo():
            start = self.convertStartTimecode(clip.activeItem().timecodeStart(), clip.activeItem().framerate().toInt(), fps.toInt())
            end = start + clip.activeItem().duration() - 1
        else:
            # Clip only contains audio and probably no inherent framerate
            start = clip.activeItem().timecodeStart()
            end = start + clip.activeItem().duration() - 1

        clipInfo = {}
        clipInfo['clip'] = {}
        clipInfo['clip']['src'] = clip
        clipInfo['clip']['framerange'] = list(range(start, end))

        return clipInfo

    def buildTracksFromTimecode(self, selection, siftedselection, fps, tracks, trackindex=0, ignoreZeroStart=False, singleTrackZero=False):
        '''Create a dictionary of tracks using clip timecode to check for overlaps.
           If timecode overlaps the clip is placed on a new track.
           @selection: the clips selected
           @siftedselection: list of items without timecode (or starting at 00:00:00:00 which is usually the same thing)
           @fps: framerate
           @tracks: empty dict we fill in using @selection
           @trackindex: the current track index
           @ignoreZeroStart: flag to ignore clips that start with timecode 00:00:00:00
           @singleTrackZero: flag to indicate we want to add clips with start timecode 00:00:00:00 to a single track (True) or stacked (False)
           @return dict of tracks, list of clips with
        '''
        for clip in selection:
            try:
                # check if the track has been initialized
                tracks[trackindex]
            except:
                # track hasn't been initialized yet so do it here
                tracks[trackindex] = []

            clipInfo = self.clipInfo(clip, fps)
            cliprange = clipInfo['clip']['framerange']

            # if the current track is not empty we need to check if the clip conflicts with
            # existing track items before we can add it
            if len(tracks[trackindex]) > 0:
                for i in tracks:
                    if clip in tracks[i]:
                        clipexists = True
                        break
                    else:
                        clipexists = False

                # we didn't find the clip already on a track so we are ready to add it
                if not clipexists:
                    overlap = False
                    for item in tracks[trackindex]:
                        iteminfo = self.clipInfo(item, fps)
                        itemrange = iteminfo['clip']['framerange']
                        # If the ranges overlap we will get a list by adding sets (i.e. set(A) & set(B)).
                        # If there is no overlap the list will be empty.
                        if list(set(cliprange) & set(itemrange)):
                            # clips overlap so try to add the clip to the next track
                            overlap = True
                            break

                    if overlap:
                        # conflict was found so increment the current track by 1 and try again
                        self.buildTracksFromTimecode([clip], siftedselection, fps, tracks, trackindex=trackindex+1, ignoreZeroStart=ignoreZeroStart, singleTrackZero=singleTrackZero)
                    else:
                        # no conflict on this track so add the clip
                        tracks[trackindex].append(clip)
            else:
                for i in tracks:
                    if clip in tracks[i]:
                        clipexists = True
                        break
                    else:
                        clipexists = False
                if not clipexists:
                    tracks[trackindex].append(clip)

        return tracks, siftedselection

    def trackNameExists(self, sequence, trackName):
        for track in sequence:
            if track.name() == trackName:
                return track
        return None

    def recursiveClips(self, selection, clips):
        for s in selection:
            if isinstance(s, hiero.core.Bin):
                for item in list(s.items()):
                    if isinstance(item, hiero.core.BinItem) and hasattr(item, "activeItem"):
                        if isinstance(item.activeItem(), hiero.core.Clip):
                            clips.append(item)
                    elif isinstance(item, hiero.core.Bin):
                        self.recursiveClips([item], clips)
            elif isinstance(s, hiero.core.BinItem) and hasattr(s, "activeItem"):
                if isinstance(s.activeItem(), hiero.core.Clip):
                    clips.append(s)

        return clips

# Change doit to reallydoit to get a trace for debugging.
#   def doit(self):
#     tracer = trace.Trace( count=False, trace=True )
#     tracer.runfunc( NewSequenceFromSelectionAction.reallydoit, self )

    def doit(self):
        selection = list(hiero.ui.activeView().selection())
        ignoredItems = []
        for selected in selection:
            if isinstance(selected, hiero.core.BinItem) and hasattr(selected, "activeItem"):
                if isinstance(selected.activeItem(), hiero.core.Sequence):
                    ignoredItems.append(selected)

        if ignoredItems:
            for item in ignoredItems:
                selection.remove(item)

        isStereo = False

        if len(selection) == 1 and isinstance(selection[0], hiero.core.Bin):
            clips = []
            selection = self.recursiveClips(selection, clips)

        # if only one item is selected and it's a clip, not a bin
        if len(selection) == 1:
            if isinstance(selection[0], hiero.core.BinItem) or isinstance(selection[0], hiero.core.Clip):
                sequence, bin = self.newSequence(selection)
                bin.addItem(hiero.core.BinItem(sequence))
                sequence.editFinished()

        # If exactly 2 items are selected we check to see if they are a stereo pair
        # If yes then create a stereo sequence. Otherwise treat them as 2 normal clips
        if len(selection) == 2:
            hiero.core.projects()[-1].beginUndo("New Stereo Sequence")
            leftrx = re.compile("(.*)(_L(eft)?)$", re.IGNORECASE)
            rightrx = re.compile("(.*)(_R(ight)?)$", re.IGNORECASE)
            stereoPairs, singleFiles = self.findStereoPairs(selection)

            if stereoPairs:
                isStereo = True
                for pair in stereoPairs:

                    if len(pair) == 2:
                        if leftrx.match(pair[0].name()) and rightrx.match(pair[1].name()):
                            leftClip = pair[0]
                            rightClip = pair[1]
                        else:
                            leftClip = pair[1]
                            rightClip = pair[0]
                        try:
                            newName = leftrx.match(leftClip.name()).groups()[0]
                            sequence = self.newStereoSequence(leftClip, rightClip, newName, False)
                            bin = leftClip.binItem().parentBin()
                            if sequence:
                                bin.addItem(hiero.core.BinItem(sequence))
                                sequence.editFinished()
                        except:
                            print("Error creating Stereo Sequence")
            hiero.core.projects()[-1].endUndo()

        # If multiple clips are selected and we want a Single Sequence
        if len(selection) > 1 and isStereo == False:
            dialog = self.NewSequenceFromSelection(selection)
            if dialog.exec_():
                if dialog.radioSingle.isChecked() == True:
                    hiero.core.projects()[-1].beginUndo("New Sequence From Selection")
                    newFPS = hiero.core.TimeBase.fromString(str(dialog.fpsChooser.currentText()))
                    newResolution = dialog.formatChooser.currentFormat()

                    # If we want to stack clips on separate layers
                    if dialog.sequenceLayers.isChecked() == False:
                        clip = selection[0].activeItem()
                        bin = clip.binItem().parentBin()
                        sequence = hiero.core.Sequence(selection[0].name())

                        if clip.mediaSource().hasVideo():
                            if clip.mediaSource().metadata()["foundry.source.framerate"]:
                                fps = clip.mediaSource().metadata()["foundry.source.framerate"]
                            else:
                                fps = clip.framerate()
                            sequence.setFramerate(newFPS)
                            sequence.setFormat(newResolution)
                        else:
                            videoClip = None

                        k = 0
                        j = 0
                        for item in selection:
                            clip = item.activeItem()
                            if clip.mediaSource().hasVideo():
                                for i in range(clip.numVideoTracks()):
                                    newVideoTrack = sequence.addTrack(hiero.core.VideoTrack("Video " + str(k+1)))
                                    try:
                                        videoClip = newVideoTrack.addTrackItem(clip, 0)
                                    except:
                                        print("Failed to add clip")
                            else:
                                videoClip = None
                            k+=1

                            if clip.mediaSource().hasAudio() and dialog.includeAudio.isChecked():
                                linkedItems = []
                                for i in range(clip.numAudioTracks()):
                                    newAudioTrack = sequence.addTrack(hiero.core.AudioTrack("Audio " + str(j+i+1) ))
                                    audioClip = newAudioTrack.addTrackItem(clip, i, 0)
                                    linkedItems.append(audioClip)
                                    if videoClip:
                                        audioClip.link(videoClip)
                                    else:
                                        if len(linkedItems) > 0:
                                            audioClip.link(linkedItems[0])
                                    j+=1

                        bin.addItem(hiero.core.BinItem(sequence))
                        sequence.editFinished()
                        hiero.core.projects()[-1].endUndo()

                    # Add the clips to the sequence back to back on one video layer ignoring timecode
                    if dialog.sequenceLayers.isChecked() == True:
                        if not dialog.useTimecode.isChecked():
                            clip = selection[0].activeItem()
                            bin = clip.binItem().parentBin()
                            sequence = hiero.core.Sequence(selection[0].name())

                            if clip.mediaSource().hasVideo():
                                if clip.mediaSource().metadata()["foundry.source.framerate"]:
                                    fps = hiero.core.TimeBase(clip.mediaSource().metadata()["foundry.source.framerate"])
                                else:
                                    fps = clip.framerate()
                                sequence.setFramerate(newFPS)
                                sequence.setFormat(newResolution)
                            else:
                                videoClip = None
                                fps = sequence.framerate()

                            sequence.addTrack(hiero.core.VideoTrack("Video 1"))
                            k = 0
                            for item in selection:
                                clip = item.activeItem()

                                if clip.mediaSource().hasVideo():
                                    for i in range(clip.numVideoTracks()):
                                        try:
                                            videoClip = sequence.videoTrack(0).addTrackItem(clip, k)
                                        except:
                                            print("Failed to add clip")
                                        outTime = clip.duration() - 1
                                else:
                                    videoClip = None
                                    outTime = None

                                if clip.mediaSource().hasAudio() and dialog.includeAudio.isChecked():
                                    linkedItems = []
                                    for i in range(clip.numAudioTracks()):
                                        audioTrackName = "Audio " + str( i+1 )
                                        if not self.trackNameExists(sequence, audioTrackName):
                                            newAudioTrack = sequence.addTrack(hiero.core.AudioTrack(audioTrackName))
                                        else:
                                            newAudioTrack = self.trackNameExists(sequence, audioTrackName)

                                        audioClip = newAudioTrack.addTrackItem(clip, i, k)
                                        linkedItems.append(audioClip)
                                        if videoClip:
                                            audioClip.link(videoClip)
                                        else:
                                            if len(linkedItems) > 0:
                                                audioClip.link(linkedItems[0])
                                                # Audio only duration is currently returned in samples so we need to convert to frames
                                                outTime = ( float(clip.duration() / 48000) * fps.toFloat() ) - 1
                                k += (outTime + 1)
                            bin.addItem(hiero.core.BinItem(sequence))
                            sequence.editFinished()
                            hiero.core.projects()[-1].endUndo()
                        else:
                            # Build the sequence based on clip timecode.
                            tracks = {}
                            tracks[0] = []

                            selection = list(selection)
                            siftedselection = []

                            for clip in selection:
                                if clip.activeItem().timecodeStart() == 0:
                                    if dialog.ignoreZeroStart or dialog.singleTrackZero:
                                        siftedselection.append(clip)
                            if siftedselection:
                                for item in siftedselection:
                                    selection.remove(item)

                            if dialog.ignoreZeroStart:
                                cliptracks, zeroStartClips = self.buildTracksFromTimecode(selection, siftedselection, newFPS, tracks, 0, True)
                            elif not dialog.ignoreZeroStart and dialog.singleTrackZero:
                                cliptracks, zeroStartClips = self.buildTracksFromTimecode(selection, siftedselection, newFPS, tracks, 0, False, True)
                            else:
                                cliptracks, zeroStartClips = self.buildTracksFromTimecode(selection, siftedselection, newFPS, tracks, 0, False)
                            if cliptracks:
                                clip1 = cliptracks[0][0]
                                bin = clip1.parentBin()
                                # Name the sequence based on the first clip in the selection
                                sequence = hiero.core.Sequence(clip1.name())
                                sequence.setFramerate(newFPS)
                                sequence.setFormat(newResolution)
                                # Set the start timecode to zero so everything is added correctly
                                # If the default is set to 01:00:00:00 and we try to add clips with
                                # timecode before hour 01 then it won't work so start with zero and
                                # change it back later if necessary
                                sequence.setTimecodeStart(0)

                                for trackindex in range(len(cliptracks)):
                                    sequence.addTrack(hiero.core.VideoTrack("Video %s" % (trackindex + 1)))

                                    for item in cliptracks[trackindex]:
                                        clip = item.activeItem()
                                        if not clip.mediaSource().hasVideo():
                                            videoClip = None
                                            fps = sequence.framerate()

                                        if clip.mediaSource().hasVideo():
                                            for i in range(clip.numVideoTracks()):
                                                clipfps = clip.framerate()
                                                sequencefps = sequence.framerate()
                                                if clipfps.toInt() != sequencefps.toInt():
                                                    # Convert start timecodes to the destination fps
                                                    # This will ensure that the first frame of each clip with a different timebase
                                                    # is still at the correct timecode
                                                    clipStart = self.convertStartTimecode(clip.timecodeStart(), clipfps.toInt(), sequencefps.toInt())
                                                    if clipStart < sequence.timecodeStart():
                                                        sequence.setTimecodeStart(clipStart)
                                                        start = sequence.timecodeStart() - clipStart
                                                    else:
                                                        start = clipStart - sequence.timecodeStart()
                                                else:
                                                    if clip.timecodeStart() < sequence.timecodeStart():
                                                        sequence.setTimecodeStart(clip.timecodeStart())
                                                        start = sequence.timecodeStart() - clip.timecodeStart()
                                                    else:
                                                        start = clip.timecodeStart() - sequence.timecodeStart()
                                                try:
                                                    videoClip = sequence.videoTrack(trackindex).addTrackItem(clip, start)
                                                except:
                                                    print("Failed to add clip")
                                        else:
                                            videoClip = None

                                        if clip.mediaSource().hasAudio() and dialog.includeAudio.isChecked():
                                            linkedItems = []
                                            for i in range(clip.numAudioTracks()):
                                                print("Source track Num", i)
                                                print("Trackindex", trackindex)
                                                try:
                                                    newAudioTrack = sequence.audioTrack(trackindex + i)
                                                except:
                                                    newAudioTrack = sequence.addTrack(hiero.core.AudioTrack("Audio %s" % (trackindex + i + 1)))
                                                audioClip = newAudioTrack.addTrackItem(clip, i, start)
                                                linkedItems.append(audioClip)
                                                if videoClip:
                                                    audioClip.link(videoClip)
                                                else:
                                                    if len(linkedItems) > 0:
                                                        audioClip.link(linkedItems[0])

                            if siftedselection and dialog.singleTrackZero:
                                # len will give us current track count (len([0,1]) = 2 so 2 is the next trackindex)
                                trackindex = len(cliptracks)
                                start = 0
                                for selection in siftedselection:
                                    clip = selection.activeItem()
                                    self.addClipToSequence(sequence, clip, start, trackindex, False, False)
                                    start += clip.duration()
                            bin.addItem(hiero.core.BinItem(sequence))
                            # Set the start timecode to the earliest clip start time in the sequence
                            # and then remove anything between 0 and the first clip.
                            newStartTC = self.earliestTimecode(sequence)
                            sequence.setTimecodeStart(newStartTC)
                            sequence.clearRange(0, newStartTC-1, True)
                            sequence.editFinished()
                            hiero.core.projects()[-1].endUndo()

                # Multiple Sequences from selection
                if dialog.radioMultiple.isChecked():
                    hiero.core.projects()[-1].beginUndo("Multiple Sequences From Selection")
                    leftrx = re.compile("(.*)(_L(eft)?)$", re.IGNORECASE)
                    rightrx = re.compile("(.*)(_R(ight)?)$", re.IGNORECASE)

                    stereoPairs, singleFiles = self.findStereoPairs(selection)

                    for pair in stereoPairs:

                        if len(pair) == 2:
                            if leftrx.match(pair[0].name()) and rightrx.match(pair[1].name()):
                                leftClip = pair[0]
                                rightClip = pair[1]
                            else:
                                leftClip = pair[1]
                                rightClip = pair[0]

                            try:
                                newName = leftrx.match(leftClip.name()).groups()[0]
                                sequence = self.newStereoSequence(leftClip, rightClip, newName, dialog.useTimecode.isChecked())
                                bin = leftClip.binItem().parentBin()
                                if sequence:
                                    bin.addItem(hiero.core.BinItem(sequence))
                                    sequence.editFinished()
                            except:
                                print("Error creating Stereo Sequence")

                    for clip in singleFiles:

                        bin = clip.binItem().parentBin()
                        sequence = hiero.core.Sequence(clip.name())

                        if clip.mediaSource().hasVideo():
                            if clip.mediaSource().metadata()["foundry.source.framerate"]:
                                fps = clip.mediaSource().metadata()["foundry.source.framerate"]
                            else:
                                fps = clip.framerate()
                            sequence.setFramerate(hiero.core.TimeBase.fromString(str(fps)))
                            sequence.setFormat(clip.format())

                            if dialog.useTimecode.isChecked():
                                start = clip.timecodeStart()
                                sequence.setTimecodeStart(0)
                            else:
                                start = 0
                            for i in range(clip.numVideoTracks()):
                                sequence.addTrack(hiero.core.VideoTrack("Video " + str(i+1)))
                                try:
                                    videoClip = sequence.videoTrack(i).addTrackItem(clip, start)
                                except:
                                    print("Failed to add clip")
                                    videoClip = None
                        else:
                            videoClip = None

                        if clip.mediaSource().hasAudio() and dialog.includeAudio.isChecked():
                            linkedItems = []

                            if dialog.useTimecode.isChecked():
                                start = clip.timecodeStart()
                                sequence.setTimecodeStart(0)
                            else:
                                start = 0

                            for i in range(clip.numAudioTracks()):
                                audioTrackName = "Audio " + str( i+1 )
                                if not self.trackNameExists(sequence, audioTrackName):
                                    newAudioTrack = sequence.addTrack(hiero.core.AudioTrack(audioTrackName))
                                else:
                                    newAudioTrack = self.trackNameExists(sequence, audioTrackName)
                                audioClip = newAudioTrack.addTrackItem(clip, i, start)
                                linkedItems.append(audioClip)
                                if videoClip:
                                    audioClip.link(videoClip)
                                else:
                                    if len(linkedItems) > 0:
                                        audioClip.link(linkedItems[0])

                        bin.addItem(hiero.core.BinItem(sequence))
                        sequence.editFinished()
                    hiero.core.projects()[-1].endUndo()

    def eventHandler(self, event):
        if not hasattr(event.sender, 'selection'):
            return

        # Disable if nothing is selected
        selection = event.sender.selection()

        if selection is None:
            selection = ()

        title = "New Sequence from Selection"
        self.setText(title)
        self.setEnabled( len(selection) > 0 )
        if hasattr(event, "menu"):
            event.menu.addAction(self)

action = NewSequenceFromSelectionAction()
