import c4d
from ssp_utils_c4d_version import *
from ssp_utils_dialog import *
from ssp_utils_image import *
from ssp_ids import *
from ssp_utils_pref import *
from ssp_utils import *
from ssp_dialog_about import DialogAbout
from ssp_dialog_manage_sessions import DialogManageSessions, CommandManageSessions
from ssp_thumb_render import DeferredThumbRendering


####################################################################################################
# Class DialogMain #################################################################################
####################################################################################################
ID_GROUP_MAIN_DIALOG                 = 1000
ID_GROUP_DESIGN_COMBO                = 1001
ID_GROUP_DESIGN_TABS                 = 1002
ID_GROUP_DESIGN_THUMBS               = 1003
ID_SCROLLGROUP_SCENES                = 1005
ID_GROUP_CONTENT_SCROLLGROUP_SCENES  = 1006
ID_GROUP_SCENES_THUMBNAIL_COLUMNS    = 1007

ID_CALL_PREFERENCES                  = 2000
ID_COMBO_SCENES                      = 2001
ID_QUICKTAB_SCENES                   = 2002
ID_BITMAPBUTTON_PREFS                = 2003
ID_CALL_ABOUT                        = 2004
ID_UPDATE_CURRENT_THUMB              = 2005
ID_SESSION_STORE                     = 2006
ID_ADD_THUMB_CAM                     = 2007
ID_ADD_THUMB_RENDER_SETTINGS         = 2008
ID_SESSION_MANAGE                    = 2009
ID_CALL_SAVE_LAYOUT                  = 2010
ID_BITMAPBUTTON_ENABLE_THUMBS        = 2011
ID_BITMAPBUTTON_ENABLE_COMBO         = 2012
ID_BITMAPBUTTON_ENABLE_TABS          = 2013
ID_BITMAPBUTTON_THUMB_SIZE           = 2014
ID_BUTTON_NEXT_DOC                   = 2015
ID_BUTTON_PREV_DOC                   = 2016

ID_SESSION_IMPORT                    = 2017
ID_SESSION_EXPORT                    = 2018

ID_CALL_WELCOME                      = 2019

ID_BUTTON_NEW_PROJECT                = 2020


class DialogMain(c4d.gui.GeDialog):
    _quickTabScenes = None
    _bitmapButtonEnableCombo = None
    _bitmapButtonEnableTabs = None
    _bitmapButtonEnableThumbs = None
    _bitmapButtonThumbSize = None

    _bmpThumbRenderBusy = None
    _bmpActiveProject = None
    _bmpActiveProjectSmall = None

    _thumbnails = {}
    _thumbResolutions = None
    _thumbRenderer = None

    _timeLastThumbnailClick = MyTime()


    def __init__(self):
        self._quickTabScenes = None
        self._bitmapButtonEnableCombo = None
        self._bitmapButtonEnableTabs = None
        self._bitmapButtonEnableThumbs = None
        self._bitmapButtonThumbSize = None

        self.InitThumbRenderBusy()
        self.InitActiveProjectLabel()

        self._thumbnails = {}
        self.InitThumbSizeIcons()
        self._thumbRenderer = DeferredThumbRendering(self._thumbResolutions[-1][0], self._thumbResolutions[-1][1])

        self._timeLastThumbnailClick = MyTime()

        c4d.gui.GeDialog.__init__(self)


    def InitThumbRenderBusy(self):
        if c4d.GetC4DVersion() >= 21000:
            self._bmpThumbRenderBusy = GetIconImage(440000299)

        else:
            self._bmpThumbRenderBusy = GetIconImage(431000005)

    def InitActiveProjectLabel(self, subfolder='img'):
        self._bmpActiveProject = LoadImage('active_project', subfolder=subfolder)
        self._bmpActiveProjectSmall = LoadImage('active_project_small', subfolder=subfolder)


    def InitThumbSizeIcons(self):
        self._thumbResolutions = list(THUMB_RESOLUTIONS)

        versionC4DMajor = c4d.GetC4DVersion() // 1000
        if versionC4DMajor >= 25:
            self._thumbResolutions[0][3] = GetIconImage(16740, factor=0.5) # Inherit Small Icons # TODO Andreas: symbol?
            self._thumbResolutions[1][3] = GetIconImage(16741, factor=0.5) # Inherit Medium Icons # TODO Andreas: symbol?
            self._thumbResolutions[2][3] = GetIconImage(16742, factor=0.5) # Inherit Large Icons # TODO Andreas: symbol?

        elif versionC4DMajor >= 20:
            self._thumbResolutions[0][3] = GetIconImage(c4d.RESOURCEIMAGE_BROWSER_SMALLVIEW)
            self._thumbResolutions[1][3] = GetIconImage(c4d.RESOURCEIMAGE_TIMELINE_STATE5)
            self._thumbResolutions[2][3] = GetIconImage(c4d.RESOURCEIMAGE_BROWSER_BIGVIEW)

        else:
            self._thumbResolutions[0][3] = GetIconImage(c4d.RESOURCEIMAGE_OBJECTMANAGER_DOT3)
            self._thumbResolutions[1][3] = GetIconImage(c4d.RESOURCEIMAGE_TIMELINE_STATE5)
            self._thumbResolutions[2][3] = GetIconImage(c4d.RESOURCEIMAGE_KEYFRAME_BUTTON_UP)


    def CreateLayoutAddMenu_File(self, sessions):
        if self.MenuSubBegin('Menu'):
            if len(sessions) > 0:
                if self.MenuSubBegin('Sessions'):
                    for idxSession, nameSession in enumerate(sessions):
                        self.MenuAddString(ID_BASE_SESSION_MENU + idxSession, nameSession)

                self.MenuSubEnd() # Sessions

            #else:
            #    self.MenuAddString(42, 'No session stored, yet')

            self.MenuAddString(ID_SESSION_STORE, 'Store Session...')
            #self.MenuAddString(ID_SESSION_IMPORT, 'Export Session...')
            #self.MenuAddString(ID_SESSION_EXPORT, 'Import Session...')
            self.MenuAddString(ID_SESSION_MANAGE, 'Manage Sessions...')

            self.MenuAddSeparator()

            self.MenuAddString(ID_CALL_SAVE_LAYOUT, 'Save as Startup Layout')

            self.MenuAddSeparator()

            self.MenuAddString(ID_CALL_PREFERENCES, 'Preferences...')

            self.MenuAddSeparator()

            if ENABLE_THUMB_COMMANDS and GetPrefThumbEnabled():
                if GetPrefThumbModeRender():
                    self.MenuAddString(ID_ADD_THUMB_CAM, 'Add a Thumb Camera to Current Scene')
                    self.MenuAddString(ID_ADD_THUMB_RENDER_SETTINGS, 'Add Thumb Render Settings to Current Scene')

                self.MenuAddString(ID_UPDATE_CURRENT_THUMB, 'Update Thumnail of Current Scene')

                self.MenuAddSeparator()

            #self.MenuAddString(ID_CALL_WELCOME, 'Introduction...')

            self.MenuAddString(ID_CALL_ABOUT, 'About...')

        self.MenuSubEnd() # File


    def CreateLayoutAddMenu_OpenScenes(self, bcScenesNames, bcScenesIsActive):
        if self.MenuSubBegin('Projects'):
            for idxDoc, (idDoc, nameDoc) in enumerate(bcScenesNames):
                sShortcut = ''

                if GetPrefShowShortcuts():
                    sShortcut = SwitchShortcutToString(idxDoc)

                self.MenuAddString(idDoc, nameDoc + sShortcut)
                self.MenuInitString(idDoc, True, bcScenesIsActive[idDoc])

        self.MenuSubEnd() # Open Scenes


    def CreateLayoutAddMenu(self):
        self.MenuFlushAll()

        if GetPrefMenuFilesEnabled():
            sessions = SessionGetSessions()
            self.CreateLayoutAddMenu_File(sessions)

        if GetPrefMenuScenesEnabled():
            bcScenesNames = GetScenesNames()
            bcScenesIsActive = GetScenesIsActive()
            self.CreateLayoutAddMenu_OpenScenes(bcScenesNames, bcScenesIsActive)

        self.MenuFinished()


    def CreateLayoutAddMenuCommandBar(self):
        versionC4DMajor = c4d.GetC4DVersion() // 1000

        if versionC4DMajor >= 21:
            # TODO Andreas: 1026693 // Settings Icon ID symbol?
            # TODO Andreas: 1026694 // Settings off Icon ID symbol?
            idIconPrefs = 1026694
            idIconTabsOn = c4d.RESOURCEIMAGE_LAYERMANAGER_STATE3
            idIconTabsOff = c4d.RESOURCEIMAGE_LAYERMANAGER_STATE4

        else:
            idIconPrefs = 1026693
            idIconTabsOn = 465001796
            idIconTabsOff = 465001732

        if versionC4DMajor >= 25:
            idIconPrev = 300000126 # AM Left
            idIconNext = 300000127 # AM Right

        else:
            # 13858: Next Project, 13857: Previous Project
            idIconPrev = 1025781 # Previous day
            idIconNext = 1025779 # Next Day

        self.GroupBeginInMenuLine()

        if GetPrefPrevNextButtons():
            CreateLayoutAddBitmapButton(self, ID_BUTTON_PREV_DOC, idIconPrev, \
                                        tooltip='Previous Project', button=True, toggle=False, flags=c4d.BFH_RIGHT, inMenu=True)
            CreateLayoutAddBitmapButton(self, ID_BUTTON_NEXT_DOC, idIconNext, \
                                        tooltip='Next Project', button=True, toggle=False, flags=c4d.BFH_RIGHT, inMenu=True)
            bmpPlus = GetIconImage(1033505, factor=0.75) # Add Color
            CreateLayoutAddBitmapButton(self, ID_BUTTON_NEW_PROJECT, bmp1=bmpPlus, \
                                        tooltip='New Project', button=True, toggle=False, flags=c4d.BFH_RIGHT, inMenu=True)

        if GetPrefDesignButtons():
            self.AddStaticText(0, c4d.BFH_RIGHT, initw=15, name=' ') # Spacer

            self._bitmapButtonEnableCombo = CreateLayoutAddBitmapButton(self, ID_BITMAPBUTTON_ENABLE_COMBO, \
                                                                        c4d.RESOURCEIMAGE_LAYERMANAGER_STATE6, idIconState2=c4d.RESOURCEIMAGE_LAYERMANAGER_STATE5, \
                                                                        tooltip='Show Dropdown', button=True, toggle=True, flags=c4d.BFH_RIGHT|c4d.BFV_CENTER, inMenu=True)
            self._bitmapButtonEnableTabs = CreateLayoutAddBitmapButton(self, ID_BITMAPBUTTON_ENABLE_TABS, \
                                                                       idIconTabsOff, idIconState2=idIconTabsOn, \
                                                                       tooltip='Show Tabs', button=True, toggle=True, flags=c4d.BFH_RIGHT|c4d.BFV_CENTER, inMenu=True)
            self._bitmapButtonEnableThumbs = CreateLayoutAddBitmapButton(self, ID_BITMAPBUTTON_ENABLE_THUMBS, \
                                                                         c4d.RESOURCEIMAGE_LAYERMANAGER_STATE2, idIconState2=c4d.RESOURCEIMAGE_LAYERMANAGER_STATE1, \
                                                                         tooltip='Show Thumbnails', button=True, toggle=True, flags=c4d.BFH_RIGHT|c4d.BFV_CENTER, inMenu=True)
            self._bitmapButtonThumbSize = CreateLayoutAddBitmapButton(self, ID_BITMAPBUTTON_THUMB_SIZE, \
                                                                      c4d.RESOURCEIMAGE_BROWSER_SMALLVIEW, \
                                                                      tooltip='Thumbnail Size', button=True, toggle=False, flags=c4d.BFH_RIGHT|c4d.BFV_CENTER, inMenu=True)

        CreateLayoutAddBitmapButton(self, ID_CALL_PREFERENCES, idIconPrefs, tooltip='Preferences', button=True, toggle=False, flags=c4d.BFH_RIGHT|c4d.BFV_CENTER, inMenu=True)

        self.GroupEnd()


    def CreateLayoutGroupDesignCombo(self):
        if GetPrefLargeCombo():
            h = 30
        else:
            h = 0

        if self.GroupBegin(ID_GROUP_DESIGN_COMBO, c4d.BFH_SCALEFIT | c4d.BFV_TOP):
            self.AddComboBox(ID_COMBO_SCENES, c4d.BFH_SCALEFIT | c4d.BFV_CENTER, inith=h)

        self.GroupEnd() # ID_GROUP_DESIGN_COMBO


    def CreateLayoutGroupDesignTabs(self):
        bcScenesNames = GetScenesNames()
        bcScenesIsActive = GetScenesIsActive()

        if self.GroupBegin(ID_GROUP_DESIGN_TABS, c4d.BFH_SCALEFIT | c4d.BFV_TOP):
            self._quickTabScenes = CreateLayoutAddQuickTab(self, ID_QUICKTAB_SCENES)

        self.GroupEnd() # ID_GROUP_DESIGN_TABS


    def CreateLayoutGroupDesignThumbs(self):
        if self.GroupBegin(ID_GROUP_DESIGN_THUMBS, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT):
            if self.ScrollGroupBegin(ID_SCROLLGROUP_SCENES, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, c4d.SCROLLGROUP_HORIZ | c4d.SCROLLGROUP_AUTOHORIZ | c4d.SCROLLGROUP_VERT | c4d.SCROLLGROUP_AUTOVERT, initw=0, inith=37):
                if self.GroupBegin(ID_GROUP_CONTENT_SCROLLGROUP_SCENES, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, cols=1):
                    self.GroupBorderSpace(0, 0, 0, 0)

                    if self.GroupBegin(ID_GROUP_SCENES_THUMBNAIL_COLUMNS, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, cols=1):
                        pass # filled in CreateScenesThumbnailGroup()

                    self.GroupEnd() # ID_GROUP_SCENES_THUMBNAIL_COLUMNS

                self.GroupEnd() # ID_GROUP_CONTENT_SCROLLGROUP_SCENES

            self.GroupEnd() # ID_SCROLLGROUP_SCENES

        self.GroupEnd() # ID_GROUP_DESIGN_THUMBS


    def ThumbHeightMax(self, previewHeight):
        thumbHeightMax = 0

        for idThumb, thumb in self._thumbnails.items():
            if thumb[1] is None:
                continue

            wThumb, hThumb = thumb[1].GetSize()
            thumbHeightMax = max(hThumb, thumbHeightMax)

        if thumbHeightMax == 0:
            thumbHeightMax = previewHeight

        return thumbHeightMax



    def ThumbAddActiveMarker(self, bmpThumb):
        drawOverlay = GetPrefThumbOverlay()
        if drawOverlay == 0:
            return

        wThumb, hThumb = bmpThumb.GetSize()
        bmMarker = GetPrefThumbMarkerImage()

        if bmMarker is None:
            print('{0} ERROR: No marker image!'.format(PLUGIN_NAME))
            return

        wMarker, hMarker = bmMarker.GetSize()

        vMarkerColor = GetPrefThumbMarkerColor()
        rMarker = int(vMarkerColor.x * 255.0)
        gMarker = int(vMarkerColor.y * 255.0)
        bMarker = int(vMarkerColor.z * 255.0)

        bmpMarkerAlpha = bmMarker.GetInternalChannel()

        for y in range(hMarker):
            for x in range(wMarker):
                #rMarker, gMarker, bMarker = bmMarker.GetPixel(x, y)
                aMarker = float(bmMarker.GetAlphaPixel(bmpMarkerAlpha, x, y)) / 255.0

                xl = x
                xr = wThumb - x
                yt = y
                yb = hThumb - y

                if drawOverlay & 0x01:
                    BlendPixel(bmpThumb, rMarker, gMarker, bMarker, aMarker, xl, yt) # LT

                if drawOverlay & 0x02:
                    BlendPixel(bmpThumb, rMarker, gMarker, bMarker, aMarker, xr, yt) # RT

                if drawOverlay & 0x04:
                    BlendPixel(bmpThumb, rMarker, gMarker, bMarker, aMarker, xl, yb) # LB

                if drawOverlay & 0x08:
                    BlendPixel(bmpThumb, rMarker, gMarker, bMarker, aMarker, xr, yb) # RB

        if (drawOverlay & 0x10) == 0:
            return

        if GetPrefThumbSize() == 0:
            bmpActiveProject = self._bmpActiveProjectSmall

        else:
            bmpActiveProject = self._bmpActiveProject

        if bmpActiveProject is None:
            print('{0} ERROR: No active project image!'.format(PLUGIN_NAME))
            return

        wActiveProject, hActiveProject = bmpActiveProject.GetSize()
        xOffs = (wThumb - wActiveProject) // 2
        yOffs = (hThumb - hActiveProject) // 2

        bmpActiveProjectAlpha = bmpActiveProject.GetInternalChannel()

        vLabelColor = GetPrefThumbLabelColor()
        rActiveProject = int(vLabelColor.x * 255.0)
        gActiveProject = int(vLabelColor.y * 255.0)
        bActiveProject = int(vLabelColor.z * 255.0)

        for y in range(hActiveProject):
            for x in range(wActiveProject):
                #rActiveProject, gActiveProject, bActiveProject = bmpActiveProject.GetPixel(x, y)

                aActiveProject = float(bmpActiveProject.GetAlphaPixel(bmpActiveProjectAlpha, x, y)) / 255.0

                BlendPixel(bmpThumb, rActiveProject, gActiveProject, bActiveProject, aActiveProject, xOffs + x, yOffs + y)


    def CreateScenesThumbnailGroup(self, noScroll=False):
        if len(self._thumbnails) <= 0:
            return False

        dim = self.GetItemDim(ID_SCROLLGROUP_SCENES)

        scrollW = dim['w']
        if scrollW == 0:
            scrollW = 300 # may happen on init? arbitrary value, shouldn't be needed

        thumbSize = GetPrefThumbSize()
        thumbOverlay = GetPrefThumbOverlay()
        hideActive = GetPrefThumbHideActive()
        fadeThumb = GetPrefThumbFadeActive()

        previewW, previewH, _, _ = self._thumbResolutions[thumbSize]

        numColumns = (scrollW + 24) // (previewW + 8 + 3)  # (w + 24): negative tolerance on the right (change columns only after thumbs are cut), (+ 8 + 3) relates to group spacing

        if numColumns == 0:
            numColumns = 1

        if thumbSize == 0:
            idxCharShortenName = 8
            factorPlusButton = None

        elif thumbSize == 1:
            idxCharShortenName = 16
            factorPlusButton = 1.5

        else:
            idxCharShortenName = (thumbSize + 1) * 15
            factorPlusButton = 2

        bcScenesNames = GetScenesNames()
        bcScenesIsActive = GetScenesIsActive()

        thumbHeightMax = self.ThumbHeightMax(previewH)

        # Note: https://plugincafe.maxon.net/topic/12118/unexpected-margin-padding-on-addcustomgui
        #  and: https://plugincafe.maxon.net/topic/12126/gui-layout-issues
        w = c4d.gui.SizePix(previewW + 4)
        h = c4d.gui.SizePix(thumbHeightMax + 4)
        self.LayoutFlushGroup(ID_GROUP_CONTENT_SCROLLGROUP_SCENES)

        if self.GroupBegin(0, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, numColumns, groupflags=c4d.BFV_CMD_EQUALCOLUMNS, title=''):
            for idDoc, nameDoc in bcScenesNames:
                if hideActive and bcScenesIsActive[idDoc]:
                    continue

                bc = c4d.BaseContainer()
                tooltip = '<b>{0}</b>\n'.format(nameDoc)
                tooltip += '[Shift+Click] More Options...\n'
                tooltip += '[Shift+Ctrl+Click] Close Project\n'
                bc.SetString(c4d.BITMAPBUTTON_TOOLTIP, tooltip)
                bc.SetInt32(c4d.BITMAPBUTTON_IGNORE_BITMAP_WIDTH, False)
                bc.SetInt32(c4d.BITMAPBUTTON_IGNORE_BITMAP_HEIGHT, False)
                bc.SetBool(c4d.BITMAPBUTTON_BUTTON, True)
                bc.SetBool(c4d.BITMAPBUTTON_DRAWPOPUPBUTTON, False)

                if len(nameDoc) > idxCharShortenName:
                    nameDoc = nameDoc[:idxCharShortenName-2] + '...'

                if GetPrefThumbTitle():
                    self.GroupBegin(0, c4d.BFH_CENTER | c4d.BFV_TOP, title=nameDoc)

                    if bcScenesIsActive[idDoc]:
                        self.GroupBorder(c4d.BORDER_GROUP_TOP | c4d.BORDER_WITH_TITLE_BOLD)

                    else:
                        self.GroupBorder(c4d.BORDER_NONE)
                else:
                    self.GroupBegin(0, c4d.BFH_CENTER | c4d.BFV_TOP, title='')

                    self.GroupBorderNoTitle(c4d.BORDER_NONE)

                self.GroupBorderSpace(0, 0, 0, 0)
                self.GroupSpace(0, 0)

                bButton = self.AddCustomGui(idDoc, c4d.CUSTOMGUI_BITMAPBUTTON, '', c4d.BFH_CENTER | c4d.BFV_TOP, w, h, bc)
                if idDoc in self._thumbnails and self._thumbnails[idDoc][1] is not None:
                    bmpThumb = self._thumbnails[idDoc][1]

                    if bcScenesIsActive[idDoc] and fadeThumb > 0:
                        bmpThumb = bmpThumb.GetClone()
                        FadeImageToBackground(bmpThumb, fadeThumb)

                    if bcScenesIsActive[idDoc] and thumbOverlay:
                        bmpThumb = bmpThumb.GetClone()
                        self.ThumbAddActiveMarker(bmpThumb)

                    bButton.SetImage(bmpThumb, True)

                else:
                    bButton.SetImage(self._bmpThumbRenderBusy, False)

                self.GroupEnd()

            if hideActive and len(bcScenesNames) <= 1:
                self.AddStaticText(0, c4d.BFH_LEFT | c4d.BFV_TOP, name='No scene to switch to.')

            if GetPrefNewProject():
                bc = c4d.BaseContainer()
                tooltip = 'New Project'
                bc.SetString(c4d.BITMAPBUTTON_TOOLTIP, tooltip)
                bc.SetInt32(c4d.BITMAPBUTTON_IGNORE_BITMAP_WIDTH, False)
                bc.SetInt32(c4d.BITMAPBUTTON_IGNORE_BITMAP_HEIGHT, False)
                bc.SetBool(c4d.BITMAPBUTTON_BUTTON, True)
                bc.SetBool(c4d.BITMAPBUTTON_DRAWPOPUPBUTTON, False)

                bButton = self.AddCustomGui(ID_BUTTON_NEW_PROJECT, c4d.CUSTOMGUI_BITMAPBUTTON, '', c4d.BFH_CENTER | c4d.BFV_TOP, w, h, bc)

                bmpPlus = GetIconImage(1033505, factor=factorPlusButton) # 1033505 - Add Color
                bButton.SetImage(bmpPlus, True)

        self.GroupEnd()

        self.LayoutChanged(ID_GROUP_CONTENT_SCROLLGROUP_SCENES)

        return True


    def InitThumbnails(self, forceRender=False):
        doc = c4d.documents.GetFirstDocument()
        docActive = c4d.documents.GetActiveDocument()

        thumbSize = GetPrefThumbSize()
        thumbRenderMode = GetPrefThumbModeRender()

        thumbnailsNew = {}
        self._thumbRenderer.Reset()

        while doc is not None:
            p, idDoc, n, i = GetDocumentPathFull(doc)
            bmpPreview = doc.GetDocPreviewBitmap()
            w, h = 0, 0

            if bmpPreview is not None:
                 w, h = bmpPreview.GetSize()

            if thumbRenderMode:
                if not forceRender and idDoc in self._thumbnails and self._thumbnails[idDoc][1] is not None:
                    thumbnailsNew[idDoc] = (doc, self._thumbnails[idDoc][1])

                else:
                    thumbnailsNew[idDoc] = (doc, None)
                    self._thumbRenderer.AddJob(doc, idDoc)

                doc = doc.GetNext()
                continue

            else:
                if doc == docActive:
                    bmpPreview = self.GetFreshPreviewImage()

                if bmpPreview is None or w < 10 or h < 10: # size of bitmap is zero in case of unsaved
                    thumbnailsNew[idDoc] = (doc, None)
                    doc = doc.GetNext()
                    continue

            bmpThumb = self.ScaleThumb(bmpPreview)

            thumbnailsNew[idDoc] = (doc, bmpThumb)

            doc = doc.GetNext()

        self._thumbnails = thumbnailsNew


    def FillComboScenes(self):
        bcScenesNames = GetScenesNames()
        bcScenesIsActive = GetScenesIsActive()

        self.FreeChildren(ID_COMBO_SCENES)

        for idxDoc, (idDoc, nameDoc) in enumerate(bcScenesNames):
            nameDoc = ShortenName(nameDoc)

            sShortcut = ''

            if GetPrefShowShortcuts():
                sShortcut = SwitchShortcutToString(idxDoc)

            self.AddChild(ID_COMBO_SCENES, idDoc, nameDoc + sShortcut)

            if bcScenesIsActive[idDoc]:
                self.SetInt32(ID_COMBO_SCENES, idDoc)

        if GetPrefNewProject():
            self.AddChild(ID_COMBO_SCENES, -1, '') # separator
            self.AddChild(ID_COMBO_SCENES, 0, 'New Project')


    def FillTabsScenes(self):
        bcScenesNames = GetScenesNames()
        bcScenesIsActive = GetScenesIsActive()

        self._quickTabScenes.ClearStrings()

        for idDoc, nameDoc in bcScenesNames:
            nameDoc = ShortenName(nameDoc)

            self._quickTabScenes.AppendString(idDoc, nameDoc, bcScenesIsActive[idDoc])

        if GetPrefNewProject():
            self._quickTabScenes.AppendString(0, '+', False)

        self._quickTabScenes.DoLayoutChange()


    def ScaleThumb(self, bmp):
        if bmp is None:
            return None

        thumbSize = GetPrefThumbSize()

        w, h = bmp.GetSize()
        if w < 10 or h < 10: # size of bitmap is zero in case of unsaved
            return None

        whMax = max(w, h)

        ratio = float(self._thumbResolutions[thumbSize][0]) / float(whMax)

        if ratio == 1.0:
            return bmp.GetClone()

        wThumb = int(ratio * float(w))
        hThumb = int(ratio * float(h))

        bmpThumb = c4d.bitmaps.BaseBitmap()
        bmpThumb.Init(wThumb, hThumb)

        bmp.ScaleIt(bmpThumb, 256, True, False)

        return bmpThumb


    def GetFreshPreviewImage(self):
        if not GetPrefThumbEnabled():
            return None

        doc = c4d.documents.GetActiveDocument()

        previewCommandOrig = doc[c4d.DOCUMENT_PREVIEW_COMMAND]
        if previewCommandOrig is None:
            previewCommandOrig = 0

        doc[c4d.DOCUMENT_PREVIEW_COMMAND] = 2

        c4d.CallButton(doc, c4d.DOCUMENT_PREVIEW_COMMAND)

        doc[c4d.DOCUMENT_PREVIEW_COMMAND] = previewCommandOrig

        c4d.EventAdd()

        bmpPreview = doc.GetDocPreviewBitmap()

        bmpThumb = self.ScaleThumb(bmpPreview)

        return bmpThumb


    _lastDoc = None
    def UpdateSceneChanged(self):
        bcScenesNames = GetScenesNames()
        bcScenesIsActive = GetScenesIsActive()

        self.CreateLayoutAddMenu()

        self.FillComboScenes()
        self.FillTabsScenes()

        if GetPrefThumbUpdates() != 0:
            if GetPrefThumbModeRender():
                if self._lastDoc is not None and self._lastDoc.IsAlive():
                    p, idDoc, n, i = GetDocumentPathFull(self._lastDoc)

                    self._thumbRenderer.AddJob(self._lastDoc, idDoc)

            else:
                doc = c4d.documents.GetActiveDocument()
                bmpThumb = self.GetFreshPreviewImage()

                p, idDoc, n, i = GetDocumentPathFull(doc)

                self._thumbnails[idDoc] = (doc, bmpThumb)

        self.CreateScenesThumbnailGroup()

        # self.SetTimer(250) # TODO Andreas: Removed 20210305

        self._lastDoc = c4d.documents.GetActiveDocument()


    def UpdateComboScenes(self):
        haveCombo = GetPrefComboEnabled()

        self.LayoutFlushGroup(ID_GROUP_DESIGN_COMBO)

        if haveCombo:
            if GetPrefLargeCombo():
                h = 30
            else:
                h = 0

            self.AddComboBox(ID_COMBO_SCENES, c4d.BFH_SCALEFIT | c4d.BFV_CENTER, inith=h)

        self.LayoutChanged(ID_GROUP_MAIN_DIALOG)

        self.HideElement(ID_GROUP_DESIGN_COMBO, not haveCombo)

        self.FillComboScenes()


    def UpdateDesign(self):
        self.CreateLayoutAddMenu()

        #self.HideElement(ID_GROUP_DESIGN_COMBO, not GetPrefComboEnabled())
        self.UpdateComboScenes()
        self.FillTabsScenes()
        self.HideElement(ID_GROUP_DESIGN_TABS, not GetPrefTabsEnabled())
        self.HideElement(ID_GROUP_DESIGN_THUMBS, not GetPrefThumbEnabled())

        if GetPrefDesignButtons():
            self._bitmapButtonEnableCombo.SetToggleState(GetPrefComboEnabled())
            self._bitmapButtonEnableTabs.SetToggleState(GetPrefTabsEnabled())
            self._bitmapButtonEnableThumbs.SetToggleState(GetPrefThumbEnabled())
            self._bitmapButtonThumbSize.SetImage(self._thumbResolutions[GetPrefThumbSize()][3])

        self.LayoutChanged(ID_GROUP_MAIN_DIALOG)

        c4d.EventAdd()


    def UpdateThumbnails(self):
        if not GetPrefThumbEnabled():
            return

        self.InitThumbnails(forceRender=True)

        self.CreateScenesThumbnailGroup()


    def UpdateWithRenderedThumbnails(self):
        thumbResult = self._thumbRenderer.GetImage()

        while thumbResult is not None:
            self._thumbnails[thumbResult[1]] = (thumbResult[0], self.ScaleThumb(thumbResult[2]))

            thumbResult = self._thumbRenderer.GetImage()

        self.CreateScenesThumbnailGroup()


    def RedrawThumbnails(self):
        if not GetPrefThumbEnabled():
            return

        self.CreateScenesThumbnailGroup()


    def CommandSessionStore(self):
        sessions = SessionGetSessions()

        result, nameSession = AskSessionName(len(sessions))
        if not result:
            return False

        SessionStore(nameSession)

        self.CreateLayoutAddMenu()
        # TODO Andreas: Have preferences dialog update and show new session in combo

        c4d.EventAdd()


    def CommandSessionSwitch(self, idxSession):
        nameSession = SessionGetName(idxSession)
        merge = False

        bcInputState = c4d.BaseContainer()
        if self.GetInputState(c4d.BFM_INPUT_KEYBOARD, c4d.BFM_INPUT, bcInputState):
            if bcInputState[c4d.BFM_INPUT_QUALIFIER] & 0x02: # TODO Andreas: c4d.QUALIFIER_CTRL???
                merge = True

        if not merge:
            if GetPrefAskSessionSwitch():
                result = c4d.gui.MessageDialog('Are you sure, you want to close all documents and switch to session {0}'.format(nameSession), type=c4d.GEMB_YESNO)

                if result == c4d.GEMB_R_NO:
                    return

            c4d.CallCommand(12390) # Close All Documents # TODO Andreas: symbol for command?

        SessionOpenStoredSession(idxSession)

        c4d.SpecialEventAdd(PLUGIN_ID_COREMESSAGE_2, p1=42)


    def CommandSessionImport(self):
        pass


    def CommandSessionExport(self):
        pass


    def CommandCloseScene(self, doc, idDoc):
        if doc.GetChanged():
            message = 'Scene {0} contains unsaved changes.\n'.format(doc.GetDocumentName())
            message += '\nDo you want to save before closing the scene?\n\n'
            message += 'Yes: Save scene and close\n'
            message += 'No: Close scene without saving\n'
            message += 'Cancel: Abort, do not close scene'

            result = c4d.gui.MessageDialog(message, type=c4d.GEMB_YESNOCANCEL)
            if result == c4d.GEMB_R_CANCEL:
                return

            elif result == c4d.GEMB_R_YES:
                result = c4d.documents.SaveDocument(doc, doc.GetDocumentName(), saveflags=c4d.SAVEDOCUMENTFLAGS_DIALOGSALLOWED, format=c4d.FORMAT_C4DEXPORT)
                if result == False:
                    return

        isActive = doc == c4d.documents.GetActiveDocument()

        doc.Remove()

        doc = c4d.documents.GetFirstDocument()

        if doc is None:
            doc = c4d.documents.BaseDocument()

            c4d.documents.InsertBaseDocument(doc)

            isActive = True

        if isActive:
            c4d.documents.SetActiveDocument(doc)

        c4d.SpecialEventAdd(PLUGIN_ID_COREMESSAGE_2, p1=42)


    def CommandThumbContextMenu(self, doc, idDoc):
        bcMenu = c4d.BaseContainer()

        pathDoc = doc.GetDocumentPath()
        isSaved = len(pathDoc) > 1

        pathTex = os.path.join(pathDoc, 'tex')
        hasTexFolder = os.path.exists(pathTex)

        if isSaved:
            if c4d.GeGetCurrentOS() == c4d.OPERATINGSYSTEM_WIN:
                bcMenu.InsData(1000, 'Open Project Folder in Explorer...')

            elif c4d.GeGetCurrentOS() == c4d.OPERATINGSYSTEM_OSX:
                bcMenu.InsData(1000, 'Open Project Folder in Finder...')

            else:
                bcMenu.InsData(1000, 'Open Project Folder in Filebrowser...')

        if hasTexFolder:
            if c4d.GeGetCurrentOS() == c4d.OPERATINGSYSTEM_WIN:
                bcMenu.InsData(1001, 'Open Project\'s "tex" Folder in Explorer...')

            elif c4d.GeGetCurrentOS() == c4d.OPERATINGSYSTEM_OSX:
                bcMenu.InsData(1001, 'Open Project\'s "tex" Folder in Finder...')

            else:
                bcMenu.InsData(1001, 'Open Project\'s "tex" Folder in Filebrowser...')

        if isSaved or hasTexFolder:
            bcMenu.InsData(0, '') # Separator

        bcMenu.InsData(1002, 'Close Project [Shift+Ctrl+Click]')

        result = c4d.gui.ShowPopupDialog(cd=None, bc=bcMenu, x=c4d.MOUSEPOS, y=c4d.MOUSEPOS)

        if result == 1000:
            c4d.storage.GeExecuteFile(pathDoc)

        elif result == 1001:
            c4d.storage.GeExecuteFile(pathTex)

        elif result == 1002:
            self.CommandCloseScene(doc, idDoc)

        return


    def CommandSceneSwitch(self, idDoc):
        doc = self._thumbnails[idDoc][0]
        if doc is None or not doc.IsAlive():
            return

        doClose = False
        doContextMenu = False

        bcInputState = c4d.BaseContainer()
        if self.GetInputState(c4d.BFM_INPUT_KEYBOARD, c4d.BFM_INPUT, bcInputState):
            if bcInputState[c4d.BFM_INPUT_QUALIFIER] & 0x03 == 0x03: # TODO Andreas: c4d.QUALIFIER_SHIFT??? c4d.QSHIFT???
                doClose = True
            elif bcInputState[c4d.BFM_INPUT_QUALIFIER] & 0x01: # TODO Andreas: c4d.QUALIFIER_SHIFT??? c4d.QSHIFT???
                doContextMenu = True

        if doContextMenu:
            self.CommandThumbContextMenu(doc, idDoc)

        elif doClose:
            self.CommandCloseScene(doc, idDoc)

        else:
            if doc == c4d.documents.GetActiveDocument():
                self.CommandUpdateCurrentThumb(force=True)

            c4d.documents.SetActiveDocument(doc)

        c4d.EventAdd()

        if GetPrefPopupMode():
            self.Close()


    def CommandNewScene(self):
        docNew = c4d.documents.BaseDocument()

        c4d.documents.InsertBaseDocument(docNew)

        c4d.EventAdd()


    def CommandUpdateCurrentThumb(self, force=True):
        if not force and GetPrefThumbUpdates() == 0:
            return

        doc = c4d.documents.GetActiveDocument()
        p, idDoc, n, i = GetDocumentPathFull(doc)

        if GetPrefThumbModeRender():
            self._thumbRenderer.AddJob(doc, idDoc)

        else:
            bmpThumb = self.GetFreshPreviewImage()
            self._thumbnails[idDoc] = (doc, bmpThumb)
            self.CreateScenesThumbnailGroup()


    def CommandAddThumbCam(self):
        doc = c4d.documents.GetActiveDocument()
        rbd = doc.GetRenderBaseDraw()

        cam = doc.SearchObject(NAME_THUMB_CAM)
        if cam is not None:
            return

        cam = rbd.GetSceneCamera(doc)
        if cam is None:
            cam = rbd.GetEditorCamera()
            cam[c4d.CAMERAOBJECT_STEREO_MODE] = 0

        cam = cam.GetClone()

        cam.SetName(NAME_THUMB_CAM)

        doc.InsertObject(cam)

        c4d.EventAdd()


    def CommandAddThumbRenderSettings(self):
        doc = c4d.documents.GetActiveDocument()

        CreateDefaultRenderData(doc, self._thumbResolutions[-1][0], self._thumbResolutions[-1][1], doInsert=True)

        c4d.EventAdd()


    def CommandEnableThumbs(self):
        SetPrefThumbEnabled(not GetPrefThumbEnabled())

        if GetPrefThumbEnabled():
            self.InitThumbnails()

        self.UpdateDesign()


    def CommandEnableCombo(self):
        SetPrefComboEnabled(not GetPrefComboEnabled())

        self.UpdateDesign()


    def CommandEnableTabs(self):
        SetPrefTabsEnabled(not GetPrefTabsEnabled())

        self.UpdateDesign()


    def CommandThumbSize(self):
        thumbSize = GetPrefThumbSize()
        thumbSize = (thumbSize + 1) % 3

        SetPrefThumbSize(thumbSize)

        self.UpdateThumbnails()
        self.UpdateDesign()


    def CommandAbout(self):
        dlgAbout = DialogAbout()
        dlgAbout.Open(c4d.DLG_TYPE_MODAL)


    def CommandWelcome(self):
        #dlgWelcome = DialogWelcome()
        #dlgWelcome.Open(c4d.DLG_TYPE_ASYNC, pluginid=0, xpos=-2, ypos=-2)
        pass


    def CommandOpenPreferences(self):
        if c4d.GetC4DVersion() >= 19000:
            c4d.PrefsLib_OpenDialog(PLUGIN_ID_PREFS)

        else:
            c4d.CallCommand(PLUGIN_ID_OPTIONS)


    def CreateLayout(self):
        self.SetTitle('{0} {1}'.format(PLUGIN_NAME, PLUGIN_VERSION))

        self.CreateLayoutAddMenuCommandBar()

        if self.GroupBegin(ID_GROUP_MAIN_DIALOG, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, cols=1):
            self.CreateLayoutGroupDesignCombo()
            self.CreateLayoutGroupDesignTabs()
            self.CreateLayoutGroupDesignThumbs()

        self.GroupEnd() # ID_GROUP_MAIN_DIALOG

        self.CreateLayoutAddMenu()

        self.UpdateDesign()

        return True


    def InitValues(self):
        self.InitThumbnails()

        self.UpdateSceneChanged()

        return True


    def Restore(self, pluginid, secref):
        self._thumbRenderer.RestartRenderThread()
        return c4d.gui.GeDialog.Restore(self, pluginid, secref)


    _timerUpdateThumbs = False
    def Timer(self, msg): # delayed scrollgroup adjust
        if not GetPrefPluginEnabled():
            self.SetTimer(0)

        if self._timerUpdateThumbs:
            self.CreateScenesThumbnailGroup()
            self._timerUpdateThumbs = False

        else:
            self.CommandUpdateCurrentThumb(force=False)

        if GetPrefThumbEnabled():
            self.SetTimer(GetPrefThumbUpdateIntervalEffective())

        else:
            self.SetTimer(0)


    def Message(self, msg, result):
        idMsg = msg.GetId()

        if idMsg == c4d.BFM_SIZED:
            self.CreateScenesThumbnailGroup() # Relayout thumbnails on window resize

        elif idMsg == c4d.BFM_LAYOUT_CHANGED:
            self.CreateScenesThumbnailGroup() # TODO: This one fixes an issue on very fast scrollgroup slider movements, unfortunately leads to doubled updates in some cases

        elif idMsg == c4d.BFM_ADJUSTSIZE:
            # BFM_ADJUSTSIZE occurs with high frequency, thus we'll defer relayout via Timer
            self._timerUpdateThumbs = True
            self.SetTimer(50)

        return c4d.gui.GeDialog.Message(self, msg, result)


    def CoreMessage(self, id, msg):
        if id != PLUGIN_ID_COREMESSAGE:
            return c4d.gui.GeDialog.CoreMessage(self, id, msg)

        subIdCoreMessage = GetCoreMessageParam(msg, c4d.BFM_CORE_PAR1)

        if subIdCoreMessage == SUBID_COREMESSAGE_UPDATE:
            self.InitValues()

        elif subIdCoreMessage == SUBID_COREMESSAGE_CHANGE_DESIGN:
            self.UpdateDesign()
            self.UpdateThumbnails()

        elif subIdCoreMessage == SUBID_COREMESSAGE_REDRAW_THUMBS:
            self.RedrawThumbnails()

        elif subIdCoreMessage == SUBID_COREMESSAGE_THUMB_RENDER_FINISHED:
            self.UpdateWithRenderedThumbnails()

        elif subIdCoreMessage == SUBID_COREMESSAGE_CHANGE_SESSIONS:
            self.CreateLayoutAddMenu()

        elif subIdCoreMessage == SUBID_COREMESSAGE_CHANGE_THUMB_INTERVAL:
            self.SetTimer(GetPrefThumbUpdateIntervalEffective())

        elif subIdCoreMessage == SUBID_COREMESSAGE_CHANGE_MENU_BUTTONS:
            if self.IsOpen():
                self.Close()
                self.Open(c4d.DLG_TYPE_ASYNC, PLUGIN_ID, xpos=-1, ypos=-1, defaultw=0, defaulth=0)

        elif subIdCoreMessage == SUBID_COREMESSAGE_CLOSE_SSP:
            if self.IsOpen():
                self.Close()

        return c4d.gui.GeDialog.CoreMessage(self, id, msg)


    def AskClose(self):
        if self._thumbRenderer is not None:
            self._thumbRenderer.StopRenderThread()

        return False


    def Command(self, id, msg):
        if id in self._thumbnails: # menu and thumbnail scene clicks
            self.CommandSceneSwitch(id)

        elif id == ID_COMBO_SCENES:
            idDoc = self.GetInt32(id)

            if idDoc == 0:
                self.CommandNewScene()

                return True

            self.CommandSceneSwitch(idDoc)

        elif id == ID_QUICKTAB_SCENES:
            bcScenesNames = GetScenesNames()

            if self._quickTabScenes.IsSelected(0):
                self.CommandNewScene()
                return True

            for idDoc, nameDoc in bcScenesNames:
                if not self._quickTabScenes.IsSelected(idDoc):
                    continue

                self.CommandSceneSwitch(idDoc)

                break

        elif id == ID_BUTTON_NEW_PROJECT:
            self.CommandNewScene()

        elif id >= ID_BASE_SESSION_MENU and id < ID_BASE_SESSION_MENU + 1000: # stored session indeces (Note: we receive id 92 from close button of docked dialog!!!)
            idxSession = id - ID_BASE_SESSION_MENU
            self.CommandSessionSwitch(idxSession)

        elif id == ID_SESSION_STORE:
            self.CommandSessionStore()

        elif id == ID_SESSION_MANAGE:
            CommandManageSessions(fromPrefs=False)

        elif id == ID_SESSION_IMPORT:
            pass

        elif id == ID_SESSION_EXPORT:
            pass

        elif id == ID_CALL_PREFERENCES:
            self.CommandOpenPreferences()

        elif id == ID_CALL_ABOUT:
            self.CommandAbout()

        elif id == ID_CALL_WELCOME:
            self.CommandWelcome()

        elif id == ID_UPDATE_CURRENT_THUMB:
            self.CommandUpdateCurrentThumb(force=True)

        elif id == ID_ADD_THUMB_CAM:
            self.CommandAddThumbCam()

        elif id == ID_ADD_THUMB_RENDER_SETTINGS:
            self.CommandAddThumbRenderSettings()

        elif id == ID_CALL_SAVE_LAYOUT:
            result = c4d.gui.MessageDialog('The current layout will be saved as Startup Layout.\nAre you sure?', type=c4d.GEMB_YESNO)
            if result == c4d.GEMB_R_YES:
                c4d.CallCommand(12672) # Save as Startup Layout # TODO Andreas: symbol?

        elif id == ID_BITMAPBUTTON_ENABLE_THUMBS:
            self.CommandEnableThumbs()

        elif id == ID_BITMAPBUTTON_ENABLE_COMBO:
            self.CommandEnableCombo()

        elif id == ID_BITMAPBUTTON_ENABLE_TABS:
            self.CommandEnableTabs()

        elif id == ID_BITMAPBUTTON_THUMB_SIZE:
            self.CommandThumbSize()

        elif id == ID_BUTTON_PREV_DOC:
            c4d.CallCommand(13858)

        elif id == ID_BUTTON_NEXT_DOC:
            c4d.CallCommand(13857)

        elif id == 92: # We receive id 92 from close button of docked dialog!!!
            pass

        else:
            print("{0} ERROR: UNKNOWN COMMAND. Click! Did you hear this? A developer's soul has vanished.".format(PLUGIN_NAME))

        return True
