/*
   Copyright (c) 2012 Emitrom LLC. All rights reserved.
   For licensing questions, please contact us at licensing@emitrom.com

   Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */

package com.emitrom.lienzo.client.core.animation;

import com.emitrom.lienzo.client.core.shape.Node;
import com.google.gwt.animation.client.AnimationScheduler;
import com.google.gwt.animation.client.AnimationScheduler.AnimationCallback;
import com.google.gwt.user.client.Timer;

public class AbstractAnimation implements IAnimation, IAnimationHandle
{
    private Node<?>                  m_node;

    private final double             m_duration;

    private final IAnimationCallback m_callback;

    private double                   m_begtime = 0;

    private boolean                  m_running = false;

    protected AbstractAnimation(double duration, IAnimationCallback callback)
    {
        m_duration = duration;

        m_callback = callback;
    }

    protected double getBegTime()
    {
        return m_begtime;
    }

    @Override
    public IAnimationHandle run()
    {
        if (isRunning())
        {
            return this;
        }
        m_running = true;

        m_begtime = System.currentTimeMillis();

        final AnimationCallback animate = new AnimationCallback()
        {
            @Override
            public void execute(double time)
            {
                doFrame();

                if (isRunning())
                {
                    AnimationScheduler.get().requestAnimationFrame(this);
                }
                else
                {
                    doClose();
                }
            }
        };
        Timer t = new Timer()
        {
            @Override
            public void run()
            {
                if (isRunning())
                {
                    doStart();

                    AnimationScheduler.get().requestAnimationFrame(animate);
                }
            }
        };
        t.schedule(0);

        return this;
    }

    @Override
    public IAnimationHandle stop()
    {
        m_running = false;

        return this;
    }

    @Override
    public boolean isRunning()
    {
        return m_running;
    }

    @Override
    public IAnimation setNode(Node<?> node)
    {
        m_node = node;

        return this;
    }

    @Override
    public Node<?> getNode()
    {
        return m_node;
    }

    @Override
    public double getPercent()
    {
        double duration = getDuration();

        if (duration != INDEFINITE_ANIMATION)
        {
            return (((double) System.currentTimeMillis()) - m_begtime) / m_duration;
        }
        else
        {
            return 1.0;
        }
    }

    @Override
    public double getDuration()
    {
        return m_duration;
    }

    @Override
    public IAnimation doStart()
    {
        if (null != m_callback)
        {
            m_callback.onStart(this, this);
        }
        return this;
    }

    @Override
    public IAnimation doFrame()
    {
        if (null != m_callback)
        {
            m_callback.onFrame(this, this);
        }
        return this;
    }

    @Override
    public IAnimation doClose()
    {
        if (null != m_callback)
        {
            m_callback.onClose(this, this);
        }
        return this;
    }
}

@@ -731,16 +731,67 @@
 ation;%0A%0A
+import com.emitrom.lienzo.client.core.shape.Layer;%0A
 import c
@@ -832,16 +832,16 @@
 e.Node;%0A
-
 import c
@@ -968,16 +968,64 @@
 llback;%0A
+import com.google.gwt.dom.client.CanvasElement;%0A
 import c
@@ -1810,24 +1810,83 @@
 eMillis();%0A%0A
+        final CanvasElement element = getCanvasElement();%0A%0A
         fina
@@ -2101,32 +2101,255 @@
               %7B%0A
+                    if (null != element)%0A                    %7B%0A                        AnimationScheduler.get().requestAnimationFrame(this, element);%0A                    %7D%0A                    else%0A                    %7B%0A    
                 
@@ -2402,24 +2402,46 @@
 rame(this);%0A
+                    %7D%0A
             
@@ -2744,24 +2744,250 @@
 doStart();%0A%0A
+                    if (null != element)%0A                    %7B%0A                        AnimationScheduler.get().requestAnimationFrame(animate, element);%0A                    %7D%0A                    else%0A                    %7B%0A    
             
@@ -3051,16 +3051,38 @@
 imate);%0A
+                    %7D%0A
         
@@ -3520,32 +3520,32 @@
 getNode()%0A    %7B%0A
-
         return m
@@ -3550,32 +3550,350 @@
  m_node;%0A    %7D%0A%0A
+    private final CanvasElement getCanvasElement()%0A    %7B%0A        Node%3C?%3E node = getNode();%0A%0A        if (null != node)%0A        %7B%0A            Layer layer = node.getLayer();%0A%0A            if (null != layer)%0A            %7B%0A                return layer.getCanvasElement();%0A            %7D%0A        %7D%0A        return null;%0A    %7D%0A%0A
     @Override%0A  

use requestAnimationFrame with canvas element if available