# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is the Python Computer Graphics Kit.
#
# The Initial Developer of the Original Code is Matthias Baas.
# Portions created by the Initial Developer are Copyright (C) 2004
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****

from _OpenGL.GL import *
import _Image as Image
import _ImageDraw as ImageDraw
import glob, types
import component
from eventmanager import eventManager
from events import *
from scene import getScene
from targetcamera import TargetCamera
from plane import Plane
from quadrics import Sphere
from glmaterial import GLMaterial, GLTexture
from sl import *
from slots import *
from cgtypes import *
from math import *
import cmds
import _core

# Cube transition
class XCube:
    def __init__(self, duration=2.0):
        self.duration = duration
    
    # preTransition
    def preTransition(self, slideshow):
        """Prepare transition.

        This method is called at the beginning of a transition.
        """
        slideshow.backplate.rot = mat3().rotation(pi/2, vec3(0,1,0))
        slideshow.backplate.pos = vec3(2,0,-2)
        slideshow.helper.pos = vec3(0,0,-2)
        slideshow.helper.rot = mat3(1)
        
        cmds.link(slideshow.frontplate, slideshow.helper)
        cmds.link(slideshow.backplate, slideshow.helper)

    # doTransition
    def doTransition(self, slideshow, t):
        """Do the transition.

        This method is called every frame during the transition.
        t is the current transition value (0-1).
        At t=0 the frontplate shows and the backplate is not visible,
        at t=1 the frontplate should not be visible anymore and the
        backplate has come to sight
        """
        w = smoothstep(0,1,smoothstep(0, 1, t))
        slideshow.helper.rot = mat3().rotation(-w*pi/2, vec3(0,1,0))

    # postTransition
    def postTransition(self, slideshow):
        """Post transition.

        This method is called at the end of a transition.
        post: frontplate must be visible
        """
        slideshow.helper.rot = mat3(1)
        cmds.link(slideshow.frontplate, None)
        cmds.link(slideshow.backplate, None)


# Crossfade
class XFade:
    def __init__(self, duration=2.0, zmove=0.0):
        self.duration = duration
        self.zmove = zmove

    ############ XFade transition ####################

    def preTransition(self, slideshow):
        slideshow.backplate.pos = slideshow.frontplate.pos+vec3(0,0,-0.001)
        mat = slideshow.frontplate.getMaterial()
        mat.blend_sfactor = GL_SRC_ALPHA
        mat.blend_dfactor = GL_ONE_MINUS_SRC_ALPHA

    def doTransition(self, slideshow, t):
        w = smoothstep(0,1,t)
        mat = slideshow.frontplate.getMaterial()
        mat.diffuse = vec4(1,1,1,1-w)
        slideshow.frontplate.pos = vec3(0,0,self.zmove*t*t)

    def postTransition(self, slideshow):
        mat = slideshow.frontplate.getMaterial()
        mat.diffuse = vec4(1,1,1,1)
        mat.blend_sfactor = -1
        mat.blend_dfactor = -1
        mat = slideshow.backplate.getMaterial()
        mat.diffuse = vec4(1,1,1,1)

######################################################################

# Slide
class Slide:
    def __init__(self, filepattern, transition=XCube()):
        self.files = glob.glob(filepattern)
        self.transition = transition

######################################################################

# SlideShow
class SlideShow(component.Component):
    """Slide show component.
    """
    
    def __init__(self, name="SlideShow", slides=[], auto_insert=True):
        
        component.Component.__init__(self, name, auto_insert)

        if isinstance(slides, types.StringTypes):
            slides = Slide(slides)

        try:
            len(slides)
        except:
            slides = [slides]

        self.images = []
        for s in slides:
            for f in s.files:
                self.images.append((f, s.transition))

        # Currently displayed image index
        self.imageidx = 0

        self.setup()

        em = eventManager()
        em.connect(STEP_FRAME, self)
        em.connect(LEFT_DOWN, self)
        em.connect(KEY_PRESS, self)

        self.transition = self.images[0][1]

        # Global time when the next transition starts
#        self.transition_start = 3.0
        self.transition_start = 999999.0
        # Slide show state
        self.state = 0


    # setup
    def setup(self):
        """Setup the scene.
        """
        scene = getScene()

        # Set up direction
        scene.up = (0,1,0)

        # Camera
        self.cam = TargetCamera(
            name = "SlideShow_Cam",
            pos = (0,0,5.6),
            fov = 30
        )

        # Set background...
        gradient = Image.new("RGB", (2,128))
        draw = ImageDraw.Draw(gradient)
        colbottom = vec3(0.1, 0.1, 0.3)
        colmid = vec3(0.95,0.95,1)
        coltop = vec3(0.2, 0.2, 0.4)
        colormap = [colbottom, colbottom, colmid, coltop, coltop]
        for y in range(128):
            t = float(y)/128
            c = spline(t, colormap)
            draw.line([0,y, 1,y], fill=(int(c.x*255), int(c.y*255), int(c.z*255)))

        back = Plane(
            lx=4,
            ly=3,
            pos=(0,0,-5),
            scale=2.4,
            material = GLMaterial( diffuse = (0,1,1),
                                   texture = GLTexture(image = gradient,
                                                       mode  = GL_REPLACE))
        )


        # Create plate materials...
        initial = Image.new("RGB", (4,4))
        invY = mat4(1).translate(vec3(0,1,0)).scale(vec3(1,-1,1))
        
        self.mat1 = GLMaterial(
           diffuse = (1,1,1,1),
           texture = GLTexture( image = initial,
                                mode = GL_REPLACE,
#                                size = (512,512),
                                transform = invY,
                                wrap_s = GL_CLAMP,
                                wrap_t = GL_CLAMP
                               )
        )
        self.backmaterial = self.mat1
        self.setBackImage(self.images[0][0])

        self.mat2 = GLMaterial(
           diffuse = (1,1,1,1),
           texture = GLTexture( image = initial,
                                mode = GL_REPLACE,
#                                size = (512,512),
                                transform = invY,
                                wrap_s = GL_CLAMP,
                                wrap_t = GL_CLAMP
                               )
        )
        self.backmaterial = self.mat2
        self.setBackImage(self.images[1][0])

        self.frontmaterial = self.mat1
        self.backmaterial = self.mat2

        # Create plates...
        self.frontplate = Plane(lx = 4, ly = 3, material = self.frontmaterial)

        self.backplate = Plane(lx = 4, ly = 3,
                               pos = (0,0,-0.1),
                               material = self.backmaterial
                               )

        self.helper = Sphere(radius=0.1, pos=(0,0,-1))


    # onLeftDown
    def onLeftDown(self, e):
        if self.state==0:
            self.transition_start = getScene().timer().time

    def onKeyPress(self, e):
        # Page down or Enter?
        if e.keycode==281 or e.keycode==13:
            self.transition_start = getScene().timer().time
        # Page up
        elif e.keycode==280:
            pass
        # q (reset cam)
        elif e.key=="q":
            getScene().up = vec3(0,1,0)
            self.cam.pos = vec3(0,0,5.6)
            self.cam.target = vec3(0,0,0)
            self.cam.fov = 30
            cmds.link(self.cam)
        
    # onStepFrame
    def onStepFrame(self):
        timer = getScene().timer()

        ### State 0: Waiting for the transition
        if self.state==0:
            if timer.time>=self.transition_start:
                self.state = 1

        ### State 1: Begin of transition
        if self.state==1:
            self.transition.preTransition(self)
            eventManager().event("SlidePreTransition", self.images[self.imageidx][0])
            self.state = 2
            
        ### State 2: Transition
        if self.state==2:
            if self.transition.duration>0:
                t = (timer.time-self.transition_start)/self.transition.duration
            else:
                t = 1.0
            if t>1.0:
                t = 1.0
            self.transition.doTransition(self, t)
            eventManager().event("SlideDoTransition", t)
            if t==1.0:
                self.state = 3
                
        ### State 3: Transition is over
        elif self.state==3:
            eventManager().event("SlidePostTransition")
            self.transition.postTransition(self)
            self.switchSlide()

            self.frontplate.transform = mat4(1)
            self.backplate.transform = mat4(1)
            self.backplate.pos = vec3(0,0,-1)
                       
#            self.transition_start += 5.0
            self.transition_start = 999999.0
            self.state = 0

    # switchSlide
    def switchSlide(self):
        """Prepare everything for the next slide.
        """
        # Swap front and back material...
        dummy = self.frontmaterial
        self.frontmaterial = self.backmaterial
        self.backmaterial = dummy

        # Update the materials of the plates
        self.frontplate.setMaterial(self.frontmaterial)
        self.backplate.setMaterial(self.backmaterial)

        # Set the next image on the back material
        idx = (self.imageidx+2)%len(self.images)
        self.setBackImage(self.images[idx][0])
        self.imageidx = (self.imageidx+1)%len(self.images)

        # Set the new transition object
        self.transition = self.images[self.imageidx][1]
        

    # setBackImage
    def setBackImage(self, name):
        """Set a new image on the back material."""
        self.backmaterial.texture.imagename = name
        img = Image.open(name)
        if img.mode!="RGB" and img.mode!="RGBA":
            img = img.convert("RGB")
        self.backmaterial.texture.image = img
        w,h = img.size
        f = (4.0*h)/(3.0*w)
        if f>=1.0:
            S = mat4(1).scale(vec3(f,1,1))
            S.translate(vec3(-0.5+0.5/f,0,0))
            draw = ImageDraw.Draw(img)
            draw.line([(0,0), (0,h)], fill=(0,0,0))
            draw.line([(1,0), (1,h)], fill=(0,0,0))
            draw.line([(w-1,0), (w-1,h)], fill=(0,0,0))
            draw.line([(w-2,0), (w-2,h)], fill=(0,0,0))
        else:
            f = (3.0*w)/(4.0*h)
            S = mat4(1).scale(vec3(1,f,1))
            S.translate(vec3(0,-0.5+0.5/f,0))
            draw = ImageDraw.Draw(img)
            draw.line([(0,0), (w,0)], fill=(0,0,0))
            draw.line([(0,1), (w,1)], fill=(0,0,0))
            draw.line([(0,h-1), (w,h-1)], fill=(0,0,0))
            draw.line([(0,h-2), (w,h-2)], fill=(0,0,0))
        invY = mat4(1).translate(vec3(0,1,0)).scale(vec3(1,-1,1))
        self.backmaterial.texture.transform = invY*S
               
            

######################################################################

#sl = SlideShow()
