#!/usr/bin/env python
# -*- coding: utf-8 -*-
# libavg - Media Playback Engine.
# Copyright (C) 2003-2014 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, utils, player
from testcase import *


class FXTestCase(AVGTestCase):
    def __init__(self, testFuncName):
        AVGTestCase.__init__(self, testFuncName)

    def testImageNullFX(self):
        def activateFX():
            for node in self.nodes[0]:
                node.setEffect(avg.NullFXNode())

        def newNode():
            self.newNode = avg.ImageNode(parent=root, href="rgb24-32x32.png", pos=(64,0))
            self.newNode.setEffect(avg.NullFXNode())

        def newFX():
            self.newNode.setEffect(avg.NullFXNode())

        def addBgNode():
            node = avg.RectNode(pos=(0,0), size=(64,96), fillopacity=1, opacity=0,
                    fillcolor="FFFFFF")
            root.insertChild(node, 0)

        def emptyImageFX():
            node = avg.ImageNode(parent=root, href="", pos=(64,0))
            node.setEffect(avg.NullFXNode())

        # Initial setup is 3x2 images: 
        # rows: no alpha, alpha, alpha & opacity 0.6
        # cols: no FX, FX
        # The two cols should look the same.
        root = self.loadEmptyScene()
        self.nodes = []
        for fx in (False, True):
            curNodes = []
            self.nodes.append(curNodes)
            def configureNode(node, fx):
                curNodes.append(node)
                if fx:
                    node.x = 32
                    node.setEffect(avg.NullFXNode())

            node = avg.ImageNode(parent=root, href="rgb24-32x32.png", pos=(0,0))
            configureNode(node, fx)
            node = avg.ImageNode(parent=root, href="rgb24alpha-32x32.png", pos=(0,32))
            configureNode(node, fx)
            node = avg.ImageNode(parent=root, href="rgb24alpha-32x32.png", pos=(0,64),
                opacity=0.6)
            configureNode(node, fx)

        self.start(False,
                (lambda: self.compareImage("testImageNullFX1"),
                 addBgNode,
                 lambda: self.compareImage("testImageNullFX2"),
                 activateFX,
                 lambda: self.compareImage("testImageNullFX2"),
                 newNode,
                 lambda: self.compareImage("testImageNullFX3"),
                 newFX,
                 lambda: self.compareImage("testImageNullFX3"),
                 emptyImageFX,
                 lambda: utils.initFXCache(10),
                ))

    def testVideoNullFX(self):
        root = self.loadEmptyScene()
        player.setFakeFPS(25)
        node = avg.VideoNode(parent=root, href="mjpeg-48x48.avi",
                threaded=False)
        node.setEffect(avg.NullFXNode())
        node.play()
        self.start(False, (lambda: self.compareImage("testVideoNullFX"),))

    def testWordsNullFX(self):
        root = self.loadEmptyScene()
        node = avg.WordsNode(parent=root, text="testtext", font="Bitstream Vera Sans")
        node.setEffect(avg.NullFXNode())
        node = avg.WordsNode(parent=root, text="testtext", pos=(0,20),
                font="Bitstream Vera Sans")
        self.start(True,
                (lambda: self.compareImage("testWordsNullFX"),
                ))

    def testCanvasNullFX(self):
        def setOuterOpacity():
            node.opacity=0.6

        def setInnerOpacity():
            innerNode = canvas.getElementByID("test")
            innerNode.opacity = 0.0

        root = self.loadEmptyScene()
        canvas = self.__createOffscreenCanvas()
        node = avg.ImageNode(parent=root, href="canvas:offscreen")
        node.setEffect(avg.NullFXNode())
        self.start(False,
                (lambda: self.compareImage("testCanvasNullFX1"),
                 setOuterOpacity,
                 lambda: self.compareImage("testCanvasNullFX2"),
                 setInnerOpacity,
                 lambda: self.compareImage("testCanvasNullFX3"),
                ))

    def testNodeInCanvasNullFX(self):
        root = self.loadEmptyScene()
        canvas = self.__createOffscreenCanvas()
        avg.ImageNode(parent=root, href="canvas:offscreen")
        node = canvas.getElementByID("test")
        node.setEffect(avg.NullFXNode())
        rect = avg.RectNode(size=(100,100), strokewidth=0, fillcolor="FF0000",
                fillopacity=1)
        canvas.getRootNode().insertChild(rect, 0)
        
        self.start(False,
                (lambda: self.compareImage("testNodeInCanvasNullFX1"),
                ))

    def testRenderPipeline(self):
        sys.stderr.write("\n")
        for useSrcCanvas in (False, True):
            for useDestCanvas in (False, True):
                for useFX in (False, True):
                    for useColorConv in (False, True):
                        sys.stderr.write("  "+str(useSrcCanvas)+" "+str(useDestCanvas)+
                                " "+str(useFX)+" "+str(useColorConv)+"\n")
                        root = self.loadEmptyScene()
                        if useSrcCanvas:
                            srcCanvas = player.createCanvas(id="src", size=(160,120),
                                    mediadir="media")
                            avg.ImageNode(href="rgb24alpha-64x64.png", 
                                    parent=srcCanvas.getRootNode())
                            srcImg = avg.ImageNode(href="canvas:src")
                        else:
                            srcImg = avg.ImageNode(href="rgb24alpha-64x64.png")
                        if useFX:
                            srcImg.setEffect(avg.NullFXNode())
                        if useColorConv:
                            srcImg.contrast = (1.01, 1.0, 1.0)
                        if useDestCanvas:
                            destCanvas = player.createCanvas(id="dest",
                                    size=(160,120), mediadir="media")
                            destCanvas.getRootNode().appendChild(srcImg)
                            avg.ImageNode(href="canvas:dest", parent=root)
                        else:
                            root.appendChild(srcImg)
                        self.start(False,
                                (lambda: self.compareImage("testRenderPipeline"),
                                ))

    def testBlurFX(self):
       
        def setRadius(radius):
            self.effect.radius = radius
        
        def removeFX():
            self.node.setEffect(None)

        def reAddFX():
            self.node.setEffect(self.effect)

        def addNewFX():
            effect = avg.BlurFXNode(8)
            self.node.setEffect(effect)

        root = self.loadEmptyScene()
        self.node = avg.ImageNode(parent=root, pos=(10,10), href="rgb24-64x64.png")
        self.effect = avg.BlurFXNode()
        self.start(False,
                (self.skipIfMinimalShader,
                 lambda: self.node.setEffect(self.effect),
                 lambda: self.compareImage("testBlurFX1"),
                 lambda: setRadius(8),
                 lambda: self.compareImage("testBlurFX2"),
                 removeFX,
                 lambda: self.compareImage("testBlurFX3"),
                 reAddFX,
                 lambda: self.compareImage("testBlurFX2"),
                 removeFX,
                 addNewFX,
                 lambda: self.compareImage("testBlurFX2"),
                 lambda: setRadius(300),
                ))

    def testHueSatFX(self):

        def resetFX():
            self.effect = avg.HueSatFXNode()
            self.node.setEffect(self.effect)

        def setParam(param, value):
            assert(hasattr(self.effect, param))
            setattr(self.effect, param, value)

        root = self.loadEmptyScene()
        self.node = avg.ImageNode(parent=root, pos=(10,10), href="rgb24alpha-64x64.png")
        resetFX()
        self.start(False,
                (self.skipIfMinimalShader,
                 lambda: self.compareImage("testHueSatFX1"),
                 lambda: setParam('saturation', -50),
                 lambda: self.compareImage("testHueSatFX2"),
                 lambda: setParam('saturation', -100),
                 lambda: self.compareImage("testHueSatFX3"),
                 lambda: setParam('saturation', -150),
                 lambda: self.compareImage("testHueSatFX3"),
                 resetFX,
                 lambda: setParam('hue', 180),
                 lambda: self.compareImage("testHueSatFX4"),
                 lambda: setParam('hue', -180),
                 lambda: self.compareImage("testHueSatFX4"),
                ))

    def testInvertFX(self):

        def resetFX():
            self.effect = avg.InvertFXNode()
            self.node.setEffect(self.effect)

        def redAlphaScene():
            self.redRect = avg.RectNode(parent=self.root, pos=(5, 5), fillcolor='FF0000',
                    fillopacity=1, opacity=0, size=(72, 72))
            self.node = avg.ImageNode(parent=self.root, pos=(10,10),
                    href="rgb24alpha-64x64.png")
            resetFX()

        self.root = self.loadEmptyScene()
        self.node = avg.ImageNode(parent=self.root, pos=(10,10), href="hsl.png")
        resetFX()
        self.start(False,
                (self.skipIfMinimalShader,
                 lambda: self.compareImage("testInvertFX1"),
                 redAlphaScene,
                 lambda: self.compareImage("testInvertFX2"),
                ))

    def testShadowFX(self):
        
        def setParams(offset, radius, opacity, color):
            effect.offset = offset
            effect.radius = radius
            effect.opacity = opacity
            effect.color =  color

        root = self.loadEmptyScene()
        rect = avg.RectNode(parent=root, pos=(9.5,9.5), color="0000FF")
        node = avg.ImageNode(parent=root, pos=(10,10), href="shadow.png")
        rect.size = node.size + (1, 1)
        effect = avg.ShadowFXNode((0,0), 1, 1, "FFFFFF")
        self.start(False,
                (self.skipIfMinimalShader,
                 lambda: node.setEffect(effect),
                 lambda: self.compareImage("testShadowFX1"),
                 lambda: setParams((0,0), 3, 2, "00FFFF"),
                 lambda: self.compareImage("testShadowFX2"),
                 lambda: setParams((2,2), 0.1, 1, "FFFFFF"),
                 lambda: self.compareImage("testShadowFX3"),
                 lambda: setParams((-2,-2), 0.1, 1, "FFFFFF"),
                 lambda: self.compareImage("testShadowFX4"),
                 lambda: setParams((-2,-2), 3, 1, "FFFFFF"),
                 lambda: self.compareImage("testShadowFX5"),
                 lambda: setParams((0,0), 0, 1, "FFFFFF"),
                 lambda: self.compareImage("testShadowFX6"),
                ))

    def testWordsShadowFX(self):
        
        def setParams(offset, radius, opacity, color):
            effect.offset = offset
            effect.radius = radius
            effect.opacity = opacity
            effect.color =  color

        root = self.loadEmptyScene()
        node = avg.WordsNode(parent=root, pos=(10,10), text="testtext", 
                font="Bitstream Vera Sans")
        effect = avg.ShadowFXNode()
        setParams((0,0), 1.5, 1.5, "FF0000")
        self.start(True,
                (self.skipIfMinimalShader,
                 lambda: node.setEffect(effect),
                 lambda: self.compareImage("testWordsShadowFX1"),
                 lambda: setParams((2,2), 2, 2, "00FFFF"),
                 lambda: self.compareImage("testWordsShadowFX2"),
                ))

    def testGamma(self):
        def setGamma(val):
            node.gamma = val

        root = self.loadEmptyScene()
        node = avg.ImageNode(parent=root, href="colorramp.png", gamma=(0.5,0.5,0.5))
        self.assertEqual(node.gamma, (0.5,0.5,0.5))
        self.start(False,
                (lambda: self.compareImage("testGamma1"),
                 lambda: setGamma((1.5,2.0,2.5)),
                 lambda: self.assertEqual(node.gamma, (1.5,2.0,2.5)),
                 lambda: self.compareImage("testGamma2"),
                ))

    def testIntensity(self):
        def setIntensity(val):
            node.intensity = val

        def showVideo():
            node.unlink(True)
            self.videoNode = avg.VideoNode(parent=root, size=(96,96), threaded=False, 
                    href="mpeg1-48x48.mov", intensity=(0.5,0.5,0.5))
            self.videoNode.play()

        root = self.loadEmptyScene()
        node = avg.ImageNode(parent=root, href="colorramp.png", intensity=(0.5,0.5,0.5))
        self.assertEqual(node.intensity, (0.5,0.5,0.5))
        player.setFakeFPS(10)
        self.start(False,
                (lambda: self.compareImage("testIntensity1"),
                 lambda: setIntensity((1.5,2.0,2.5)),
                 lambda: self.assertEqual(node.intensity, (1.5,2.0,2.5)),
                 lambda: self.compareImage("testIntensity2"),
                 showVideo,
                 lambda: self.compareImage("testIntensity3"),
                ))
        player.setFakeFPS(-1)
        self.videoNode = None

    def testWordsIntensity(self):
        root = self.loadEmptyScene()
        avg.WordsNode(parent=root, fontsize=24, font="Bitstream Vera Sans",
                intensity=(0.5,0.5,0.5), text="brightness",
                width=140)
        self.start(True,
                (lambda: self.compareImage("testWordsIntensity"),
                ))


    def testContrast(self):
        def setContrast(val):
            node.contrast = val

        def showVideo():
            node.unlink(True)
            videoNode = avg.VideoNode(parent=root, size=(96,96), threaded=False, 
                    href="mpeg1-48x48.mov", contrast=(0.5,0.5,0.5))
            videoNode.play()

        root = self.loadEmptyScene()
        node = avg.ImageNode(parent=root, href="colorramp.png", contrast=(0.5,0.5,0.5))
        self.assertEqual(node.contrast, (0.5,0.5,0.5))
        player.setFakeFPS(10)
        self.start(False,
                (lambda: self.compareImage("testContrast1"),
                 lambda: setContrast((1.5,2.0,2.5)),
                 lambda: self.assertEqual(node.contrast, (1.5,2.0,2.5)),
                 lambda: self.compareImage("testContrast2"),
                 showVideo,
                 lambda: self.compareImage("testContrast3"),
                ))
        player.setFakeFPS(-1)

    def testFXUpdate(self):
        # This tests if the FX render-on-demand functionality doesn't forget updates.
        def changeTexture():
            node.href = "colorramp.png"

        def addMaskTex():
            node.maskhref = "mask.png"

        def changeMaskTex():
            node.maskhref = "mask2.png"

        def changeMaskPos():
            node.maskpos = (10, 10)

        def changeFX():
            effect.radius = 2

        def addVideo():
            node.unlink(True)
            videoNode = avg.VideoNode(parent=root, threaded=False, size=(96,96),
                    href="mpeg1-48x48.mov")
            effect = avg.BlurFXNode()
            effect.radius = 0
            videoNode.setEffect(effect)
            videoNode.play()

        root = self.loadEmptyScene()
        node = avg.ImageNode(parent=root, href="rgb24alpha-64x64.png")
        effect = avg.BlurFXNode()
        effect.radius = 0
        player.setFakeFPS(25)
        self.start(False,
                (self.skipIfMinimalShader,
                 lambda: node.setEffect(effect),
                 changeTexture,
                 lambda: self.compareImage("testFXUpdateTex"),
                 addMaskTex,
                 lambda: self.compareImage("testFXUpdateMaskTex1"),
                 changeMaskTex,
                 lambda: self.compareImage("testFXUpdateMaskTex2"),
                 changeMaskPos,
                 lambda: self.compareImage("testFXUpdateMaskPos"),
                 changeFX,
                 lambda: self.compareImage("testFXUpdateFX"),
                 addVideo,
                 None,
                 lambda: self.compareImage("testFXUpdateVideo"),
                ))

    def testChromaKeyFX(self):

        def setParams(htol, ltol, stol):
            effect.htolerance = htol
            effect.ltolerance = ltol
            effect.stolerance = stol

        root = self.loadEmptyScene()
        node = avg.ImageNode(parent=root, href="rgb24-64x64.png")
        effect = avg.ChromaKeyFXNode()
        setParams(0.01, 0.01, 0.01)
        self.start(False,
                (self.skipIfMinimalShader,
                 lambda: node.setEffect(effect),
                 lambda: self.compareImage("testChromaKeyFX1"),
                 lambda: setParams(0.2, 0.2, 0.2),
                 lambda: self.compareImage("testChromaKeyFX2"),
                 lambda: effect.__setattr__("color", "FF0000"),
                 lambda: self.compareImage("testChromaKeyFX3"),
                 lambda: effect.__setattr__("spillthreshold", 1),
                 lambda: self.compareImage("testChromaKeyFX4"),
                ))

    def __createOffscreenCanvas(self):
        canvas = player.createCanvas(id="offscreen", size=(160,120), mediadir="media")
        root = canvas.getRootNode()
        avg.ImageNode(href="rgb24-32x32.png", parent=root)
        avg.ImageNode(id="test", pos=(32,0), href="rgb24alpha-32x32.png", parent=root)
        return canvas


def fxTestSuite(tests):
    availableTests = [
            "testImageNullFX",
            "testVideoNullFX",
            "testWordsNullFX",
            "testCanvasNullFX",
            "testNodeInCanvasNullFX",
            "testRenderPipeline",
            "testBlurFX",
            "testHueSatFX",
            "testInvertFX",
            "testShadowFX",
            "testWordsShadowFX",
            "testGamma",
            "testIntensity",
            "testWordsIntensity",
            "testContrast",
            "testFXUpdate",
            "testChromaKeyFX",
        ]
    return createAVGTestSuite(availableTests, FXTestCase, tests)
