# -*- coding: utf-8 -*-
# libavg - Media Playback Engine.
# Copyright (C) 2003-2011 Ulrich von Zadow
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Current versions can be found at www.libavg.de
#

from libavg import avg, player
from testcase import *
import gc

class OffscreenTestCase(AVGTestCase):
    def __init__(self, testFuncName):
        AVGTestCase.__init__(self, testFuncName)
    
    def testCanvasBasics(self):
        def createCanvas(isFirst, canvasName, x):
            canvas = self.__createOffscreenCanvas(canvasName, False)
            canvas.getElementByID("test1").x = x
            node = avg.ImageNode(parent=root, id="imagenode")
            node.href="canvas:"+canvasName
            if isFirst:
                self.assertEqual(canvas.getNumDependentCanvases(), 0)
                self.canvas1 = canvas
            else:
                self.assertEqual(canvas.getNumDependentCanvases(), 1)
                self.canvas2 = canvas

        def unlink():
            self.node = player.getElementByID("imagenode")
            self.node.unlink()
            self.assertEqual(self.canvas1.getNumDependentCanvases(), 0)
            gc.collect()

        def relink():
            root.appendChild(self.node)
            self.node = None
            self.assertEqual(self.canvas1.getNumDependentCanvases(), 1)
            
        def changeHRef(href):
            player.getElementByID("imagenode").href = href

        def setBitmap():
            bitmap = avg.Bitmap("media/rgb24-65x65.png")
            player.getElementByID("imagenode").setBitmap(bitmap)

        def deleteCanvases():
            changeHRef("")
            firstNode.href = ""
            player.deleteCanvas("testcanvas1")
#            self.assertException(lambda: changeHRef("canvas:testcanvas1"))
            changeHRef("canvas:testcanvas2")
#            self.assertException(lambda: player.deleteCanvas("testcanvas2"))
            changeHRef("")
            player.deleteCanvas("testcanvas2")
#            self.assertException(lambda: player.deleteCanvas("foo"))

        root = self.loadEmptyScene()
        root.mediadir = "media"
        createCanvas(True, "testcanvas1", 0)
        firstNode = player.getElementByID("imagenode")
        self.start(False,
                (lambda: self.compareImage("testOffscreen1"),
                 unlink,
                 lambda: self.compareImage("testOffscreen2"), 
                 relink,
                 lambda: self.compareImage("testOffscreen1"),
                 unlink,
                 lambda: createCanvas(False, "testcanvas2", 80),
                 lambda: self.compareImage("testOffscreen3"),
                 lambda: changeHRef("canvas:testcanvas1"),
                 lambda: self.assertEqual(self.canvas1.getNumDependentCanvases(), 1),
                 lambda: self.assertEqual(self.canvas2.getNumDependentCanvases(), 0),
                 lambda: self.compareImage("testOffscreen1"),
                 lambda: changeHRef("rgb24-65x65.png"),
                 lambda: self.assertEqual(self.canvas1.getNumDependentCanvases(), 0),
                 lambda: self.compareImage("testOffscreen4"),
                 lambda: changeHRef("canvas:testcanvas1"),
                 lambda: self.assertEqual(self.canvas1.getNumDependentCanvases(), 1),
                 lambda: self.compareImage("testOffscreen1"),
                 setBitmap,
                 lambda: self.compareImage("testOffscreen4"),
                 deleteCanvases,
                 lambda: self.compareImage("testOffscreen5"),
                ))

    def testCanvasLoadAfterPlay(self):
        def createOffscreenCanvas():
            self.__createOffscreenCanvas("offscreencanvas", False)
            self.node = avg.ImageNode(parent=root, href="canvas:offscreencanvas")
    
        root = self.loadEmptyScene()
        self.start(False,
                (createOffscreenCanvas,
                 lambda: self.compareImage("testOffscreen1"),
                ))

    def testCanvasResize(self):
        def setSize():
            self.node.size = (80, 60)

        mainCanvas, offscreenCanvas = self.__setupCanvas(False)
        self.start(False,
                (setSize,
                 lambda: self.compareImage("testCanvasResize")
                ))

    def testCanvasErrors(self):
        self.loadEmptyScene()
        # Missing size
        self.assertException(
                lambda: player.createCanvas(id="foo"))
        # Duplicate canvas id
        player.createCanvas(id="foo", size=(160, 120))
        self.assertException(
                lambda: player.createCanvas(id="foo", size=(160, 120)))

    def testCanvasAPI(self):
        def checkMainScreenshot():
            bmp1 = player.screenshot()
            bmp2 = mainCanvas.screenshot()
            self.assert_(self.areSimilarBmps(bmp1, bmp2, 0.01, 0.01))

        def checkCanvasScreenshot():
            bmp = offscreenCanvas.screenshot()
            self.compareBitmapToFile(bmp, "testOffscreenScreenshot")

        def createCompressed():
            avg.ImageNode(href="canvas:offscreencanvas", compression="B5G6R5", 
                    parent=root)

        root = self.loadEmptyScene()
        mainCanvas = player.getMainCanvas()
        self.assertEqual(mainCanvas.getRootNode(), root)
        offscreenCanvas = self.__createOffscreenCanvas("offscreencanvas", False)
        self.assertEqual(offscreenCanvas, player.getCanvas("offscreencanvas"))
        self.assertEqual(offscreenCanvas.getElementByID("test1").href, "rgb24-65x65.png")
        self.assertEqual(offscreenCanvas.getElementByID("missingnode"), None)
        self.assertException(player.screenshot)
        self.assertException(createCompressed)
        self.start(False,
                (checkMainScreenshot,
                 checkCanvasScreenshot))

    def testCanvasEvents(self):
        def onOffscreenImageDown(event):
            self.__offscreenImageDownCalled = True

        def onMainDown(event):
            self.__mainDownCalled = True

        def reset():
            self.__offscreenImageDownCalled = False
            self.__mainDownCalled = False

        def setPos():
            self.node.pos = (80, 60)
            self.node.size = (80, 60)

        mainCanvas, offscreenCanvas = self.__setupCanvas(True)
        offscreenImage = offscreenCanvas.getElementByID("test1")
        offscreenImage.subscribe(avg.Node.CURSOR_DOWN, onOffscreenImageDown)
        player.getRootNode().subscribe(avg.Node.CURSOR_DOWN, onMainDown)
        self.__offscreenImageDownCalled = False
        self.__mainDownCalled = False
        self.start(False,
                (lambda: self.fakeClick(10, 10),
                 lambda: self.assert_(self.__offscreenImageDownCalled),
                 reset,
                 lambda: self.fakeClick(80, 10),
                 lambda: self.assert_(not(self.__offscreenImageDownCalled)),
                 reset,
                 setPos,
                 lambda: self.fakeClick(70, 65),
                 lambda: self.assert_(not(self.__offscreenImageDownCalled)),
                 lambda: self.fakeClick(120, 65),
                 lambda: self.assert_(not(self.__offscreenImageDownCalled)),
                 reset,
                 lambda: self.fakeClick(110, 65),
                 lambda: self.assert_(self.__offscreenImageDownCalled and 
                        self.__mainDownCalled),
                 reset,
                 lambda: self.fakeClick(1, 1),
                 lambda: self.assert_(not(self.__offscreenImageDownCalled) and 
                        self.__mainDownCalled),
                ))

    def testCanvasEventCapture(self):
        def onOffscreenImageDown(event):
            self.__offscreenImageDownCalled = True

        mainCanvas, offscreenCanvas = self.__setupCanvas(True)
        offscreenImage = offscreenCanvas.getElementByID("test1")
        offscreenImage.subscribe(avg.Node.CURSOR_DOWN, onOffscreenImageDown);
        self.__offscreenImageDownCalled = False
        offscreenImage.setEventCapture()
        self.start(False,
                (lambda: self._sendMouseEvent(avg.Event.CURSOR_DOWN, 80, 10),
                 lambda: self.assert_(self.__offscreenImageDownCalled),
                ))
                
    def testCanvasRender(self):
        def createCanvas():
            canvas = player.createCanvas(id="testcanvas", size=(160,120),
                    mediadir="media", autorender=False)
            avg.ImageNode(id="test", href="rgb24-65x65.png", parent=canvas.getRootNode())
            return canvas

        def testEarlyScreenshotException():
            self.assertException(self.__offscreenCanvas.screenshot)

        def renderCanvas():
            self.__offscreenCanvas.render()
            bmp = self.__offscreenCanvas.screenshot()
            self.compareBitmapToFile(bmp, "testOffscreenScreenshot")

        def deleteCanvas():
            player.deleteCanvas("testcanvas")
            self.__offscreenCanvas = None

        def recreateCanvas():
            self.__offscreenCanvas = createCanvas()

        self.loadEmptyScene()
        self.__offscreenCanvas = createCanvas()
        self.assertException(renderCanvas)
        self.start(False,
                (testEarlyScreenshotException,
                 renderCanvas,
                 deleteCanvas,
                 recreateCanvas,
                 renderCanvas
                ))

    def testCanvasAutoRender(self):
        def createCanvas():
            canvas = self.__createOffscreenCanvas("testcanvas", False)
            avg.ImageNode(href="canvas:testcanvas", parent=root)
            return canvas

        def disableAutoRender():
            self.__offscreenCanvas.autorender = False

        def enableAutoRender():
            self.__offscreenCanvas.autorender = True

        def changeContent():
            self.__offscreenCanvas.getElementByID("test1").x = 42

        root = self.loadEmptyScene()
        self.__offscreenCanvas = createCanvas()
        self.start(False,
                (lambda: self.assert_(self.__offscreenCanvas.autorender),
                 lambda: self.compareImage("testOffscreenAutoRender1"),
                 disableAutoRender,
                 lambda: self.assert_(not(self.__offscreenCanvas.autorender)),
                 changeContent,
                 lambda: self.compareImage("testOffscreenAutoRender1"),
                 enableAutoRender,
                 lambda: self.assert_(self.__offscreenCanvas.autorender),
                 lambda: self.compareImage("testOffscreenAutoRender2")
                ))

    def testCanvasCrop(self):
        root = self.loadEmptyScene()
        canvas = player.createCanvas(id="testcanvas", size=(160,120), 
                mediadir="media")
        div = avg.DivNode(pos=(40,30), size=(80,60), crop=True, 
                parent=canvas.getRootNode())
        avg.ImageNode(id="test1", pos=(-32, -32), href="rgb24-65x65.png", parent=div)
        avg.ImageNode(parent=root, href="canvas:testcanvas")
        self.start(False, (lambda: self.compareImage("testCanvasCrop"),))

    def testCanvasAlpha(self):
        root = self.loadEmptyScene()
        canvas = player.createCanvas(id="testcanvas", size=(80,120), mediadir="media")
        avg.ImageNode(id="test1", href="rgb24alpha-64x64.png", 
                parent=canvas.getRootNode())
        avg.RectNode(parent=root, fillcolor="FFFFFF",
                pos=(0.5, 0.5), size=(160, 48), fillopacity=1)
        avg.ImageNode(parent=root, href="canvas:testcanvas")
        avg.ImageNode(parent=root, x=64, href="rgb24alpha-64x64.png")
        self.start(False, (lambda: self.compareImage("testCanvasAlpha"),))
    
    def testCanvasBlendModes(self):
        def createBaseCanvas():
            canvas = player.createCanvas(id="testcanvas", size=(64,64), 
                    mediadir="media")
            avg.ImageNode(href="rgb24alpha-64x64.png", parent=canvas.getRootNode())
            return canvas
       
        root = self.loadEmptyScene()
        createBaseCanvas()
        avg.RectNode(parent=root, pos=(48,0), size=(32, 120), strokewidth=2, 
                fillopacity=1, fillcolor="808080")
        avg.ImageNode(parent=root, href="canvas:testcanvas")
        avg.ImageNode(parent=root, pos=(0,64), href="canvas:testcanvas", 
                opacity=0.6)
        avg.ImageNode(parent=root, pos=(64,0), href="canvas:testcanvas", 
                blendmode="add")
        avg.ImageNode(parent=root, pos=(64,64), href="canvas:testcanvas", 
                opacity=0.6, blendmode="add")
        self.start(False, (lambda: self.compareImage("testCanvasBlendModes"),))

    def testCanvasMultisampling(self):
        def testIllegalSamples(numSamples):
            self.canvas = player.createCanvas(id="brokencanvas", size=(160,120), 
                            multisamplesamples=numSamples)

        def screenshot():
            bmp = self.canvas.screenshot()
            self.compareBitmapToFile(bmp, "testOffscreenMultisampleScreenshot")

        def createCanvas():
            if not(avg.OffscreenCanvas.isMultisampleSupported()):
                self.skip("Offscreen multisampling not supported")
                player.stop()
                return
            try:
                self.canvas = player.createCanvas(id="testcanvas", size=(160,120),
                        mediadir="media", multisamplesamples=2)
                avg.ImageNode(id="test1", href="rgb24-65x65.png", angle=0.1,
                        parent=self.canvas.getRootNode())
            except RuntimeError:
                self.skip("Offscreen multisampling init failed")
                player.stop()
                return
            self.assertEqual(self.canvas.multisamplesamples, 2)
            avg.ImageNode(parent=root, href="canvas:testcanvas")
            

        root = self.loadEmptyScene()
        self.start(False,
                (createCanvas,
                 lambda: self.compareImage("testCanvasMultisample"),
                 screenshot,
                 lambda: self.assertException(lambda: testIllegalSamples(42)),
                 lambda: self.assertException(lambda: testIllegalSamples(0)),
                ))
        self.canvas = None
       
    def testCanvasMipmap(self):
        root = self.loadEmptyScene()

        canvas = player.createCanvas(id="testcanvas", size=(80,120), mediadir="media",
                mipmap=True)
        avg.ImageNode(id="test1", href="rgb24alpha-64x64.png", 
                parent=canvas.getRootNode())
        avg.ImageNode(parent=root, size=(40, 30), href="canvas:testcanvas")
        try:
            self.start(False, (lambda: self.compareImage("testCanvasMipmap"),))
        except RuntimeError:
            self.skip("Offscreen mipmap init failed.")
            return

    def testCanvasDependencies(self):
        def makeCircularRef():
            self.offscreen1.getElementByID("test1").href = "canvas:offscreencanvas2"
            
        def makeSelfRef1():
            avg.ImageNode(href="canvas:offscreencanvas1", 
                    parent=self.offscreen1.getRootNode())

        def makeSelfRef2():
            self.offscreen1.getElementByID("test1").href = "canvas:offscreencanvas1"

        def createTwoCanvases():
            self.offscreen1 = self.__createOffscreenCanvas("offscreencanvas1", False)
            self.offscreen2 = self.__createOffscreenCanvas("offscreencanvas2", False)
            self.node = avg.ImageNode(parent=root, 
                    href="canvas:offscreencanvas1")
            node = self.offscreen1.getElementByID("test1")
            node.href = "canvas:offscreencanvas2"
            node.size = (80, 60)
            
        def exchangeCanvases():
            self.offscreen1.getElementByID("test1").href = "rgb24-65x65.png"
            self.offscreen2.getElementByID("test1").href = "canvas:offscreencanvas1"
            self.node.href = "canvas:offscreencanvas2"
            
        def loadCanvasDepString():
            player.createCanvas(id="canvas1", size=(160, 120))
            canvas2 = player.createCanvas(id="canvas2", size=(160, 120))
            avg.ImageNode(href="canvas:canvas1", parent=canvas2.getRootNode())
            player.deleteCanvas('canvas2')
            player.deleteCanvas('canvas1')

        root = self.loadEmptyScene()
        createTwoCanvases()
        self.offscreen1.getElementByID("test1").href = ""
        self.offscreen1 = None
        self.offscreen2 = None
        self.node.href = ""
        self.node = None
        player.deleteCanvas("offscreencanvas1")
        player.deleteCanvas("offscreencanvas2")
        self.start(False,
                (createTwoCanvases,
                 lambda: self.compareImage("testCanvasDependencies1"),
                 exchangeCanvases,
                 lambda: self.compareImage("testCanvasDependencies2"),
                 lambda: self.assertException(makeCircularRef),
                 lambda: self.assertException(makeSelfRef1),
                 lambda: self.assertException(makeSelfRef2),
                 loadCanvasDepString,
                ))

    def __setupCanvas(self, handleEvents):
        root = self.loadEmptyScene()
        mainCanvas = player.getMainCanvas()
        offscreenCanvas = self.__createOffscreenCanvas("offscreencanvas", handleEvents)
        self.node = avg.ImageNode(parent=root, href="canvas:offscreencanvas")
        return (mainCanvas, offscreenCanvas)

    def __createOffscreenCanvas(self, canvasName, handleEvents):
        canvas = player.createCanvas(id=canvasName, size=(160,120), 
                handleevents=handleEvents)
        canvas.getRootNode().mediadir = "media"
        avg.ImageNode(id="test1", href="rgb24-65x65.png", parent=canvas.getRootNode())
        return canvas

def isOffscreenSupported():
    
    def testOffscreenSupported():
        global offscreenSupported
        offscreenSupported = avg.OffscreenCanvas.isSupported()
        player.stop()

    global offscreenSupported
    sceneString = """<avg id="avg" width="160" height="120"/>"""
    player.loadString(sceneString)
    player.setTimeout(0, testOffscreenSupported)
    player.play() 
    return offscreenSupported

def offscreenTestSuite(tests):
    if isOffscreenSupported():
        availableTests = (
                "testCanvasBasics",
                "testCanvasLoadAfterPlay",
                "testCanvasResize",
                "testCanvasErrors",
                "testCanvasAPI",
                "testCanvasEvents",
                "testCanvasEventCapture",
                "testCanvasRender",
                "testCanvasAutoRender",
                "testCanvasCrop",
                "testCanvasAlpha",
                "testCanvasBlendModes",
                "testCanvasMultisampling",
                "testCanvasMipmap",
                "testCanvasDependencies",
                )
        return createAVGTestSuite(availableTests, OffscreenTestCase, tests)
    else:
        sys.stderr.write("Skipping offscreen tests - no canvas support with this graphics configuration.\n")
        return unittest.TestSuite()
