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

import csa.gui.Scaler;
import csa.gui.TimingTriggerer;
import csa.gui.TriggerCallback;
import csa.tiletools.graphics.BaseImageData;
import csa.tiletools.graphics.ImageSequenceDataPool;
import csa.tiletools.map.MapPanel;
import csa.tiletools.tiles.AnimationListener;

import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.Vector;

/**
 *
 * @author salchr
 */
public abstract class Paintable {
    static HashMap <String, ImageSequenceDataPool> POOLMAP;
    static
    {
        POOLMAP = new HashMap <String, ImageSequenceDataPool>();
    }

    public int layer=-1;
    //private ArrayList<BufferedImage> images;
    protected Vector <BaseImageData> mBData;


    protected boolean randomAnim = false;
    protected boolean started = false;
    protected int delay = -1;
    
    protected MapPanel panel = null;
    protected boolean isDisplayed = false;

    
    // volatile data, which is set by painter (buildPaintList)
    //public int layer=-1; // in what layer is this tile
    //public int tileInLayer=-1; // whats this tile number in the current layer

    protected int animCounter = 0;
    protected int scalePercent = 100;
    protected int X=0;
    protected int Y=0;
    protected int XScaled=0;
    protected int YScaled=0;
    protected int maxWS = 0;
    protected int maxHS = 0;
    protected int maxWO = 0;
    protected int maxHO = 0;

    // non ISO
    protected int maxHSReal = 0;
    protected int maxHOReal = 0;
    protected int minHeightScaledReal=Integer.MAX_VALUE;
    protected int minHeightUnscaledReal=Integer.MAX_VALUE;
    
    
    protected int minWidthScaled=Integer.MAX_VALUE;
    protected int minHeightScaled=Integer.MAX_VALUE;
    protected int minWidthUnscaled=Integer.MAX_VALUE;
    protected int minHeightUnscaled=Integer.MAX_VALUE;

    protected Vector<AnimationListener> mAnimationListener= null;

    protected boolean isInPaintList = false;
    
    public void setInPaintList(boolean p) {isInPaintList = p;}
    public boolean isInPaintList() {return isInPaintList;}

    
    // for now only X, y
    protected void setPaintableData(Paintable p)
    {
        X = p.X;
        Y = p.Y;
        XScaled = p.XScaled;
        YScaled = p.YScaled;
    }
    
    // is allways scaled correctly, becaus the image is scaled when set scale is called
    public final int getMaxHeight()
    {
        return maxHS;
    }
    public final int getMaxWidth()
    {
        return maxWS;
    }
    // if forced is set, than this is forced size
    // which is size in 100%
    public final int getMaxHeightUnscaled()
    {
        return maxHO;
    }
    
    // scale in 0-100
    public final int getMaxHeightDirectScale(int scale)
    {
        return Scaler.scalePercentToInt(maxHO, scale);
    }

    // if forced is set, than this is forced size
    // which is size in 100%
    public final int getMaxWidthUnscaled()
    {
        return maxWO;
    }

    // scale in 0 - 100
    public final int getMaxWidthDirectScale(int scale)
    {
        return Scaler.scalePercentToInt(maxWO, scale);
    }

    // is allways scaled correctly, because the image is scaled when set scale is called
    public int getCurrentOffsetX()
    {
        return mBData.elementAt(animCounter).ox;
    }
    // is allways scaled correctly, becaus the image is scaled when set scale is called
    public int getCurrentOffsetY()
    {
        return mBData.elementAt(animCounter).oy;
    }

    // unscaled!
    public final int getX() {
        return X;
    }
    
    // scale in 0 - 100
    public final int getXDirectScale(int scale)
    {
        return Scaler.scalePercentToInt(X, scale);
    }

    public final int getXScaled() {
        return XScaled;
    }

    public void setX(int X) {
        this.X = X;
        correctCoordinates();
        positionXChanged();
    }
    // unscaled!
    public final int getY() {
        return Y;
    }
    // scale in 0 - 100
    public final int getYDirectScale(int scale)
    {
        return Scaler.scalePercentToInt(Y, scale);
    }
    public final int getYScaled() {
        return YScaled;
    }

    public void setY(int Y) {
        this.Y = Y;
        correctCoordinates();
        positionYChanged();
    }
    
    // used by sprite to reset position fractions!
    protected void positionYChanged()
    {
    }
    protected void positionXChanged()
    {
    }
    
    protected void correctCoordinates()
    {
        XScaled = Scaler.scalePercentToInt(X, scalePercent);
        YScaled = Scaler.scalePercentToInt(Y, scalePercent);
    }    

    public void addAnimationListener(AnimationListener listener)
    {
        removeAnimationListener(listener);
        if (mAnimationListener==null) mAnimationListener= new Vector<AnimationListener>();
        mAnimationListener.addElement(listener);
    }
    public void removeAnimationListener(AnimationListener  listener)
    {
        if (mAnimationListener==null) return;
        mAnimationListener.removeElement(listener);
    }
    public void fireAnimationStateChanged()
    {
        if (mAnimationListener==null) return;
        for (int i=0; i<mAnimationListener.size(); i++)
        {
            mAnimationListener.elementAt(i).animationStateChanged();
        }
    }

    public void setDisplay(MapPanel panel)
    {
        this.panel = panel;
    }

    protected static ImageSequenceDataPool getPool(String name)
    {
        ImageSequenceDataPool pool=POOLMAP.get(name);
        if (pool == null)
        {
            pool = new ImageSequenceDataPool(name);
            POOLMAP.put(name, pool);
        }
        return pool;
    }
    
    public boolean setScalePercent(int s)
    {
        if (scalePercent == s) return false;
        MapPanel p = panel;
        deinit();
        panel = p;
        scalePercent = s;
        init();
        correctCoordinates();
        return true;
    }
    public int getScalePercent()
    {
        return (int)scalePercent;
    }
 
    public int getPaintX() {
        return X+mBData.elementAt(animCounter).ox;
    }
    public int getPaintXScaled() {
        // image data is scaled in image, not here!
        //return XScaled+((int)(mBData.elementAt(animCounter).ox*scalePercent)/100);
        
        return XScaled+((int)(mBData.elementAt(animCounter).ox));
    }
    public int getPaintY() {
        return Y+mBData.elementAt(animCounter).oy;
    }
    
    public int getPaintYScaled() {
        // image data is scaled in image, not here!
        return YScaled+((int)(mBData.elementAt(animCounter).oy));
    }    
    public BufferedImage getImage()
    {
        return mBData.elementAt(animCounter).image;
    }
    
    TriggerCallback tt;
    public void start()
    {
        if (started) return;
        started = true;
        if (delay==-1) return;
        final TimingTriggerer timer = TimingTriggerer.getTimer();
        timer.setResolution(100); //100*1ms resoltuion is 0,1seconds
        tt = new TriggerCallback()
        {
            @Override
            public void doIt(int state, Object o)
            {
                synchronized (Paintable.this)
                {
                    if (mBData!=null)
                        animCounter = (animCounter+1) % (mBData.size());
                    if (started)
                        timer.addTrigger(tt, delay, 0, null);
                    if (isInPaintList)
                        if (panel != null)
                            panel.updateNeeded(true);
                        else
                            panel = panel;
                    fireAnimationStateChanged();
                }
            }
        };
        timer.addTrigger(tt, delay, 0, null);
    }

    public void stop()
    {
        if (!started) return;
        started = false;
        if (delay==-1) return;
        final TimingTriggerer timer = TimingTriggerer.getTimer();
        timer.removeTrigger(tt);
    }    
    
    public void setDisplayed(boolean displayed)
    {
        if (isDisplayed == displayed) return;
        isDisplayed = displayed;
        if (!isDisplayed)
        {
            // remove timing Trigger
            stop();
        }
        else
        {
            // add timing Trigger
            start();
        }
    }

    // scaled
    public int getMinWidth()
    {
        return minWidthScaled;
        
    }
    public int getMinHeight()
    {
        return minHeightScaled;
    }
    public int getMinWidthUnscaled()
    {
        return minWidthUnscaled;
        
    }
    public int getMinHeightUnscaled()
    {
        return minHeightUnscaled;
    }
    public void deinit()
    {
        panel = null;
        stop();
    }
    
    abstract protected void init(); // should issue a start()
}
