##########################################################################
#
#  Copyright (c) 2013-2015, Image Engine Design Inc. All rights reserved.
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions are
#  met:
#
#      * Redistributions of source code must retain the above
#        copyright notice, this list of conditions and the following
#        disclaimer.
#
#      * Redistributions in binary form must reproduce the above
#        copyright notice, this list of conditions and the following
#        disclaimer in the documentation and/or other materials provided with
#        the distribution.
#
#      * Neither the name of John Haddon nor the names of
#        any other contributors to this software may be used to endorse or
#        promote products derived from this software without specific prior
#        written permission.
#
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
#  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
#  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
#  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
#  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
#  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES LOSS OF USE, DATA, OR
#  PROFITS OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
#  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
##########################################################################

import unittest

import IECore
import os
import Gaffer
import GafferTest
import GafferImage

class ImageTransformTest( unittest.TestCase ) :

	fileName = os.path.expandvars( "$GAFFER_ROOT/python/GafferImageTest/images/checker.exr" )
	path = os.path.expandvars( "$GAFFER_ROOT/python/GafferImageTest/images/" )

	def testIdentityHash( self ) :

		r = GafferImage.ImageReader()
		r["fileName"].setValue( self.fileName )

		t = GafferImage.ImageTransform()
		t["in"].setInput( r["out"] )

		c = Gaffer.Context()
		c["image:channelName"] = "R"
		c["image:tileOrigin"] = IECore.V2i( 0 )
		with c :
			h1 = t["out"].hash()
			h2 = r["out"].hash()
			self.assertEqual( h1, h2 )

	def testTilesWithSameInputTiles( self ) :

		# This particular transform (along with many others) has output tiles
		# which share the exact same set of input tiles affecting their result.
		# This revealed a bug in ImageTransform::hashChannelData() whereby the
		# tile origin wasn't being hashed in to break the hashes for these output
		# tiles apart.

		r = GafferImage.ImageReader()
		r["fileName"].setValue( os.path.join( self.path, "rgb.100x100.exr" ) )

		t = GafferImage.ImageTransform()
		t["in"].setInput( r["out"] )
		t["transform"]["rotate"].setValue( 1. )
		t["transform"]["scale"].setValue( IECore.V2f( 1.5, 1. ) )
		imageToTest = t["out"].image()
		imageToTest.blindData().clear()

		r2 = GafferImage.ImageReader()
		r2["fileName"].setValue( os.path.join( self.path, "knownTransformBug.exr" ) )
		expectedImage = r2['out'].image()
		expectedImage.blindData().clear()

		op = IECore.ImageDiffOp()
		res = op(
			imageA = imageToTest,
			imageB = expectedImage
		)

		self.assertFalse( res.value )

	def testScaleHash( self ) :

		r = GafferImage.ImageReader()
		r["fileName"].setValue( self.fileName )

		t = GafferImage.ImageTransform()
		t["in"].setInput( r["out"] )

		h1 = t["__scaledFormat"].hash()
		t["transform"]["scale"].setValue( IECore.V2f( 2., 2. ) )
		h2 = t["__scaledFormat"].hash()
		self.assertNotEqual( h1, h2 )

	def testDirtyPropagation( self ) :

		r = GafferImage.ImageReader()
		r["fileName"].setValue( self.fileName )

		t = GafferImage.ImageTransform()
		t["in"].setInput( r["out"] )

		cs = GafferTest.CapturingSlot( t.plugDirtiedSignal() )
		t["transform"]["scale"].setValue( IECore.V2f( 2., 2. ) )

		dirtiedPlugs = set( [ x[0].relativeName( x[0].node() ) for x in cs ] )
		self.assertEqual( len( dirtiedPlugs ), 8 )
		self.assertTrue( "transform.scale.x" in dirtiedPlugs )
		self.assertTrue( "transform.scale.y" in dirtiedPlugs )
		self.assertTrue( "transform.scale" in dirtiedPlugs )
		self.assertTrue( "transform" in dirtiedPlugs )
		self.assertTrue( "out" in dirtiedPlugs )
		self.assertTrue( "out.channelData" in dirtiedPlugs )
		self.assertTrue( "out.dataWindow" in dirtiedPlugs )
		self.assertTrue( "__scaledFormat" in dirtiedPlugs )

	def testOutputFormat( self ) :

		r = GafferImage.ImageReader()
		r["fileName"].setValue( self.fileName )

		t = GafferImage.ImageTransform()
		t["in"].setInput( r["out"] )
		t["transform"]["translate"].setValue( IECore.V2f( 2., 2. ) )

		c = Gaffer.Context()
		c["image:channelName"] = "R"
		c["image:tileOrigin"] = IECore.V2i( 0 )
		with c :
			self.assertEqual( t["out"]["format"].hash(), r["out"]["format"].hash() )

	def testHashPassThrough( self ) :

		r = GafferImage.ImageReader()
		r["fileName"].setValue( self.fileName )

		t = GafferImage.ImageTransform()
		t["in"].setInput( r["out"] )

		c = Gaffer.Context()
		c["image:channelName"] = "R"
		c["image:tileOrigin"] = IECore.V2i( 0 )
		t["enabled"].setValue( True )
		with c :
			self.assertEqual( r["out"].hash(), t["out"].hash() )

			t["transform"]["translate"].setValue( IECore.V2f( 20., 20.5 ) )
			self.assertNotEqual( r["out"].hash(), t["out"].hash() )

	def testDisabled( self ) :

		r = GafferImage.ImageReader()
		r["fileName"].setValue( self.fileName )

		t = GafferImage.ImageTransform()
		t["in"].setInput( r["out"] )

		c = Gaffer.Context()
		c["image:channelName"] = "R"
		c["image:tileOrigin"] = IECore.V2i( 0 )
		with c:
			cs = GafferTest.CapturingSlot( t.plugDirtiedSignal() )
			t["transform"]["translate"].setValue( IECore.V2f( 2., 2. ) )
			t["transform"]["rotate"].setValue( 90 )
			t["enabled"].setValue( True )
			self.assertNotEqual( r["out"].hash(), t["out"].hash() )

			t["enabled"].setValue( False )
			self.assertEqual( r["out"].hash(), t["out"].hash() )

	def testBoxFilter( self ) :
		self.__testFilter( "Box" )

	def testBSplineFilter( self ) :
		self.__testFilter( "BSpline" )

	def testBilinearFilter( self ) :
		self.__testFilter( "Bilinear" )

	def testCatmullRomFilter( self ) :
		self.__testFilter( "CatmullRom" )

	def testCubicFilter( self ) :
		self.__testFilter( "Cubic" )

	def testHermiteFilter( self ) :
		self.__testFilter( "Hermite" )

	def testLanczosFilter( self ) :
		self.__testFilter( "Lanczos" )

	def testMitchellFilter( self ) :
		self.__testFilter( "Mitchell" )

	def testSincFilter( self ) :
		self.__testFilter( "Sinc" )

	def __testFilter( self, filter ) :

		reader = GafferImage.ImageReader()
		reader["fileName"].setValue( os.path.join( self.path, "checkerWithNegativeDataWindow.200x150.exr" ) )

		t = GafferImage.ImageTransform()
		t["transform"]["translate"].setValue( IECore.V2f( 20, -10 ) )
		t["transform"]["scale"].setValue( IECore.V2f( .75, 1.1 ) )
		t["transform"]["rotate"].setValue( 40 )
		t["transform"]["pivot"].setValue( IECore.V2f( 50, 30 ) )
		t["filter"].setValue( filter )
		t["in"].setInput( reader["out"] )
		filteredImage = t["out"].image()
		filteredImage.blindData().clear()

		file = os.path.expandvars( "$GAFFER_ROOT/python/GafferImageTest/images/transformedChecker" + filter + ".200x150.exr" )
		expectedOutput = GafferImage.ImageReader()
		expectedOutput["fileName"].setValue( os.path.join( self.path, file ) )
		expectedImage = expectedOutput['out'].image()
		expectedImage.blindData().clear()

		op = IECore.ImageDiffOp()
		res = op(
			imageA = filteredImage,
			imageB = expectedImage
		)

		self.assertFalse( res.value )

	def testPassThrough( self ) :

		c = GafferImage.Constant()
		t = GafferImage.ImageTransform()
		t["in"].setInput( c["out"] )
		t["transform"]["translate"].setValue( IECore.V2f( 1, 0 ) )

		self.assertEqual( t["out"]["metadata"].hash(), c["out"]["metadata"].hash() )
		self.assertEqual( t["out"]["channelNames"].hash(), c["out"]["channelNames"].hash() )

		self.assertEqual( t["out"]["metadata"].getValue(), c["out"]["metadata"].getValue() )
		self.assertEqual( t["out"]["channelNames"].getValue(), c["out"]["channelNames"].getValue() )

	def testCopyPaste( self ) :

		script = Gaffer.ScriptNode()
		script["t"] = GafferImage.ImageTransform()

		script.execute( script.serialise( filter = Gaffer.StandardSet( [ script["t"] ] ) ) )

	def testAffects( self ) :

		c = GafferImage.Constant()
		t = GafferImage.ImageTransform()
		t["in"].setInput( c["out"] )

		cs = GafferTest.CapturingSlot( t.plugDirtiedSignal() )
		c["color"]["r"].setValue( .25 )

		self.assertTrue( t["out"]["channelData"] in set( s[0] for s in cs ) )

if __name__ == "__main__":
	unittest.main()


