/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package csa.tiletools.sprites;

import csa.Global;
import csa.gui.Scaler;
import csa.tiletools.graphics.BaseImageData;
import csa.tiletools.graphics.ImageSequenceData;
import csa.tiletools.graphics.ImageSequenceDataPool;
import csa.tiletools.tiles.Paintable;
import java.awt.Dimension;
import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

/**
 *
 * @author salchr
 */
public class Sprite extends Paintable 
{
    public int spriteInLayer=-1;

    double fractionX = 0;
    double fractionY = 0;
    double _scaleX = 0;
    double _scaleY = 0;
    boolean automaticMovment = false; // movement can be disabled "on the fly" due to usage as mousecursor!
    
    
    Vector<String> supportedActions = new Vector<String>(); // Strings like RUN, STAND, WALK...
    public Vector<String> getActions()
    {
        return (Vector<String>) supportedActions.clone();
    }
    public Vector<String> getDirections(String action)
    {
        Vector<String> directions;
        
        directions = directionMap.get(action);
        if (directions == null) return new Vector<String>();
        return directions;
    }

    // action -> subsequent action map
    HashMap<String, String> subsequentActionMap = new HashMap<String, String>(); 
    HashMap<String, Boolean> actionLoopMap = new HashMap<String, Boolean>(); 
    
    // for each entry with actual data add one entry to hashmap
    
    // map each action to a vector of supported directions for this action
    HashMap<String, Vector<String>> directionMap = new HashMap<String, Vector<String>>(); 

    // action and direction (combined via "_") Map to actual Image Data
    HashMap<String, ImageSequenceData> imageMap = new HashMap<String, ImageSequenceData>(); 
    HashMap<String, Vector <BaseImageData>> baseimageMap = new HashMap<String, Vector <BaseImageData>>(); 
    
    
    HashMap<String, String> imagepoolFilenameMap = new HashMap<String, String>(); 
    HashMap<String, String> imageNameMap = new HashMap<String, String>(); 

    // action and direction and number (combined via "_") Map to movement offsets
    HashMap<String, Dimension> movementMap = new HashMap<String, Dimension>(); 
    
    
    public SpriteData data = null;
    private static SpriteDataPool mSpriteDataPool = null;
    
    String active_action = "Stand";
    String active_direction = "n";
    
    public static Sprite buildSprite(SpriteData d)
    {
        return new Sprite(d);
    }

    // doesnt clone "autoMoveEnabled"
    public static Sprite cloneSprite(Sprite s)
    {
        return new Sprite(s);
    }
    public static Sprite buildSprite(String fileName, String spriteName)
    {
        String newFileName = "Sprite"+File.separator+fileName+".xml";
        boolean loadPool = false;
        if (mSpriteDataPool == null) 
        {
            loadPool = true;
        }
        else if (! mSpriteDataPool.getFilename().equals(newFileName))
        {
            loadPool = true;
        }
        if (loadPool)
            mSpriteDataPool = new SpriteDataPool(newFileName);
    
        return new Sprite(mSpriteDataPool.get(spriteName));
    }
    
    private Sprite(SpriteData d)
    {
        setData(d);
    }

    // doesnt clone "autoMoveEnabled"
    private Sprite(Sprite s)
    {
        setData(cloneData(s.data));
        
        // and clone from paintable!
        setPaintableData(s);
        
        active_action = s.active_action;
        active_direction = s.active_direction;
        
        init();
        
    }

    
    
    private SpriteData cloneData(SpriteData d)
    {
        SpriteData data = new SpriteData();
        
	data.mClass=d.mClass;
	data.mName=d.mName;
	data.mWidth=d.mWidth;
	data.mHeight=d.mHeight;
	data.mType=d.mType;
	data.mID=d.mID;
	data.mDescription=d.mDescription;
	data.mForceSpriteSize=d.mForceSpriteSize;
	data.mStepsAbsolut=d.mStepsAbsolut;
	data.mPoolName=d.mPoolName;
	data.mBackShadow=d.mBackShadow;

        data.mBackThickness=d.mBackThickness;
        data.mBackAntialiazing=d.mBackAntialiazing;
	data.mBack_R=d.mBack_R;
	data.mBack_G=d.mBack_G;
	data.mBack_B=d.mBack_B;

        
        
        
	data.mAutomaticMovment=d.mAutomaticMovment;
	data.mUpdateWhenNotInViewport=d.mUpdateWhenNotInViewport;
        
        data.mAtrributeName=(Vector<String>)d.mAtrributeName.clone();
	data.mAttributeType=(Vector<String>)d.mAttributeType.clone();
	data.mAttributeValue=(Vector<String>)d.mAttributeValue.clone();
	data.mActionString=(Vector<String>)d.mActionString.clone();
	data.mImageSetName=(Vector<String>)d.mImageSetName.clone();
	data.mActionDirectionImageName=(Vector<String>)d.mActionDirectionImageName.clone();
	data.mSubsequentAction=(Vector<String>)d.mSubsequentAction.clone();
        data.mActionDirectionAnimationStepDeltaX=(Vector<Integer>)d.mActionDirectionAnimationStepDeltaX.clone();
	data.mActionDirectionAnimationStepDeltaY=(Vector<Integer>)d.mActionDirectionAnimationStepDeltaY.clone();
	data.mLoopAction=(Vector<Boolean>)d.mLoopAction.clone();
    
        
        
        
        return data;
    }
    
    private void setData(SpriteData d)
    {
        _scaleX = 0;
        _scaleY = 0;
        data = d;
        automaticMovment = d.mAutomaticMovment;
        
        String lastPoolName = "";
        ImageSequenceDataPool imagePool = null;
        int movementCounter = 0;
	for (int i=0; i<data.mActionString.size(); i++)
        {
            String action = data.mActionString.elementAt(i);
            if (!supportedActions.contains(action))
            {
                supportedActions.addElement(action);
            }
            
            String subsequentAction = "";
            if (data.mSubsequentAction.size()>i)
                subsequentAction = data.mSubsequentAction.elementAt(i);
            Boolean loopAction = false;
            if (data.mLoopAction.size()>i)
                loopAction = data.mLoopAction.elementAt(i);
            subsequentActionMap.put(action, subsequentAction);
            actionLoopMap.put(action, loopAction);
        }

	for (int i=0; i<data.mActionDirectionImageName.size(); i++)
        {
            String imageActionDirection = data.mActionDirectionImageName.elementAt(i);
            String action = getActionFromImageActionDirection(imageActionDirection);
            String direction = getDirectionFromImageActionDirection(imageActionDirection);
            
            
            if (!supportedActions.contains(action))
            {
                supportedActions.addElement(action);
            }
            Vector<String> directions = directionMap.get(action);
            if (directions == null)
                directions = new Vector<String>();
            if (!directions.contains(direction))
            {
                directions.addElement(direction);
            }
            directionMap.put(action, directions);

            String imagePoolFilename = data.mImageSetName.elementAt(i); // sizes of vectors MUST match!
            String imageName = getImageFromImageActionDirection(imageActionDirection);
            
            if ((!lastPoolName.equals(imagePoolFilename)) || (imagePool == null))
            {
                String imageFilename = "Graphics"+File.separator+imagePoolFilename+".xml";
                imagePool = new ImageSequenceDataPool(imageFilename);
                if (imagePool == null) 
                {
                    lastPoolName = "";
                    continue;
                }
                lastPoolName = imagePoolFilename;
                
                
                if (active_action.length()==0)
                {
                    active_action = action;
                    active_direction = direction;
                }
            }
            // pool should be initialized
            ImageSequenceData sequenceData = imagePool.get(imageName);
            imageMap.put(action+"_"+direction, sequenceData); 
            imagepoolFilenameMap.put(action+"_"+direction, imagePoolFilename); 
            imageNameMap.put(action+"_"+direction, imageName); 
                    
            // movement offsets
            int count = sequenceData.getHeight().size();
            for (int c=0; c<count; c++)
            {
                if (d.mActionDirectionAnimationStepDeltaX.size()<=movementCounter)
                {
                    continue;
                }
                int x = d.mActionDirectionAnimationStepDeltaX.elementAt(movementCounter);
                int y = d.mActionDirectionAnimationStepDeltaY.elementAt(movementCounter);
                movementCounter++;
                if ((x==0) && (y==0)) continue;
                setMovement(action, direction, c, x, y);
            }
        }
    }
        
    public void setMovement(String action, String direction, int i, int x, int y)
    {
        movementMap.put(action+"_"+direction+"_"+i, new Dimension(x,y));
    }
    public Dimension getMovement(String action, String direction, int i)
    {
        Dimension dim =  movementMap.get(action+"_"+direction+"_"+i);
        if (dim == null) return new Dimension(0,0);
        return dim;
    }

    public ImageSequenceData getImageSequenceData(String action, String direction)
    {
        return imageMap.get(action+"_"+direction); 
    }
    // null if not available
    public String getImagePoolname(String action, String direction)
    {
        return imagepoolFilenameMap.get(action+"_"+direction); 
    }
    // null if not available
    public String getImagename(String action, String direction)
    {
        return imageNameMap.get(action+"_"+direction); 
    }
    
    // NAME_ACTION_DIRECTION
    private static String getActionFromImageActionDirection(String ad)
    {
        if (ad == null) return null;
        if (ad.length() == 0) return null;
        
        int index = ad.lastIndexOf("_");
        if (index == -1) return null;
        String sub = ad.substring(0, index);
        
        index = sub.lastIndexOf("_");
        if (index == -1) return null;
        
        String a = sub.substring(index+1);
        return a;
    }

    private static String getDirectionFromImageActionDirection(String ad)
    {
        if (ad == null) return null;
        if (ad.length() == 0) return null;
        
        int index = ad.lastIndexOf("_")+1;
        if (index <= 0) return null;
        
        String d = ad.substring(index);
        return d;
    }
    
    public static String getImageFromImageActionDirection(String ad)
    {
        String d = getDirectionFromImageActionDirection(ad);
        String a = getActionFromImageActionDirection(ad);
        if (d==null) return null;
        if (a==null) return null;
        
        String i = csa.util.UtilityString.replaceLastOccurrence(ad, "_"+a+"_"+d, "");
        return i;
    }

    public void addReplaceImage(String action, String direction, String imagePoolFilename, ImageSequenceData iData)
    {
        if (!supportedActions.contains(action))
        {
            supportedActions.addElement(action);
            subsequentActionMap.put(action, "");
            actionLoopMap.put(action, false);
        }
        Vector<String> directions = directionMap.get(action);
        if (directions == null)
            directions = new Vector<String>();
        if (!directions.contains(direction))
        {
            directions.addElement(direction);
        }
        directionMap.put(action, directions);

        // ATTENTION!
        // actual data, not a clone copy... changes can be made from outside!
        imageMap.put(action+"_"+direction, iData); 
        imageNameMap.put(action+"_"+direction, iData.getName()); 
        imagepoolFilenameMap.put(action+"_"+direction, imagePoolFilename); 
        _scaleX = 0;
        _scaleY = 0;
    }
    // returns true if image was the last of the action
    public boolean removeImage(String action, String direction)
    {
        Vector<String> directions = directionMap.get(action);
        directions.remove(direction);
        imageMap.remove(action+"_"+direction); 
        imageNameMap.remove(action+"_"+direction); 
        imagepoolFilenameMap.remove(action+"_"+direction); 
        
        
        
        Set entries = movementMap.entrySet();
        Iterator it = entries.iterator();
        Vector<String> toRemove = new Vector<String>();
        while (it.hasNext())
        {
            Map.Entry entry = (Map.Entry) it.next();
            Dimension value = (Dimension) entry.getValue();
            String key = (String) entry.getKey();
            if (key.startsWith(action+"_"+direction+"_"))
                toRemove.addElement(key);
        }
        for (int i = 0; i < toRemove.size(); i++) {
            String key = toRemove.elementAt(i);
            movementMap.remove(key);
        }
        return directions.size()==0;
    }
    
    public void removeAction(String action)
    {
        supportedActions.removeElement(action);
        subsequentActionMap.remove(action);
        actionLoopMap.remove(action);
        directionMap.remove(action);
    }
    
    // puts the current image information to SpriteData
    // reverse of constructor init
    public void toData()
    {
	data.mActionString=new Vector<String>();
	data.mImageSetName=new Vector<String>();
	data.mActionDirectionImageName=new Vector<String>();
        data.mSubsequentAction = new Vector<String>();
        data.mLoopAction = new Vector<Boolean>();
        data.mActionDirectionAnimationStepDeltaX = new Vector<Integer>();
        data.mActionDirectionAnimationStepDeltaY = new Vector<Integer>();

        for (int i=0; i< supportedActions.size(); i++)
        {
            String action = supportedActions.elementAt(i);
            String subsequentAction = getSubsequentAction(action);
            if (subsequentAction == null) 
                subsequentAction = "";
            
            
            data.mSubsequentAction.addElement(subsequentAction);
            boolean isLoop = isActionLoop(action);
            data.mLoopAction.addElement(isLoop);
        }

        Set entries = directionMap.entrySet();
        Iterator it = entries.iterator();
        while (it.hasNext())
        {
            Map.Entry entry = (Map.Entry) it.next();
            Vector<String> directions = (Vector<String>) entry.getValue();
            String action = (String) entry.getKey();
            if (!data.mActionString.contains(action))
            {
                data.mActionString.addElement(action);
            }
            for (int i=0; i<directions.size(); i++)
            {
                String direction = directions.elementAt(i);
                ImageSequenceData iData = imageMap.get(action+"_"+direction);
                data.mImageSetName.addElement(imagepoolFilenameMap.get(action+"_"+direction));
                data.mActionDirectionImageName.addElement(iData.getName()+"_"+action+"_"+direction);

                // movement stuff
                int count = iData.getHeight().size();
                for (int c=0; c<count; c++)
                {
                    Dimension dim = getMovement(action, direction, c);
                    data.mActionDirectionAnimationStepDeltaX.addElement(dim.width);
                    data.mActionDirectionAnimationStepDeltaY.addElement(dim.height);
                }
            }
        }
    }
    
    
    
    // returns em pty string if not found!
    public String getSubsequentAction(String action)
    {
        String sa = subsequentActionMap.get(action);
        if (sa != null) return sa;
        return "";
    }
    public void setSubsequentAction(String action, String subsequenctAction)
    {
        if (subsequenctAction == null) 
        {
            subsequenctAction = "";
        }
        else
            subsequentActionMap.put(action, subsequenctAction);
    }
    // false if not existent!
    public boolean isActionLoop(String action)
    {
        Boolean b = actionLoopMap.get(action);
        if (b != null) return b;
        return false;
    }
    public void setActionLoop(String action, Boolean loop)
    {
        actionLoopMap.put(action, loop);
    }

    public void setActiveAction(String action)
    {
        if ((action!= null) && (action.trim().length() != 0))
            active_action = action;
        initAction();
    }
    public String getActiveAction()
    {
        return active_action;
    }
    public String getActiveDirection()
    {
        return active_direction;
    }
    
    public String getActiveCoordinator()
    {
        return "none - TODO";
    }
    
    public void setActiveDirection(String direction)
    {
        if ((direction!= null) && (direction.trim().length() != 0))
            active_direction = direction;
        initAction();
    }
    public void setActiveActionDirection(String action, String direction)
    {
        if ((action!= null) && (action.trim().length() != 0))
            active_action = action;
        if ((direction!= null) && (direction.trim().length() != 0))
            active_direction = direction;
        initAction();
    }

    protected void init()
    {
        _scaleX = 0;
        _scaleY = 0;
        initAction();
    }    
    
    private void resetSpriteImageCache()
    {
        baseimageMap.clear();
    }
    public boolean setScalePercent(int s)
    {
        resetSpriteImageCache();
        return super.setScalePercent(s);
    }
    
    private void initAction()
    {
        stop();

        _scaleX = 0;
        _scaleY = 0;
        ImageSequenceData mData = imageMap.get(active_action+"_"+active_direction);
        if (mData==null) return;

        delay = mData.getDelay();

        if (data.mBackShadow)
        {
            BaseImageData._thickness = data.mBackThickness;
            BaseImageData._antialiaseCount = data.mBackAntialiazing;
            BaseImageData._shadow_r = data.mBack_R;
            BaseImageData._shadow_g = data.mBack_G;
            BaseImageData._shadow_b = data.mBack_B;
        }
        
        
        String key;
        if (data.mForceSpriteSize)
        {
            key = active_action+"_"+active_direction+"_"+data.mWidth+"_"+data.mHeight+"_"+data.mBackShadow;
        }
        else
        {
            key = active_action+"_"+active_direction+"_"+scalePercent+"_"+data.mBackShadow;
        }
        
        // System.out.println("Sprite Key: "+key);
        mBData = baseimageMap.get(key);
        
        if (mBData==null)
        {
            if (data.mForceSpriteSize)
            {
                mBData = BaseImageData.toBasePercent(mData, scalePercent, data.mWidth, data.mHeight, data.mBackShadow);
            }
            else
            {
                mBData = BaseImageData.toBasePercent(mData, scalePercent, data.mBackShadow);
            }
            baseimageMap.put(key, mBData); // if it is zero -> doesnt distrub anything
        }
        if (mBData==null) return;

        maxWS=0;
        maxHS=0;
        minWidthScaled=Integer.MAX_VALUE;
        minHeightScaled=Integer.MAX_VALUE;
        
        for (int i = 0; i < mBData.size(); i++)
        {
            BaseImageData baseImageData = mBData.elementAt(i);
            if (baseImageData==null) continue;
            if (baseImageData.image==null) continue;
            
            if (maxWS==0)
                maxWS = baseImageData.image.getWidth(null);
            if (maxHS==0)
                maxHS = baseImageData.image.getHeight(null);
            if (maxWS<baseImageData.image.getWidth(null)+baseImageData.ox)
                maxWS = baseImageData.image.getWidth(null)+baseImageData.ox;
            if (maxHS<baseImageData.image.getHeight(null)+baseImageData.oy)
                maxHS = baseImageData.image.getHeight(null)+baseImageData.oy;

            if (minWidthScaled>baseImageData.image.getWidth(null)+baseImageData.ox) minWidthScaled = baseImageData.image.getWidth(null)+baseImageData.ox;
            if (minHeightScaled>baseImageData.image.getHeight(null)+baseImageData.oy) minHeightScaled = baseImageData.image.getHeight(null)+baseImageData.oy;
        }
        if (scalePercent == 100)
        {
            maxHSReal = maxHS;
            minHeightScaledReal = maxHS;
            maxWO=maxWS;
            maxHO=maxHS;
            minWidthUnscaled=minWidthScaled;
            minHeightUnscaled=minHeightScaled;
        }
        else
        {
            if (minWidthUnscaled==Integer.MAX_VALUE)
            {
                Vector <BaseImageData> oBData;

                if (data.mForceSpriteSize)
                    oBData = BaseImageData.toBase(mData, 1, data.mWidth, data.mHeight, data.mBackShadow);
                else
                    oBData = BaseImageData.toBase(mData, 1, data.mBackShadow);
                    oBData = BaseImageData.toBase(mData, 1);
                if (oBData==null) return;

                minWidthUnscaled=Integer.MAX_VALUE;
                minHeightUnscaled=Integer.MAX_VALUE;
                for (int i = 0; i < oBData.size(); i++)
                {
                    BaseImageData baseImageData = oBData.elementAt(i);

                    if (baseImageData==null) continue;
                    if (baseImageData.image==null) continue;
                    if (minWidthUnscaled==Integer.MAX_VALUE)
                        minWidthUnscaled = baseImageData.image.getWidth(null);
                    if (minHeightUnscaled==Integer.MAX_VALUE)
                        minHeightUnscaled = baseImageData.image.getHeight(null);
                   
                    if (minWidthUnscaled>baseImageData.image.getWidth(null)+baseImageData.ox)
                        minWidthUnscaled = baseImageData.image.getWidth(null)+baseImageData.ox;
                    if (minHeightUnscaled>baseImageData.image.getHeight(null)+baseImageData.oy)
                        minHeightUnscaled = baseImageData.image.getHeight(null)+baseImageData.oy;
                }                
            }
            if (maxWO + maxHO == 0)
            {
                Vector <BaseImageData> oBData;

                if (data.mForceSpriteSize)
                    oBData = BaseImageData.toBase(mData, 1, data.mWidth, data.mHeight);
                else
                    oBData = BaseImageData.toBase(mData, 1);
                if (oBData==null) return;

                maxWO=0;
                maxHO=0;
                for (int i = 0; i < oBData.size(); i++)
                {
                    BaseImageData baseImageData = oBData.elementAt(i);

                    if (maxWO==0)
                        maxWO = baseImageData.image.getWidth(null);
                    if (maxHO==0)
                        maxHO = baseImageData.image.getHeight(null);
                    if (maxWO<baseImageData.image.getWidth(null)+baseImageData.ox)
                        maxWO = baseImageData.image.getWidth(null)+baseImageData.ox;
                    if (maxHO<baseImageData.image.getHeight(null)+baseImageData.oy)
                        maxHO = baseImageData.image.getHeight(null)+baseImageData.oy;
                }
            }
            maxHSReal = maxHS;
            minHeightScaledReal = minHeightScaled;
            maxHOReal = maxHO;
            minHeightUnscaledReal = minHeightUnscaled;
        }
        if (mBData.size()<=1) delay = -1;

        if ((isDisplayed)|| (data.mUpdateWhenNotInViewport))
            if (delay!=-1)
                start();
        
        
        randomAnim = mData.getRandomAnimationStart();
        if (randomAnim)
        {
            animCounter =  Global.getRand().nextInt(mBData.size());
        }
        else
            animCounter =  0;
    }
    
    int inSetting=0;
    // only thismethod fires position changed events!
    private void setPosition(int x, int y)
    {
        boolean fire = ((x!=X) || (y!=Y));
        if (!fire) return;
        inSetting++;
        super.setX(x);
        super.setY(y);
        inSetting--;

        if (fire) firePositionChanged();
    }
    protected void positionYChanged()
    {
        if (inSetting>0) return;
        fractionY=0;
    }
    protected void positionXChanged()
    {
        if (inSetting>0) return;
        fractionX=0;
    }
    
    // returns scaling (in Percent) used for movement Delta
    // scale respects 
    // a) absolut (forced) size of sprite
    // b) current scale factor (of map)
    private double getCurrentScalingX()
    {
        if (data.mForceSpriteSize)
        {
            if (_scaleX == 0)
            {
                initCurrentXYScales();
            }
            return _scaleX;
        }
        return scalePercent;
    }

    private void initCurrentXYScales()
    {
        ImageSequenceData mData = imageMap.get(active_action+"_"+active_direction);
        Dimension orgImageSize = BaseImageData.getUnscaledMaxDimensions(mData);

        // float scaleX = Scaler.unscaleFloatToFloat(data.mWidth,((float)(orgImageSize.width)) );
        // float scaleY = Scaler.unscaleFloatToFloat(data.mHeight,((float)(orgImageSize.height)) );

        // scaling to 100%
        double orgFactorX = Scaler.getScaleFactor(orgImageSize.width, data.mWidth);
        double orgFactorY = Scaler.getScaleFactor(orgImageSize.height, data.mHeight);
        
        // downscale with given scaling!
        double scaleX = Scaler.scalePercentToDouble(orgFactorX, scalePercent);
        double scaleY = Scaler.scalePercentToDouble(orgFactorY, scalePercent);

        _scaleX = scaleX*100;
        _scaleY = scaleY*100;        
    }
    
    
    // returns scaling (in Percent) used for movement Delta
    // scale respects 
    // a) absolut (forced) size of sprite
    // b) current scale factor (of map)
    private double getCurrentScalingY()
    {
        if (data.mForceSpriteSize)
        {
            if (_scaleY == 0)
            {
                initCurrentXYScales();
            }
            return _scaleY;
        }
        return scalePercent;
    }
    @Override
    public void fireAnimationStateChanged()
    {
        super.fireAnimationStateChanged();
        
        
        if ((animCounter == 0) && mBData.size()>1)
        {
            animationCounterReset();
        }
        if (!data.mAutomaticMovment) return;
        if (!automaticMovment) return;
        
        Dimension delta = getMovement(active_action, active_direction, animCounter);
        // setX(X+delta.width);
        // setY(Y+delta.height);
        
        if (!data.mStepsAbsolut)
        {
            // scale is in percent!
            double scaleX = getCurrentScalingX();
            double scaleY = getCurrentScalingY();
            
            double newFractionX = Scaler.scalePercentToDouble(delta.width, scaleX);
            double newFractionY = Scaler.scalePercentToDouble(delta.height, scaleY);

            fractionX += newFractionX;
            fractionY += newFractionY;

            int deltaX = (int) Math.round(fractionX);
            int deltaY = (int) Math.round(fractionY);
            fractionX -= deltaX;
            fractionY -= deltaY;
            setPosition(X+deltaX, Y+deltaY);
            
        }
        else
        {
            setPosition(X+delta.width, Y+delta.height);
        }
        
  //      not working yet!
        if (!isInPaintList)
        {
            if (panel != null) // can be zero right in the initialization of a sprite!
            {
                if (data.mUpdateWhenNotInViewport)
                    panel.updateNeeded(this);
            }
        }
    }
    
    
    protected Vector<PositionListener> mPositionListener= null;
    public void addPositionListener(PositionListener listener)
    {
        removePositionListener(listener);
        if (mPositionListener==null) mPositionListener= new Vector<PositionListener>();
        mPositionListener.addElement(listener);
    }
    public void removePositionListener(PositionListener  listener)
    {
        if (mPositionListener==null) return;
        mPositionListener.removeElement(listener);
    }
    public void firePositionChanged()
    {
        if (mPositionListener==null) return;
        for (int i=0; i<mPositionListener.size(); i++)
        {
            mPositionListener.elementAt(i).positionChanged();
        }
    }
    protected Vector<ActionListener> mActionListener= null;
    public void addActionListener(ActionListener listener)
    {
        removeActionListener(listener);
        if (mActionListener==null) mActionListener= new Vector<ActionListener>();
        mActionListener.addElement(listener);
    }
    public void removeActionListener(ActionListener  listener)
    {
        if (mActionListener==null) return;
        mActionListener.removeElement(listener);
    }
    public void fireActionChanged()
    {
        if (mActionListener==null) return;
        for (int i=0; i<mActionListener.size(); i++)
        {
            mActionListener.elementAt(i).actionComplete();
        }
    }

    private void animationCounterReset()
    {
        if (isActionLoop(active_action))
            return;
        String nextAction = getSubsequentAction(active_action);
        if (nextAction.equals(active_action)) return;
        setActiveActionDirection(nextAction, active_direction);
        
        fireActionChanged();
    }
    
    public void setAutoMoveEnabled(boolean b)
    {
        automaticMovment = b;
    }
    public void setDisplayed(boolean displayed)
    {
        if (data.mUpdateWhenNotInViewport) return;
        if (isDisplayed == displayed) return;
        
        super.setDisplayed(displayed);
    }
}
