#!/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
#


import shutil

from libavg import avg, player
from testcase import *

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

    def testImageHRef(self):
        def createXmlNode(pos):
            node = player.createNode(
                    """<image pos="%s" href="rgb24-32x32.png"/>"""%str(pos))
            self.assertEqual(node.getMediaSize(), avg.Point2D(32, 32))
            return node

        def createDictNode(root, p):
            node = avg.ImageNode(pos=p, href="rgb24-32x32.png", parent=root)
            self.assertEqual(node.getMediaSize(), avg.Point2D(32, 32))
            self.assertEqual(node.size, avg.Point2D(32, 32))
            return node

        def addNodes(y):
            xmlNode = createXmlNode((16, y))
            root.appendChild(xmlNode)
            
            createDictNode(root, (48, y))
            
            noAttachNode = createXmlNode((80, y))
            noAttachNode.href = "rgb24alpha-32x32.png"
            self.assertEqual(noAttachNode.getMediaSize(), avg.Point2D(32, 32))
            self.assertEqual(noAttachNode.size, avg.Point2D(32,32))
            root.appendChild(noAttachNode)

            attachNode = createXmlNode((112, y))
            root.appendChild(attachNode)
            attachNode.href = "rgb24alpha-32x32.png"
            self.assertEqual(attachNode.getMediaSize(), avg.Point2D(32, 32))
            self.assertEqual(attachNode.size, avg.Point2D(32,32))

        def setUnicodeHref():
            if self._isCurrentDirWriteable():
                # Can't check unicode filenames into svn or the windows client breaks.
                # So we rename the file locally.
                shutil.copyfile("media/oe.png", u"media/ö.png")
                node = createXmlNode((16, 16))
                root.appendChild(node)
                node.href = u"ö.png"
                os.remove(u"media/ö.png")

        def compareUnicode():
            if self._isCurrentDirWriteable():
                self.compareImage("testImgHRef3")

        root = self.loadEmptyScene()
        addNodes(16)
        self.start(False,
                (lambda: self.compareImage("testImgHRef1"),
                 lambda: addNodes(48),
                 lambda: self.compareImage("testImgHRef2"),
                 setUnicodeHref,
                 compareUnicode
                ))
      
    def testImagePos(self):
        def createXmlNode(pos):
            return player.createNode(
                    """<image pos="%s" href="rgb24-32x32.png"/>"""%str(pos))        

        def createDictNode(root, p):
            return avg.ImageNode(pos=p, href="rgb24-32x32.png", parent=root)

        def illegalMove(node):
            self.assertException(node.pos.x == 23)
            self.assertException(node.pos.y == 23)

        def addNodes(y):
            xmlNode = createXmlNode((16, y))
            root.appendChild(xmlNode)
            createDictNode(root, (48, y))
            noAttachNode = createXmlNode((0, 0))
            noAttachNode.pos = avg.Point2D(80, y)
            illegalMove(noAttachNode)
            root.appendChild(noAttachNode)
            attachNode = createXmlNode((0, 0))
            root.appendChild(attachNode)
            attachNode.pos = avg.Point2D(112, y)
            illegalMove(attachNode)

        root = self.loadEmptyScene()
        addNodes(16)
        self.start(False,
                (lambda: self.compareImage("testImgPos1"),
                 lambda: addNodes(48),
                 lambda: self.compareImage("testImgPos2"),
                ))

    def testImageSize(self):
        def createXmlNode(pos, size):
            return player.createNode(
                    """<image pos="%s" size="%s" href="rgb24-64x64.png"/>"""
                    %(str(pos), str(size))) 

        def createDictNode(p, s):
            return avg.ImageNode(pos=p, size=s, href="rgb24-64x64.png")

        def addNodes(y):
            xmlNode = createXmlNode((16, y), (32, 32))
            self.assertEqual(xmlNode.size, avg.Point2D(32, 32))
            root.appendChild(xmlNode)
            dictNode = createDictNode((48, y), (32, 32))
            self.assertEqual(dictNode.size, avg.Point2D(32, 32))
            root.appendChild(dictNode)
            noAttachNode = createXmlNode((80, y), (0, 0))
            noAttachNode.size = avg.Point2D(32, 32)
            self.assertEqual(noAttachNode.size, avg.Point2D(32, 32))
            root.appendChild(noAttachNode)
            attachNode = createXmlNode((112, y), (0, 0))
            root.appendChild(attachNode)
            attachNode.size = avg.Point2D(32, 32)
            self.assertEqual(attachNode.size, avg.Point2D(32, 32))

        root = self.loadEmptyScene()
        addNodes(16)
        self.start(False,
                (lambda: self.compareImage("testImgSize1"),
                 lambda: addNodes(48),
                 lambda: self.compareImage("testImgSize2"),
                ))
       
    def testImageWarp(self):
        def createNode(p):
            return avg.ImageNode(pos=p, href="rgb24-32x32.png",
                    maxtilewidth=16, maxtileheight=8) 

        def moveVertex(node):
            grid = node.getWarpedVertexCoords()
            grid[0][1] = (grid[0][1][0]+0.25, grid[0][1][1]+0.25)
            node.setWarpedVertexCoords(grid)

        def testEarlyAccessException():
            node = createNode((16, 16))
            root.appendChild(node)
            self.assertException(node.getWarpedVertexCoords)
            node.unlink()

        def addNode():
            self.node = createNode((16, 16))
            root.appendChild(self.node)
            moveVertex(self.node)
       
        def changeHref():
            self.node.href = "rgb24-65x65.png"
            grid = self.node.getWarpedVertexCoords()
            self.assert_(len(grid) == 10)
            self.assert_(len(grid[0]) == 6)
            

        root = self.loadEmptyScene()
        testEarlyAccessException()
        self.start(False,
                (lambda: addNode(),
                 lambda: self.compareImage("testImgWarp1"),
                 lambda: changeHref(),
                 lambda: self.compareImage("testImgWarp2"),
                ))

    def testBitmap(self):
        def getBitmap(node):
            bmp = node.getBitmap()
            self.assertEqual(bmp.getSize(), (65,65))
            self.assert_(bmp.getFormat() == avg.R8G8B8X8 or 
                    bmp.getFormat() == avg.B8G8R8X8)
            node.setBitmap(bmp)
            self.assertEqual(node.getMediaSize(), (65,65))
        
        def loadFromBitmap(p, orighref):
            node = avg.ImageNode(pos=p, size=(32, 32), href=orighref)
            bmp = avg.Bitmap('media/rgb24-65x65.png')
            self.assertEqual(bmp.getSize(), (65,65))
            node.setBitmap(bmp)
            self.assertEqual(node.getMediaSize(), (65,65))
            root.appendChild(node)
        
        def testStringConversion():
            bmp = avg.Bitmap('media/rgb24-65x65.png')
            s = bmp.getPixels()
            bmp1 = avg.Bitmap(bmp.getSize(), bmp.getFormat(), "sample")
            bmp1.setPixels(s)
            self.assert_(self.areSimilarBmps(bmp, bmp1, 0.01, 0.01))

        def testCropRect():
            bmp = avg.Bitmap('media/rgb24-65x65.png')
            bmp1 = avg.Bitmap(bmp, (32,32), (64,64))
            self.assert_(bmp1.getSize() == (32,32))
            node = avg.ImageNode(pos=(96,0), parent=root)
            node.setBitmap(bmp1)

        def testBlt():
            srcBmp = avg.Bitmap('media/rgb24-65x65.png')
            destBmp = avg.Bitmap((65,65), srcBmp.getFormat(), "bmp")
            destBmp.blt(srcBmp, (0,0))
            destBmp.blt(srcBmp, (32,32))
            node = avg.ImageNode(pos=(96,32), size=(32,32), parent=root)
            node.setBitmap(destBmp)
            
        def testResize():
            srcBmp = avg.Bitmap('media/rgb24-32x32.png')
            destBmp = srcBmp.getResized((64,64))
            self.assert_(destBmp.getSize() == (64,64))
            node = avg.ImageNode(pos=(128,0), size=(32,32), parent=root)
            node.setBitmap(destBmp)

        def testUnicode():
            if self._isCurrentDirWriteable():
                # Can't check unicode filenames into svn or the windows client breaks.
                # So we rename the file locally.
                shutil.copyfile("media/oe.png", u"media/ö.png")
                avg.Bitmap(u"media/ö.png")
                os.remove(u"media/ö.png")

        def testGetPixel():
            bmp = avg.Bitmap('media/rgb24-65x65.png')
            self.assertEqual(bmp.getPixel((1,1)), (255,0,0,255))
            self.assertEqual(bmp.getPixel((33,1)), (0,255,0,255))
            bmp = avg.Bitmap('media/rgb24alpha-64x64.png')
            self.assertEqual(bmp.getPixel((1,1)), (0,0,0,0))
            self.assertEqual(bmp.getPixel((63,1)), (83,255,83,142))
            bmp = avg.Bitmap('media/greyscale.png')
            self.assertEqual(bmp.getPixel((1,1)), (255,255,255,255))
            self.assertEqual(bmp.getPixel((1,63)), (0,0,0,255))
            self.assertException(lambda: bmp.getPixel((64,0)))

        def setNullBitmap():
            node.setBitmap(None)

        def testSubBitmap():
            srcBmp = avg.Bitmap('media/rgb24-32x32.png')
            destBmp = avg.Bitmap(srcBmp, (16,16), (32,32))
            self.assertEqual(srcBmp.getPixel((16,16)), destBmp.getPixel((0,0)))
            self.assertException(lambda: avg.Bitmap(srcBmp, (16,16), (16,32)))

        node = avg.ImageNode(href="media/rgb24-65x65.png", size=(32, 32))
        getBitmap(node)

        root = self.loadEmptyScene()
        node = avg.ImageNode(pos=(0,0), size=(32, 32), href="rgb24-65x65.png")
        root.appendChild(node)
        getBitmap(node)
        self.assertEqual(node.size, (32,32))
        loadFromBitmap((32,0), "")
        loadFromBitmap((64,0), "rgb24alpha-64x64.png")
        testStringConversion()
        testUnicode()
        self.start(False,
                (lambda: getBitmap(node),
                 lambda: loadFromBitmap((32,32), ""),
                 lambda: loadFromBitmap((64,32), "rgb24alpha-64x64.png"),
                 lambda: self.compareImage("testBitmap1"),
                 testCropRect,
                 lambda: self.compareImage("testBitmap2"),
                 testBlt,
                 lambda: self.compareImage("testBitmap3"),
                 testResize,
                 lambda: self.compareImage("testBitmap4"),
                 testGetPixel,
                 lambda: self.assertException(setNullBitmap),
                 testSubBitmap,
                ))

    def testBitmapManager(self):
        WAIT_TIMEOUT = 2000
        def expectException(returnValue, nextAction):
            if isinstance(returnValue, Exception):
                nextAction()
            else:
                raise RuntimeError("Expected exception, got %s (%s)" % (
                        returnValue, type(returnValue)))
            
        def loadValidBitmap():
            def validBitmapCb(bitmap):
                self.assert_(not isinstance(bitmap, Exception))
                player.setTimeout(0, loadBitmapWithPixelFormat)

            avg.BitmapManager.get().loadBitmap("media/rgb24alpha-64x64.png",
                    validBitmapCb)

        def loadBitmapWithPixelFormat():
            def validBitmapCb(bitmap):
                self.assert_(not isinstance(bitmap, Exception))
                self.assert_(bitmap.getFormat() == avg.B5G6R5)
                player.setTimeout(0, loadUnexistentBitmap)

            avg.BitmapManager.get().loadBitmap("media/rgb24alpha-64x64.png",
                    validBitmapCb, avg.B5G6R5)

        def loadUnexistentBitmap():
            avg.BitmapManager.get().loadBitmap("nonexistent.png",
                    lambda bmp: expectException(
                            returnValue=bmp,
                            nextAction=lambda: player.setTimeout(0, loadBrokenBitmap)))

        def loadBrokenBitmap():
            import tempfile
            tempFileName = os.path.join(tempfile.gettempdir(),
                    "broken.png")
            open(tempFileName, "w")

            def cleanupAndTestReturnValue(returnValue):
                os.unlink(tempFileName)
                expectException(returnValue=returnValue, nextAction=player.stop)

            avg.BitmapManager.get().loadBitmap(tempFileName,
                    cleanupAndTestReturnValue)

        def reportStuck():
            raise RuntimeError("BitmapManager didn't reply "
                    "within %dms timeout" % WAIT_TIMEOUT)
            player.stop()
            
        for multithread in [False, True]:
            self.loadEmptyScene()
            if multithread:
                avg.BitmapManager.get().setNumThreads(2)
            player.setTimeout(WAIT_TIMEOUT, reportStuck)
            player.setResolution(0, 0, 0, 0)
            loadValidBitmap()
            player.play()
        avg.BitmapManager.get().setNumThreads(1)
        
    def testBitmapManagerException(self):
        def bitmapCb(bitmap):
            raise RuntimeError

        self.loadEmptyScene()
        avg.BitmapManager.get().loadBitmap("rgb24alpha-64x64.png", bitmapCb),
        self.assertException(player.play)

    def testBlendMode(self):
        def isBlendMinMaxSupported():
            def tryInsertNode():
                try:
                    avg.ImageNode(href="rgb24-65x65.png", blendmode="min", parent=root)
                except RuntimeError:
                    self.supported = False
            root = self.loadEmptyScene()
            self.supported = True
            self.start(False,
                    (tryInsertNode,
                    ))
            return self.supported
            

        def setBlendMode():
            blendNode.blendmode="add"
        
        if not(isBlendMinMaxSupported()):
            self.skip("Blend modes min and max not supported.")
            return
        root = self.loadEmptyScene()
        avg.ImageNode(href="freidrehen.jpg", parent=root)
        blendNode = avg.ImageNode(opacity=0.6, href="rgb24-65x65.png", parent=root)
        avg.ImageNode(pos=(0,48), opacity=0.6, href="rgb24-65x65.png", blendmode="add",
                parent=root)
        avg.ImageNode(pos=(48,0), opacity=1, href="rgb24-65x65.png", blendmode="min",
                parent=root)
        avg.ImageNode(pos=(48,48), opacity=1, href="rgb24-65x65.png", blendmode="max",
                parent=root)

        self.start(False,
                (lambda: self.compareImage("testBlend1"),
                 setBlendMode,
                 lambda: self.compareImage("testBlend2")
                ))

    def testImageMask(self):
        def createNode(p):
            node = avg.ImageNode(href="rgb24-65x65.png", maskhref="mask.png", 
                    pos=p, size=(32, 32))
            root.appendChild(node)

        def setNoAttach(p):
            node = avg.ImageNode(href="rgb24-65x65.png", pos=p, size=(32, 32))
            node.maskhref = "mask.png"
            root.appendChild(node)

        def setAttach(p):
            node = avg.ImageNode(href="rgb24-65x65.png", pos=p, size=(32, 32))
            root.appendChild(node)
            node.maskhref = "mask.png"

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

        def changeBaseHRef():
            node.href = "greyscale.png" 

        def setMaskNotFound():
            node.maskhref = "nonexistentmask.png"        
            
        root = self.loadEmptyScene()
        createNode((0,0))
        node = root.getChild(0)
        setNoAttach((32,0))
        setAttach((64,0))
        self.start(False,
                (lambda: createNode((0, 32)),
                 lambda: setNoAttach((32,32)),
                 lambda: setAttach((64,32)),
                 lambda: self.compareImage("testImgMask1"),
                 changeHRef,
                 lambda: self.compareImage("testImgMask2"),
                 changeBaseHRef,
                 lambda: self.compareImage("testImgMask3"),
                 setMaskNotFound
                ))

    def testImageMaskCanvas(self):
        root = self.loadEmptyScene()
        canvas = player.createCanvas(id="testcanvas", size=(64,64), mediadir="media")
        avg.ImageNode(href="rgb24-64x64.png", parent=canvas.getRootNode())
        avg.RectNode(size=(160,120), fillcolor="FFFFFF", fillopacity=1, parent=root)
        avg.ImageNode(href="canvas:testcanvas", maskhref="mask.png", parent=root)
        self.start(False,
                (lambda: self.compareImage("testImgMaskCanvas"),))

    def testImageMaskPos(self):
        def createNode(p):
            node = avg.ImageNode(href="rgb24-65x65.png", maskhref="mask.png", 
                    pos=p, size=(32, 32), maskpos=(32, 32))
            root.appendChild(node)
            
        def setNoAttach(p):
            node = avg.ImageNode(href="rgb24-65x65.png", maskhref="mask.png", 
                    pos=p, size=(32, 32))
            node.maskpos = (32, 32)
            root.appendChild(node)

        def setAttach(p):
            node = avg.ImageNode(href="rgb24-65x65.png", maskhref="mask.png", 
                    pos=p, size=(32, 32))
            root.appendChild(node)
            node.maskpos = (32, 32)

        root = self.loadEmptyScene()
        createNode((0,0))
        setNoAttach((32,0))
        setAttach((64,0))
        self.start(False,
                (lambda: createNode((0, 32)),
                 lambda: setNoAttach((32,32)),
                 lambda: setAttach((64,32)),
                 lambda: self.compareImage("testImgMaskPos")
                ))

    def testImageMaskSize(self):
        def createNode(p):
            avg.ImageNode(href="rgb24-65x65.png", maskhref="mask.png", 
                    pos=p, size=(32, 32), masksize=(48, 48), parent=root)
            
        def setNoAttach(p):
            node = avg.ImageNode(href="rgb24-65x65.png", maskhref="mask.png", 
                    pos=p, size=(32, 32))
            node.masksize = (48, 48)
            root.appendChild(node)

        def setAttach(p):
            node = avg.ImageNode(href="rgb24-65x65.png", maskhref="mask.png", 
                    pos=p, size=(32, 32), parent=root)
            node.masksize = (48, 48)

        def setPos():
            node.maskpos = (16, 16)

        def resetPos():
            node.maskpos = (0, 0)
            node.masksize = (0, 0)

        root = self.loadEmptyScene()
        createNode((0,0))
        node = root.getChild(0)
        setNoAttach((32,0))
        setAttach((64,0))
        self.start(False,
                (lambda: createNode((0, 32)),
                 lambda: setNoAttach((32,32)),
                 lambda: setAttach((64,32)),
                 lambda: self.compareImage("testImgMaskSize1"),
                 setPos,
                 lambda: self.compareImage("testImgMaskSize2"),
                 resetPos,
                 lambda: self.compareImage("testImgMaskSize3")
                ))

    def testImageMipmap(self):
        root = self.loadEmptyScene()
        avg.ImageNode(size=(64,64), href="checker.png", mipmap=True, parent=root)
        self.start(False,
                (lambda: self.compareImage("testMipmap"),))

    def testImageCompression(self):
        def loadBitmap():
            bmp = avg.Bitmap("media/colorramp.png")
            self.image.setBitmap(bmp)

        def relink():
            self.image.unlink(False)
            root.appendChild(self.image)

        def checkAlpha():
            self.image.href="rgb24alpha-64x64.png"

        root = self.loadEmptyScene()
        self.image = avg.ImageNode(href="rgb24-64x64.png", compression="B5G6R5",
                parent=root)
        self.assertEqual(self.image.compression, "B5G6R5")
        self.start(False, 
                [lambda: self.compareImage("testTexCompression1"),
                 loadBitmap,
                 lambda: self.compareImage("testTexCompression2"),
                 relink,
                 lambda: self.compareImage("testTexCompression2"),
                 checkAlpha,
                ])

    def testSpline(self):
        spline = avg.CubicSpline([(0,3),(1,2),(2,1),(3,0)])
        self.assertAlmostEqual(spline.interpolate(0), 3)
        self.assertAlmostEqual(spline.interpolate(0.5), 2.5)
        self.assertAlmostEqual(spline.interpolate(1), 2)
        self.assertAlmostEqual(spline.interpolate(-1), 4)
        self.assertAlmostEqual(spline.interpolate(4), -1)

        spline = avg.CubicSpline([(2,0),(4,1),(6,3),(8,6)])
        self.assertAlmostEqual(spline.interpolate(2), 0)
        self.assert_(spline.interpolate(3) < 0.5)
        self.assert_(spline.interpolate(3) > 0.0)
        self.assert_(spline.interpolate(7) < 4.5)
        self.assert_(spline.interpolate(7) > 4)


def imageTestSuite(tests):
    availableTests = (
            "testImageHRef",
            "testImagePos",
            "testImageSize",
            "testImageWarp",
            "testBitmap",
            "testBitmapManager",
            "testBitmapManagerException",
            "testBlendMode",
            "testImageMask",
            "testImageMaskCanvas",
            "testImageMaskPos",
            "testImageMaskSize",
            "testImageMipmap",
            "testImageCompression",
            "testSpline",
            )
    return createAVGTestSuite(availableTests, ImageTestCase, tests)
