package com.drawbridge.dm.animation;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import com.drawbridge.dm.DMImageFactory.DMImage;
import com.drawbridge.dm.DMObject;
import com.drawbridge.dm.DMSimpleObject;
import com.drawbridge.jsengine.JsEngine;
import com.drawbridge.jsengine.jsobjects.JSNativeFunction;
import com.drawbridge.text.TextPanel;
import com.drawbridge.utils.AnalyticUtils;
import com.drawbridge.utils.JSUtils;
import com.drawbridge.utils.Utils;
import com.drawbridge.vl.VLPanel;
/**
* Animation object that describes an animation with a given set of objects.
*
* @author Alistair Stead
*
*/
public class AnimationManager
{
private List<DMObject> mAnimationObjects;
private AnimationListener mListener;
private Long mStartTimestamp = null;
public final static int TYPE_PROVISIONAL = 1;
public final static int TYPE_CURRENT = 2;
public enum AnimationState
{
STATE_PLAYING,
STATE_STOPPED,
STATE_RECORDING,
}
private AnimationState mState;
private NativeAnimationItem mPreviousAnimationItem = null;
private String mCurrentAnimationCode = "";
private String mProvisionalCode = "";
public AnimationManager(AnimationListener parent, LinkedList<DMObject> simpleObjects) {
mListener = parent;
mAnimationObjects = simpleObjects;
reset();
mState = AnimationState.STATE_STOPPED;
}
private void addAction(DMObject object, int x, int y, int w, int h, long timeDelay)
{
if (mStartTimestamp == null)
{
mStartTimestamp = System.currentTimeMillis();
}
mCurrentAnimationCode += object.getName() + ".setTweenPosition(" + x + ", " + y + ");\n"
+ object.getName() + ".setTweenSize(" + w + ", " + h + ");\n"
+ "setTimeToDestination(" + Utils.toDecimalPlace((double)timeDelay / 1000, 1) + ");\n\n";
mPreviousAnimationItem = new NativeAnimationItem(object, x, y, w, h, ((double)timeDelay / 1000));
Utils.out.println(this.getClass(), "Add action set text");
if (TextPanel.hasInstance())
{
TextPanel.getInstance().setText(this.getClass(), mCurrentAnimationCode);
TextPanel.getInstance().moveToBottom();
}
else if(VLPanel.hasInstance()){
JsEngine.getInstance().executeCode(null, mCurrentAnimationCode , false);
}
if (VLPanel.hasInstance())
VLPanel.getInstance().moveToBottom();
}
private void setProvisionalAction(DMObject object, int x, int y, int w, int h, long timeDelay)
{
mProvisionalCode = mCurrentAnimationCode;
mProvisionalCode += object.getName() + ".setTweenPosition(" + x + ", " + y + ");\n"
+ object.getName() + ".setTweenSize(" + w + ", " + h + ");\n"
+ "setTimeToDestination(" + Utils.toDecimalPlace((double)timeDelay / 1000, 1) + ");\n\n";
if (TextPanel.hasInstance())
{
TextPanel.getInstance().setText(this.getClass(), mProvisionalCode);
TextPanel.getInstance().moveToBottom();
TextPanel.getInstance().getDocument().updateUIOnChange(true);
}
else if(VLPanel.hasInstance()){
JsEngine.getInstance().executeCode(null, mProvisionalCode , false);
}
if (VLPanel.hasInstance())
VLPanel.getInstance().moveToBottom();
}
public void reset()
{
mStartTimestamp = null;
mPreviousAnimationItem = null;
}
public AnimationState moveToNextState()
{
mState = peekAtNextState();
AnalyticUtils.recordAnimationStateChange(mState);
switch (mState)
{
case STATE_PLAYING:
play();
break;
case STATE_RECORDING:
if(TextPanel.hasInstance())
mCurrentAnimationCode = TextPanel.getInstance().getDocument().getText() + "\n\n";
else if(VLPanel.hasInstance())
mCurrentAnimationCode = VLPanel.getInstance().mCanvas.getModel().generateCodeFromBlocks(VLPanel.getInstance().mCanvas.mViewGrid) + "\n\n";
else
throw new RuntimeException("No language panels available to execute animation");
for (DMObject obj : mAnimationObjects)
{
obj.addMouseListener(mAnimationMA);
obj.addMouseMotionListener(mAnimationMA);
}
reset();
break;
case STATE_STOPPED:
for (DMObject obj : mAnimationObjects)
{
obj.removeMouseListener(mAnimationMA);
obj.removeMouseMotionListener(mAnimationMA);
}
break;
}
mListener.onAnimationStateChange();
return mState;
}
public AnimationState peekAtNextState()
{
AnimationState nextState = mState;
switch (mState)
{
case STATE_PLAYING:
nextState = AnimationState.STATE_STOPPED;
break;
case STATE_STOPPED:
if (mPreviousAnimationItem != null)
nextState = AnimationState.STATE_PLAYING;
else
nextState = AnimationState.STATE_RECORDING;
break;
case STATE_RECORDING:
nextState = AnimationState.STATE_STOPPED;
break;
}
return nextState;
}
public AnimationState getState()
{
return mState;
}
@Override
public String toString()
{
String result = "Animation: \n";
if (mPreviousAnimationItem != null)
result += mPreviousAnimationItem.toString();
return result;
}
// Mouse listener that is bound to each object on record,
// and unbound on stop.
MouseAdapter mAnimationMA = new MouseAdapter() {
private boolean dragged = false;
@Override
public void mousePressed(MouseEvent me)
{
dragged = false;
Utils.out.println(this.getClass(), "MouseListener Animation MousePressed");
if (mStartTimestamp == null)
{
mStartTimestamp = System.currentTimeMillis();
}
}
@Override
public void mouseDragged(MouseEvent me)
{
dragged = true;
if (getState() == AnimationState.STATE_RECORDING)
{
long currentTime = System.currentTimeMillis();
long timeDelay = currentTime - mStartTimestamp;
DMObject source = (DMObject) me.getSource();
if (source instanceof DMImage)
setProvisionalAction(source, source.getX(), source.getY(), source.getWidth(), source.getHeight(), timeDelay);
else
{
DMSimpleObject dmso = (DMSimpleObject) source;
setProvisionalAction(source, dmso.getCounterpart().getX(), dmso.getCounterpart().getY(), dmso.getCounterpart().getWidth(), dmso.getCounterpart().getHeight(), timeDelay);
}
}
}
@Override
public void mouseReleased(MouseEvent me)
{
if (getState() == AnimationState.STATE_RECORDING && dragged)
{
long currentTime = System.currentTimeMillis();
long timeDelay = currentTime - mStartTimestamp;
mStartTimestamp = currentTime;
DMObject source = (DMObject) me.getSource();
if (source instanceof DMImage)
addAction(source, source.getX(), source.getY(), source.getWidth(), source.getHeight(), timeDelay);
else
{
DMSimpleObject dmso = (DMSimpleObject) source;
addAction(source, dmso.getCounterpart().getX(), dmso.getCounterpart().getY(), dmso.getCounterpart().getWidth(), dmso.getCounterpart().getHeight(), timeDelay);
}
}
dragged = false;
}
};
private void play()
{
// Reset object locations & execute the code here
Iterator<DMImage> it = getImages().iterator();
while (it.hasNext())
{
DMObject obj = it.next();
obj.setLocation(obj.getPreferredLocation());
obj.setSize(obj.getPreferredSize());
}
// Reset all the animation tracking variables
JSNativeFunction.animationItems = new LinkedList<NativeAnimationItem>();
if (JSNativeFunction.processor.isAlive())
JSNativeFunction.processor.stopAnimation();
JSNativeFunction.processor = new NativeAnimationProcessor();
JSNativeFunction.tweensCalled = 0;
JSNativeFunction.tweensProcessed = -1;
Utils.out.println(this.getClass(), "Executing/Playing from Play() in AnimationCodeGenerator.java");
if(TextPanel.hasInstance())
JsEngine.getInstance().executeCode(TextPanel.getInstance(), TextPanel.getInstance().getDocument().getText(), true);
else if(VLPanel.hasInstance())
JsEngine.getInstance().executeCode(VLPanel.getInstance(), VLPanel.getInstance().mCanvas.getModel().generateCodeFromBlocks(VLPanel.getInstance().mCanvas.mViewGrid), true);
else
throw new RuntimeException("Could not execute animation without code representation!");
}
public interface AnimationListener
{
public void onAnimationStateChange();
}
// This method is dangerous
public List<DMImage> getImages()
{
LinkedList<DMImage> images = new LinkedList<DMImage>();
for (DMObject obj : mAnimationObjects)
{
if (obj instanceof DMSimpleObject)
images.add(((DMSimpleObject) obj).getCounterpart());
}
return images;
}
/**
* Generates code based on a given animation sequence.
*
* @param mAnimation
* @return
*/
public String generateAnimationCode(int type)
{
String boilerplateCode = JSUtils.generateCanvasJS();
String result = boilerplateCode + "\n" + JSUtils.generateDMImageJS(getImages()) + "\n\n// Do Animation\n";
if (type == TYPE_CURRENT)
result += mCurrentAnimationCode;
else
result += mProvisionalCode;
return result;
}
public String getRawCode()
{
return mCurrentAnimationCode;
}
}