from threading import Condition
import c4d
from ssp_ids import *
from ssp_utils_pref import *
from ssp_utils import *

####################################################################################################
# Class DeferredThumbRendering #####################################################################
####################################################################################################
# EXPECTS A CLONED, NON-INSERTED DOCUMENT, DOCUMENT WILL BE MODIFIED
def RenderThumb(doc, xresDefault, yresDefault, useUserRenderData=False, thd=None):
    res = THUMB_RENDER_RESOLUTION

    if useUserRenderData:
        rdPreview = doc.GetActiveRenderData().GetClone()

    else:
        rdPreview = GetSSPRenderData(doc, xresDefault, yresDefault)

    rbd = doc.GetRenderBaseDraw()

    camPreview = doc.SearchObject(NAME_THUMB_CAM)
    if camPreview is None:
        camPreview = rbd.GetSceneCamera(doc)
    if camPreview is None:
        camPreview = rbd.GetEditorCamera()

    rbd.SetSceneCamera(camPreview)

    thumbFilter = GetPrefThumbFilter()
    if thumbFilter and c4d.GetC4DVersion() < 22000:
        doc.SetActiveObject(None)

        bcViewportFilterOrig = StoreViewportFilter(rbd)

        rbd[c4d.BASEDRAW_DISPLAYFILTER_MULTIAXIS] = False
        rbd[c4d.BASEDRAW_DISPLAYFILTER_OBJECTHANDLES] = False
        rbd[c4d.BASEDRAW_DISPLAYFILTER_OBJECTHIGHLIGHTING] = False
        rbd[c4d.BASEDRAW_DISPLAYFILTER_WORLDAXIS] = False
        rbd[c4d.BASEDRAW_DISPLAYFILTER_HUD] = False
        rbd[c4d.BASEDRAW_DISPLAYFILTER_BASEGRID] = False
        rbd[c4d.BASEDRAW_DISPLAYFILTER_GRID] = False
        rbd[c4d.BASEDRAW_DISPLAYFILTER_HANDLES] = False

    if GetPrefThumbAspectRatio() == 1:
        frame = c4d.documents.GetActiveDocument().GetActiveBaseDraw().GetFrame()

    elif GetPrefThumbAspectRatio() == 2:
        frame = c4d.documents.GetActiveDocument().GetActiveBaseDraw().GetSafeFrame()

    else:
        frame = {}
        frame['cr'] = 0
        frame['cl'] = 0
        frame['cb'] = 0
        frame['ct'] = 0

    w = frame['cr'] - frame['cl']
    h = frame['cb'] - frame['ct']

    if (GetPrefThumbAspectRatio() == 1 or GetPrefThumbAspectRatio() == 2) and w > 0 and h > 0:
        if w >= h:
            rdPreview[c4d.RDATA_XRES] = res[0]
            rdPreview[c4d.RDATA_YRES] = int(res[1] * (float(h) / float(w)))

        else:
            rdPreview[c4d.RDATA_XRES] = int(res[0] * (float(w) / float(h)))
            rdPreview[c4d.RDATA_YRES] = res[1]

    else:
        sizeMax = max(rdPreview[c4d.RDATA_XRES], rdPreview[c4d.RDATA_YRES])
        size = min(rdPreview[c4d.RDATA_XRES], rdPreview[c4d.RDATA_YRES])
        camPreview[c4d.CAMERA_FOCUS] = camPreview[c4d.CAMERA_FOCUS] * (sizeMax / size)
        rdPreview[c4d.RDATA_XRES] = res[0]
        rdPreview[c4d.RDATA_YRES] = res[1]

    bmPreview = c4d.bitmaps.BaseBitmap()
    bmPreview.Init(int(rdPreview[c4d.RDATA_XRES]), int(rdPreview[c4d.RDATA_YRES]))
    doc.InsertRenderDataLast(rdPreview)
    doc.SetActiveRenderData(rdPreview)

    # RENDERFLAGS_NODOCUMENTCLONE: We are working with an already cloned document!
    renderFlags = c4d.RENDERFLAGS_EXTERNAL | c4d.RENDERFLAGS_NODOCUMENTCLONE | c4d.RENDERFLAGS_PREVIEWRENDER
    result = c4d.documents.RenderDocument(doc, rdPreview.GetData(), bmPreview, renderFlags, th=thd)

    if thumbFilter and c4d.GetC4DVersion() < 22000:
        RestoreViewportFilter(rbd, bcViewportFilterOrig)
        c4d.EventAdd()

    if result != c4d.RENDERRESULT_OK:
        print('{0} ERROR: Thumb rendering: {1}'.format(PLUGIN_NAME, result))
        return None

    return bmPreview


class ThumbRenderThread(c4d.threading.C4DThread):
    _parent = None
    _run = True

    def Main(self):
        xresDefault, yresDefault = self._parent.GetDefaultResolution()

        while self._run:
            job = self._parent.WaitForJob()

            if self.TestBreak() or not self._run:
                break

            if job is None:
                continue

            docClone = job[0]
            doc = job[1]
            idDoc = job[2]

            if doc is None or not doc.IsAlive() or (docClone is not None and not docClone.IsAlive()):
                continue

            bmThumb = RenderThumb(docClone, xresDefault, yresDefault, thd=self.Get())

            self._parent.AddImage(doc, idDoc, bmThumb)


class DeferredThumbRendering():
    _defaultWidth = 1
    _defaultHeight = 1

    _mutexQueues = None

    _queueJobs = []
    _queueImages = []

    _thdRender = None


    def __init__(self, defaultWidth, defaultHeight):
        self._defaultWidth = defaultWidth
        self._defaultHeight = defaultHeight

        self._mutexQueues = Condition()

        self._queueJobs = []
        self._queueImages = []

        self._thdRender = ThumbRenderThread()

        self._thdRender._parent = self
        self._thdRender._run = True

        self._thdRender.Start()


    def AddJob(self, doc, idDoc):
        if not GetPrefThumbEnabled():
            return

        docClone = doc.GetClone()

        self._mutexQueues.acquire()

        self._queueJobs.append((docClone, doc, idDoc))
        self._mutexQueues.notify()

        self._mutexQueues.release()


    def WaitForJob(self):
        job = None

        self._mutexQueues.acquire()

        if len(self._queueJobs) <= 0:
            self._mutexQueues.wait()

        if len(self._queueJobs) > 0:
            job = self._queueJobs.pop(0)

        self._mutexQueues.release()

        return job


    def AddImage(self, doc, idDoc, bm):
        self._mutexQueues.acquire()

        self._queueImages.append((doc, idDoc, bm))

        self._mutexQueues.release()

        c4d.SpecialEventAdd(PLUGIN_ID_COREMESSAGE, p1=SUBID_COREMESSAGE_THUMB_RENDER_FINISHED)


    def GetImage(self):
        resultImage = None

        self._mutexQueues.acquire()

        if len(self._queueImages) > 0:
            resultImage = self._queueImages.pop(0)

        self._mutexQueues.release()

        return resultImage


    def Reset(self):
        self._mutexQueues.acquire()

        self._queueJobs = []
        self._queueImages = []

        self._mutexQueues.release()


    def StopRenderThread(self):
        self._thdRender._run = False

        self._thdRender.End(False)

        self._mutexQueues.acquire()

        self._mutexQueues.notify()

        self._mutexQueues.release()

        self._thdRender.End(True)


    def RestartRenderThread(self):
        if not self._thdRender._run:
            self._thdRender._run = True

            self._thdRender.Start()

            c4d.SpecialEventAdd(PLUGIN_ID_COREMESSAGE, p1=SUBID_COREMESSAGE_UPDATE)


    def GetDefaultResolution(self):
        return self._defaultWidth, self._defaultHeight
