/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * MapPanel.java
 *
 * Created on 02.07.2012, 09:14:18
 */
package csa.tiletools.map;

import csa.tiletools.tiles.Paintable;
import bsh.util.Util;
import csa.Global;
import csa.gui.CSAMainFrame;
import csa.gui.ImageCache;
import csa.gui.Scaler;
import csa.gui.TimingTriggerer;
import csa.gui.TriggerCallback;
import csa.gui.Windowable;
import csa.gui.components.CSAView;
import csa.gui.dialogs.ShowInfoDialog;
import csa.tiletools.graphics.ClickListener;
import csa.tiletools.graphics.MovedListener;
import csa.tiletools.graphics.SingleImagePanel;
import csa.tiletools.sprites.Sprite;
import csa.tiletools.tiles.AnimationListener;
import csa.tiletools.tiles.CompositeTile;
import csa.tiletools.tiles.EmptyTile;
import csa.tiletools.tiles.ShallowTile;
import csa.tiletools.tiles.Tile;
import csa.tiletools.tiles.TileData;
import csa.tiletools.tiles.TileDataPool;
import csa.util.KeyboardListener;
import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import javax.swing.JLabel;
import javax.swing.JPanel;

/**
 *
 * @author salchr
 * Thought:
 *
 * This panel displays a map - or rather displays the viewport to a map.
 * The viewport has the size of this panel, this panel actually IS the viewport to a map.
 *
 * Scaling is done to the MAP, not to the viewport.
 * If the map is scaled to a different size, than the viewport is more or less unaware of it.
 * The viewport in case of scaling is a viewport to a larger map (if scaling was upscaling).
 *
 */
public class MapPanel extends javax.swing.JPanel implements AnimationListener
{
    public static int FIGURE_DOTS = 0;
    public static int FIGURE_SQUARE = 1;
    public static int FIGURE_HEX = 2;

    public static int FIGURE_ISO = 3;
    public static int FIGURE_HOT_SPOT = 10;

    boolean mInSpriteEditMode = false;

    int delay = 10;
    TriggerCallback tt;
    boolean started = false;
    boolean paintMouseGrab = true;
    Tile mMouseGrabTile = null;
    Sprite mMouseGrabSprite = null;
    CompositeTile mMouseGrabCompositeTile = null;
    Tile mShiftMouseGrabTile = null;

    // allways unscaled in map coordinates without offset
    private Point nearestHotSpot = new Point (0,0);
    int mSelectedLayer = 0;

    TMap map = null;


    // ALL COORDINATES are unscaled from the viewport point of view
    // this also means the offset acutually IS
    // scaled, since the viewport thinks a map has a different size when scaled!


    // following values represent the current value
    // current in the sense as to what will be done in a next
    // paintComponent()
    int scale = 100; // in percent 100
    int mouseX=0;
    int mouseY=0;
    int offsetX = 0; // unscaled ! now only used for indication whether
    int offsetY = 0; // offsets changed since last paint, offsets are now kept in layers!
    int visibleWidth = 0;
    int visibleHeight = 0;

    // following are the same values, but from the
    // paintjob done last, so we can discern possible changes!
    int lastPaintScale = -1;
    int lastMouseX = -1;
    int lastMouseY = -1;
    int lastPaintOffsetX = -1;
    int lastPaintOffsetY = -1;
    int lastVisibleWidth = -1;
    int lastVisibleHeight = -1;

    boolean mapChanged = false; // must be drawn new, due to anything
    boolean mapReallyChanged = false; // map / tiles CHANGED -> the image of the map is different! (e.g. Navi must be updated!)
    boolean spriteChanged = false;

    ArrayList<Sprite> retSprites = new ArrayList<Sprite>(); // return Sprite for methods: getSpriteOnPosition() (to keep track!')
    ArrayList<Tile> retTiles = new ArrayList<Tile>(); // return tiles for methods: getTilesOnPosition() (to keep track!')
    boolean updateNeeded = false; // than component will be redrawn!
    int highlightLayer =-1;
    int highlightTile =-1;
    int highlightSprite =-1;

    int selectSprite = -1;
    int selectLayer =-1;
    
    

    boolean hasBackGroundLayer = false;
    Color backColor = Color.BLACK;
    boolean doPaint = false; // if panel is invoked from gui as bean, don´t paint anything!

    int activePaintList = 0;
    ArrayList[] paintList = new ArrayList[2]; // two alternating paintlists, this way I can keep track of the last painted tiles and the next to be painted tiles!
    
    
    private ArrayList<MapListener> mMapListener= new ArrayList<MapListener>();

    // unscaled and and in map coordinates
    public Point getLatestHotSpot()
    {
        return nearestHotSpot;
    }
    public void setScalingPercent(int scalePercent)
    {
        if (scale == scalePercent) return;
        scale = scalePercent;
        updateNeeded(true);
    }
    public boolean isMapChanged() {
        return mapChanged;
    }

    public void setMapChanged(boolean mapChanged, boolean reallyChanged) {
    
        resetPaintList();
        mapReallyChanged =reallyChanged;
        this.mapChanged = mapChanged;
    }
    public void addMapListerner(MapListener listener)
    {
        mMapListener.remove(listener);
        mMapListener.add(listener);
    }
    public void removeMapListerner(MapListener listener)
    {
        mMapListener.remove(listener);
    }
    public void fireOffsetChanged(MapEvent e)
    {
        for (int i=0; i<mMapListener.size(); i++)
        {
            mMapListener.get(i).mapOffsetChanged(e);
        }
    }

    public void fireMousePositionChanged(MapEvent e)
    {
        for (int i=0; i<mMapListener.size(); i++)
        {
            mMapListener.get(i).mousePositionChanged(e);
        }
    }
    public void fireMouseButtonChanged(MapEvent e)
    {
        for (int i=0; i<mMapListener.size(); i++)
        {
            mMapListener.get(i).mouseButtonChanged(e);
        }
    }

    public void deinit()
    {
        if (mMouseGrabTile != null)
            mMouseGrabTile.removeAnimationListener(this);

        map.deinit();
        repainterStop();
    }

    /** Creates new form MapPanel */
    public MapPanel()
    {
        doPaint = false;
        initComponents();
        init2();

    }
    public MapPanel(TMap m)
    {
        doPaint = true;
        map = m;
        initComponents();
        init2();
    }
    boolean mousePressed=false;
    boolean multiTile = false;
    void setMultiTile(boolean m)
    {
        multiTile=m;
    }
    boolean onlyOneTileOnPos = false;
    void setOnlyOneOnPosition(boolean m)
    {
        onlyOneTileOnPos=m;
    }
    
    private void init2()
    {
        paintList[0] = new ArrayList<Paintable>();
        paintList[1] = new ArrayList<Paintable>();
        activePaintList = 0;
        hasBackGroundLayer = false;
        addMouseMotionListener(new java.awt.event.MouseAdapter()
        {
            @Override synchronized public void mouseMoved(java.awt.event.MouseEvent evt)
            {
                MapEvent me = new MapEvent();
                me.orgMouseEvent = evt;

                me.oldMouseX = mouseX;
                me.oldMouseY = mouseY;
                me.mapXOffset = offsetX;
                me.mapYOffset = offsetY;
                me.mouseX = evt.getX();
                me.mouseY = evt.getY();
                mouseX=me.mouseX;
                mouseY=me.mouseY;

                nearestHotSpot = new Point(unscaleIt(mouseX), unscaleIt(mouseY));
                nearestHotSpot.x += offsetX;
                nearestHotSpot.y += offsetY;

                if (gridEnabled)
                {
                    if (snapToGrid)
                    {
                        //nearestHotSpot = getHotSpotUnderCursor(mouseX, mouseY, false);
                        nearestHotSpot = getHotSpotUnderCursorByMidPoint(mouseX, mouseY, false);
                    }
                }

                me.nearestHotSpot = nearestHotSpot;

                MapPanel.this.mouseMoved(me);
                fireMousePositionChanged(me);
                if ((mousePressed) && ( (!multiTile) || (onlyOneTileOnPos)))
                {
                    me.draging = true;
                    fireMouseButtonChanged(me);
                }
                
            }
            @Override public void mouseDragged(java.awt.event.MouseEvent evt)
            {
                mouseMoved(evt);
            }
            public void MouseExited(java.awt.event.MouseEvent evt)
            {
                lastMouseX = -1;
                lastMouseY = -1;
                mousePressed=false;
            }
        });
        addMouseListener(new java.awt.event.MouseAdapter()
        {
            @Override public void mousePressed(java.awt.event.MouseEvent evt)
            //@Override public void mouseClicked(java.awt.event.MouseEvent evt)
            {

                MapEvent me = new MapEvent();
                me.orgMouseEvent = evt;

                me.oldMouseX = mouseX;
                me.oldMouseY = mouseY;
                me.mapXOffset = offsetX;
                me.mapYOffset = offsetY;
                me.mouseX = mouseX;
                me.mouseY = mouseY;

                me.nearestHotSpot = nearestHotSpot;
                
                lastTileOnPosX=Integer.MAX_VALUE;
                lastTileOnPosY=Integer.MAX_VALUE;
                
                fireMouseButtonChanged(me);
                mousePressed=true;
            }
            @Override public void mouseReleased(java.awt.event.MouseEvent evt)
            //@Override public void mouseClicked(java.awt.event.MouseEvent evt)
            {
                mousePressed=false;
            }
        });
        setMap(map);
    }
    public  void setMap(TMap m)
    {
        if (map != null)
        {
            deinit();
        }
        map = m;
        if (map != null)
        {
            initMap();
            doPaint = true;
        }
        else
            doPaint = false;
        setMapChanged(true, true);
        updateNeeded(true);
    }
    private void initMap()
    {
        map.setDisplay(this);
        repainterStop();
        
        setMapChanged(true, true);
        updateNeeded(true);

        // also sets offsets of mapPanel to offsets of current layer
        offsetX = map.getLayer(map.getCurrentLayerNo()).getData().moffsetX;
        offsetY = map.getLayer(map.getCurrentLayerNo()).getData().moffsetY;

        selectSprite = -1;
        selectLayer =-1;

        initBackground();

        repainterStart();
    }

    public void initBackground()
    {
        int layers = map.mLayers.size();
        if (layers>0)
        {
            if ((map.mLayers.get(0).getData().misVisible) && (map.mLayers.get(0).getData().misBackground))
            {
                hasBackGroundLayer = true;
                backColor = new Color(map.mLayers.get(0).getData().mbackgroundColor);
            }
            else
            {
                hasBackGroundLayer = false;
                backColor = Color.BLACK;
            }
        }
    }
    
    private void resetPaintList()
    {
        // clear old paint list
        // and tell all tiles they are currently NOT displayed
        for (int i = 0; i < paintList[activePaintList].size(); i++)
        {
            ((ArrayList<Paintable>)paintList[activePaintList]).get(i).setInPaintList(false);
        }
        activePaintList = (activePaintList + 1)%2;
        paintList[activePaintList].clear();
    }
    
    // builds all tiles which are painted in current viewport
    // for wrapping "ShallowTiles" will be built, which
    // reference the original tile, but have 
    // wrapped coordinates!

    private void buildPaintList()
    {
        visibleWidth = getWidth();
        visibleHeight = getHeight();
        
        if ( (scale == lastPaintScale) &&
             (offsetX == lastPaintOffsetX) &&
             (offsetY == lastPaintOffsetY) &&
             (visibleWidth == lastVisibleWidth) &&
             (visibleHeight == lastVisibleHeight) && (!mapChanged) && (!spriteChanged))
        {
            return;
        }
        
        
        
        spriteChanged = false;
        updateNaviPosition(mapReallyChanged);
        mapReallyChanged = false;
        
        lastPaintScale = scale;
        lastPaintOffsetX = offsetX;
        lastPaintOffsetY = offsetY;
        lastVisibleWidth = visibleWidth;
        lastVisibleHeight = visibleHeight;
        mapChanged = false;

        resetPaintList();
        int compares=0;
        for (int l=0; l<map.mLayers.size(); l++)
        {
            Layer layer = map.mLayers.get(l);
            int offsetXScaled = scaleIt(layer.getData().moffsetX);
            int offsetYScaled = scaleIt(layer.getData().moffsetY);

            if (layer.getData().misVisible)
            {
                for (int t=0; t<layer.mTiles.size(); t++)
                {
                    Tile tile = layer.mTiles.get(t);
                    if (tile.isEmptyTile()) continue;
                    
                    tile.setInPaintList(true);

                    tile.layer=l;
                    tile.tileInLayer=t;

                    
                    // for info:
                    // maxWidth & MaxHeight include if neccesarry inImage offsets (ox)
                    // so we dont use tile.getPaintXScaled(), which includes the offset also!

                    // tile not displayed out of bounds BEFOR_DISPLAY
                    if ( (!(tile.getXDirectScale(scale)+tile.getMaxWidthDirectScale(scale) < offsetXScaled) )
                    &&    (!(tile.getYDirectScale(scale)+tile.getMaxHeightDirectScale(scale) < offsetYScaled))
                    &&    (!(tile.getXDirectScale(scale) > offsetXScaled+visibleWidth))
                    &&    (!(tile.getYDirectScale(scale) > offsetYScaled+visibleHeight))
                       )
                    {
                        tile.setScalePercent(scale);
                        ((ArrayList<Tile>)paintList[activePaintList]).add(tile);
                    }
                    
                    int isoHalfWidth = 0;
                    int isoHalfHeight = 0;
                    if (layer.getData().mISOBlockWrap)
                    {
                        isoHalfWidth = tile.getMaxWidthDirectScale(scale)/2;
                        isoHalfHeight = tile.getMaxHeightDirectScale(scale)/2;
                    }

                    if ( (layer.getData().mWrapHorizontal) && (layer.getData().mWrapVertical))
                    {
                        int scaledWrapYOffset = 0;
                        int layerHeight = scaleIt(layer.totalHeight);
                        if (layer.minYPos>0)
                        {
                            layerHeight +=  scaleIt(layer.minYPos);
                        }
                        if (layer.maxYPos<0)
                        {
                            layerHeight -=  scaleIt(layer.maxYPos);
                        }

                        int layerWidth = scaleIt(layer.totalWidth);
                        if (layer.minXPos>0)
                        {
                            layerWidth +=  scaleIt(layer.minXPos);
                        }
                        if (layer.maxXPos<0)
                        {
                            layerWidth -=  scaleIt(layer.maxXPos);
                        }
                        int scaledWrapXOffset = 0;
                        compares=0;

                        
                        
                        boolean yFirst = true;
                        scaledWrapYOffset = -layerHeight; // do first line except yFirst :-)
                   
                        int isoYFactor = 0;
                        // wrap Y positiv
                        while (offsetYScaled+visibleHeight > scaledWrapYOffset-isoYFactor*isoHalfHeight + scaleIt(layer.maxYPos))
                        {
                            scaledWrapYOffset += layerHeight;
                            isoYFactor++;
                            isoYFactor = scaledWrapYOffset/layerHeight;
                            if (yFirst) 
                                scaledWrapXOffset = 0;
                            else
                                scaledWrapXOffset = -layerWidth;
                                
                            int isoXFactor = 0;
                            // wrap X positiv
                            while (offsetXScaled+visibleWidth > scaledWrapXOffset-isoXFactor*isoHalfWidth + scaleIt(layer.maxXPos))
                            {
                                scaledWrapXOffset += layerWidth;
                                isoXFactor++;
                                isoXFactor = scaledWrapXOffset/layerWidth;

                                compares++;
                                if ( (!(tile.getXDirectScale(scale)+scaledWrapXOffset-isoXFactor*isoHalfWidth+tile.getMaxWidthDirectScale(scale) < offsetXScaled) )
                                &&    (!(tile.getYDirectScale(scale)+scaledWrapYOffset-isoYFactor*isoHalfHeight+tile.getMaxHeightDirectScale(scale) < offsetYScaled))
                                &&    (!(tile.getXDirectScale(scale)+scaledWrapXOffset-isoXFactor*isoHalfWidth > offsetXScaled+visibleWidth))
                                &&    (!(tile.getYDirectScale(scale)+scaledWrapYOffset-isoYFactor*isoHalfHeight > offsetYScaled+visibleHeight))
                                   )
                                {
                                    ShallowTile sTile = tile.buildShallowClone();
                                    sTile.setScalePercent(scale);
                                    sTile.setY(tile.getY() + unscaleIt(scaledWrapYOffset-isoYFactor*isoHalfHeight));
                                    sTile.setX(tile.getX() + unscaleIt(scaledWrapXOffset-isoXFactor*isoHalfWidth));
                                    ((ArrayList<Tile>)paintList[activePaintList]).add(sTile);
                                }
                            }
                            if (yFirst) 
                                scaledWrapXOffset = 0;
                            else
                                scaledWrapXOffset = +layerWidth;

                            isoXFactor = 0;
                            // wrap X negative
                             while (offsetXScaled < scaledWrapXOffset+isoXFactor*isoHalfWidth + scaleIt(layer.minXPos) + tile.getMaxWidthDirectScale(scale))
                             {
                                   scaledWrapXOffset -= layerWidth;
                                   isoXFactor++;
                                   isoXFactor = -scaledWrapXOffset/layerWidth;
                                   
                                   
                                   compares++;
                                   if ( (!(tile.getXDirectScale(scale)+scaledWrapXOffset+isoXFactor*isoHalfWidth+tile.getMaxWidthDirectScale(scale) < offsetXScaled) )
                                   &&    (!(tile.getYDirectScale(scale)+scaledWrapYOffset-isoYFactor*isoHalfHeight+tile.getMaxHeightDirectScale(scale) < offsetYScaled))
                                   &&    (!(tile.getXDirectScale(scale)+scaledWrapXOffset+isoXFactor*isoHalfWidth > offsetXScaled+visibleWidth))
                                   &&    (!(tile.getYDirectScale(scale)+scaledWrapYOffset-isoYFactor*isoHalfHeight > offsetYScaled+visibleHeight))
                                      )
                                   {
                                       ShallowTile sTile = tile.buildShallowClone();
                                       sTile.setScalePercent(scale);
                                       sTile.setY(tile.getY() + unscaleIt(scaledWrapYOffset-isoYFactor*isoHalfHeight));
                                       sTile.setX(tile.getX() + unscaleIt(scaledWrapXOffset+isoXFactor*isoHalfWidth));
                                    ((ArrayList<Tile>)paintList[activePaintList]).add(sTile);
                                   }
                             }
                             yFirst = false;
                        }
                        yFirst = true;
                        scaledWrapYOffset = +layerHeight; // do first line except yFirst :-)
                        isoYFactor = 0;

                        // wrap Y negative
                        while (offsetYScaled < scaledWrapYOffset+isoYFactor*isoHalfHeight + scaleIt(layer.minYPos) + tile.getMaxHeightDirectScale(scale))
                        {
                            scaledWrapYOffset -= layerHeight;
                            isoYFactor = -scaledWrapYOffset/layerHeight;
                            int isoXFactor = 0;
                            if (yFirst) 
                                scaledWrapXOffset = 0;
                            else
                                scaledWrapXOffset = -layerWidth;

                            // wrap X positiv
                            while (offsetXScaled+visibleWidth > scaledWrapXOffset-isoXFactor*isoHalfWidth + scaleIt(layer.maxXPos))
                            {
                                scaledWrapXOffset += layerWidth;
                                isoXFactor++;
                                isoXFactor = scaledWrapXOffset/layerWidth;

                                compares++;
                                if ( (!(tile.getXDirectScale(scale)+scaledWrapXOffset-isoXFactor*isoHalfWidth+tile.getMaxWidthDirectScale(scale) < offsetXScaled) )
                                &&    (!(tile.getYDirectScale(scale)+scaledWrapYOffset+isoYFactor*isoHalfHeight+tile.getMaxHeightDirectScale(scale) < offsetYScaled))
                                &&    (!(tile.getXDirectScale(scale)+scaledWrapXOffset-isoXFactor*isoHalfWidth > offsetXScaled+visibleWidth))
                                &&    (!(tile.getYDirectScale(scale)+scaledWrapYOffset+isoYFactor*isoHalfHeight > offsetYScaled+visibleHeight))
                                   )
                                {
                                    ShallowTile sTile = tile.buildShallowClone();
                                    sTile.setScalePercent(scale);
                                    sTile.setY(tile.getY() + unscaleIt(scaledWrapYOffset+isoYFactor*isoHalfHeight));
                                    sTile.setX(tile.getX() + unscaleIt(scaledWrapXOffset-isoXFactor*isoHalfWidth));
                                    ((ArrayList<Tile>)paintList[activePaintList]).add(sTile);
                                }
                            }
                            if (yFirst) 
                                scaledWrapXOffset = 0;
                            else
                                scaledWrapXOffset = +layerWidth;

                            isoXFactor=0;
                            // wrap X negative
                             while (offsetXScaled < scaledWrapXOffset+isoXFactor*isoHalfWidth + scaleIt(layer.minXPos) + tile.getMaxWidthDirectScale(scale))
                             {
                                   scaledWrapXOffset -= layerWidth;
                                   isoXFactor++;
                                   isoXFactor = -scaledWrapXOffset/layerWidth;
                                   
                                   compares++;
                                   if ( (!(tile.getXDirectScale(scale)+scaledWrapXOffset+isoXFactor*isoHalfWidth+tile.getMaxWidthDirectScale(scale) < offsetXScaled) )
                                   &&    (!(tile.getYDirectScale(scale)+scaledWrapYOffset+isoYFactor*isoHalfHeight+tile.getMaxHeightDirectScale(scale) < offsetYScaled))
                                   &&    (!(tile.getXDirectScale(scale)+scaledWrapXOffset+isoXFactor*isoHalfWidth > offsetXScaled+visibleWidth))
                                   &&    (!(tile.getYDirectScale(scale)+scaledWrapYOffset+isoYFactor*isoHalfHeight > offsetYScaled+visibleHeight))
                                      )
                                   {
                                       ShallowTile sTile = tile.buildShallowClone();
                                       sTile.setScalePercent(scale);
                                       sTile.setY(tile.getY() + unscaleIt(scaledWrapYOffset+isoYFactor*isoHalfHeight));
                                       sTile.setX(tile.getX() + unscaleIt(scaledWrapXOffset+isoXFactor*isoHalfWidth));
                                       ((ArrayList<Tile>)paintList[activePaintList]).add(sTile);
                                   }
                             }
                             yFirst = false;
                        }                        
                    }
                    else if (layer.getData().mWrapVertical)
                    {
                        int scaledWrapYOffset = 0;
                        int layerHeight = scaleIt(layer.totalHeight);
                        if (layer.minYPos>0)
                        {
                            layerHeight +=  scaleIt(layer.minYPos);
                        }
                        if (layer.maxYPos<0)
                        {
                            layerHeight -=  scaleIt(layer.maxYPos);
                        }
                        int isoYFactor = 0;
                        // wrap Y positiv
                        while (offsetYScaled+visibleHeight > scaledWrapYOffset-isoYFactor*isoHalfHeight + scaleIt(layer.maxYPos))
                        {
                            scaledWrapYOffset += layerHeight;
                            isoYFactor++;

                            if ( (!(tile.getXDirectScale(scale)+tile.getMaxWidthDirectScale(scale) < offsetXScaled) )
                            &&    (!(tile.getYDirectScale(scale)+scaledWrapYOffset-isoYFactor*isoHalfHeight+tile.getMaxHeightDirectScale(scale) < offsetYScaled))
                            &&    (!(tile.getXDirectScale(scale) > offsetXScaled+visibleWidth))
                            &&    (!(tile.getYDirectScale(scale)+scaledWrapYOffset-isoYFactor*isoHalfHeight > offsetYScaled+visibleHeight))
                               )
                            {
                                ShallowTile sTile = tile.buildShallowClone();
                                sTile.setScalePercent(scale);
                                sTile.setY(tile.getY() + unscaleIt(scaledWrapYOffset-isoYFactor*isoHalfHeight));
                                ((ArrayList<Tile>)paintList[activePaintList]).add(sTile);
                            }
                        }
                        scaledWrapYOffset = 0;

                        isoYFactor = 0;
                        // wrap Y negative
                        while (offsetYScaled < scaledWrapYOffset+isoYFactor*isoHalfHeight + scaleIt(layer.minYPos) + tile.getMaxHeightDirectScale(scale))
                        {
                            scaledWrapYOffset -= layerHeight;
                            isoYFactor++;

                            if ( (!(tile.getXDirectScale(scale)+tile.getMaxWidthDirectScale(scale) < offsetXScaled) )
                            &&    (!(tile.getYDirectScale(scale)+scaledWrapYOffset+isoYFactor*isoHalfHeight+tile.getMaxHeightDirectScale(scale) < offsetYScaled))
                            &&    (!(tile.getXDirectScale(scale) > offsetXScaled+visibleWidth))
                            &&    (!(tile.getYDirectScale(scale)+scaledWrapYOffset+isoYFactor*isoHalfHeight > offsetYScaled+visibleHeight))
                               )
                            {
                                ShallowTile sTile = tile.buildShallowClone();
                                sTile.setScalePercent(scale);
                                sTile.setY(tile.getY() + unscaleIt(scaledWrapYOffset+isoYFactor*isoHalfHeight));
                                ((ArrayList<Tile>)paintList[activePaintList]).add(sTile);
                            }
                        }
                
                    }
                    else  if (layer.getData().mWrapHorizontal)
                    {
                        int scaledWrapXOffset = 0;
                        int layerWidth = scaleIt(layer.totalWidth);
                        if (layer.minXPos>0)
                        {
                            layerWidth +=  scaleIt(layer.minXPos);
                        }
                        if (layer.maxXPos<0)
                        {
                            layerWidth -=  scaleIt(layer.maxXPos);
                        }
                        int isoXFactor = 0;

                        // wrap X positiv
                        while (offsetXScaled+visibleWidth > scaledWrapXOffset -isoXFactor*isoHalfWidth+ scaleIt(layer.maxXPos))
                        {
                            scaledWrapXOffset += layerWidth;
                            isoXFactor++;
                            
                            if ( (!(tile.getXDirectScale(scale)+scaledWrapXOffset+tile.getMaxWidthDirectScale(scale) -isoXFactor*isoHalfWidth< offsetXScaled) )
                            &&    (!(tile.getYDirectScale(scale)+tile.getMaxHeightDirectScale(scale) < offsetYScaled))
                            &&    (!(tile.getXDirectScale(scale)+scaledWrapXOffset -isoXFactor*isoHalfWidth> offsetXScaled+visibleWidth))
                            &&    (!(tile.getYDirectScale(scale) > offsetYScaled+visibleHeight))
                               )
                            {
                                ShallowTile sTile = tile.buildShallowClone();
                                sTile.setScalePercent(scale);
                                sTile.setX(tile.getX() + unscaleIt(scaledWrapXOffset-isoXFactor*isoHalfWidth));
                                ((ArrayList<Tile>)paintList[activePaintList]).add(sTile);
                            }
                        }

                        isoXFactor = 0;
                        scaledWrapXOffset = 0;

                        // wrap X negative
                        while (offsetXScaled < scaledWrapXOffset+isoXFactor*isoHalfWidth + scaleIt(layer.minXPos) + tile.getMaxWidthDirectScale(scale))
                        {
                            scaledWrapXOffset -= layerWidth;
                            isoXFactor++;

                            if ( (!(tile.getXDirectScale(scale)+scaledWrapXOffset+isoXFactor*isoHalfWidth+tile.getMaxWidthDirectScale(scale) < offsetXScaled) )
                            &&    (!(tile.getYDirectScale(scale)+tile.getMaxHeightDirectScale(scale) < offsetYScaled))
                            &&    (!(tile.getXDirectScale(scale)+scaledWrapXOffset+isoXFactor*isoHalfWidth > offsetXScaled+visibleWidth))
                            &&    (!(tile.getYDirectScale(scale) > offsetYScaled+visibleHeight))
                               )
                            {
                                ShallowTile sTile = tile.buildShallowClone();
                                sTile.setScalePercent(scale);
                                sTile.setX(tile.getX() + unscaleIt(scaledWrapXOffset+isoXFactor*isoHalfWidth));
                                ((ArrayList<Tile>)paintList[activePaintList]).add(sTile);
                            }
                        }
                
                    }
                }
                
                
                
                
                for (int t=0; t<layer.mSprites.size(); t++)
                {
                    Sprite sprite = layer.mSprites.get(t);
                    sprite.layer=l;
                    sprite.spriteInLayer=t;

                    // for info:
                    // maxWidth & MaxHeight include if neccesarry inImage offsets (ox)
                    // so we dont use tile.getPaintXScaled(), which includes the offset also!

                    // sprite not displayed out of bounds BEFOR_DISPLAY
                    if ( (!(sprite.getXDirectScale(scale)+sprite.getMaxWidthDirectScale(scale) < offsetXScaled) )
                    &&    (!(sprite.getYDirectScale(scale)+sprite.getMaxHeightDirectScale(scale) < offsetYScaled))
                    &&    (!(sprite.getXDirectScale(scale) > offsetXScaled+visibleWidth))
                    &&    (!(sprite.getYDirectScale(scale) > offsetYScaled+visibleHeight))
                       )
                    {
                        sprite.setInPaintList(true);
//                        sprite.setDisplay(this); // this should be done only once in init!!!
                        sprite.setScalePercent(scale);
                        ((ArrayList<Paintable>)paintList[activePaintList]).add(sprite);
                    }
                }
                
            }
        }
        

        shallows.clear(); 
        // all tiles that are in non active paintlist and NOT in active paintlist -> get displayed false
        for (int i = 0; i < paintList[(activePaintList+1)%2].size(); i++)
        {
            Paintable p = ((ArrayList<Paintable>)paintList[(activePaintList+1)%2]).get(i);
            
            if (p instanceof Sprite)
            {
                Sprite sprite = (Sprite)p;
                if (paintList[activePaintList].indexOf(sprite) == -1)
                {
                    sprite.setDisplayed(false);
                }
                
            }
            else if (p instanceof Tile)
            {
                Tile tile = (Tile)p;
                if (tile.isShallowTile())
                {
                    tile = ((ShallowTile) tile).getParent();
                    if (shallows.indexOf(tile) == -1)
                        shallows.add(tile);
                }
                else
                {
                    if (paintList[activePaintList].indexOf(tile) == -1)
                    {
                        tile.setDisplayed(false);
                    }
                }
            }
        }

        // all tiles that are in active paintlist get displayed true
        for (int i = 0; i < paintList[activePaintList].size(); i++)
        {
            Paintable p = ((ArrayList<Paintable>)paintList[activePaintList]).get(i);
            if (p instanceof Tile)
            {
                Tile tile = (Tile)p;
                if (tile.isShallowTile())
                {
                    tile = ((ShallowTile) tile).getParent();
                    if (shallows.indexOf(tile) != -1)
                    {
                        shallows.remove(tile);
                    }
                }
                tile.setDisplayed(true);
            }
            else if (p instanceof Sprite)
            {
                Sprite sprite = (Sprite)p;
                if (paintList[activePaintList].indexOf(sprite) != -1)
                {
                    sprite.setDisplayed(true);
                }
                
            }
        }
        for (int i = 0; i < shallows.size(); i++)
        {
            Tile tile = shallows.get(i);
            tile.setDisplayed(false);
        }
        //System.out.println("Paintlist Len: " + paintList[activePaintList].size()+" compares:" +compares);
        // build new paint list!
        // tile setInPaintList(true);
    }
    ArrayList<Tile> shallows = new ArrayList<Tile>();

    public ArrayList<Tile> getTilesUnderMouse()
    {
        return getTilesOnPosition(lastMouseX, lastMouseY);
    }

    /** In Map XY pos, input are the direct input from mouse, that is in screen coordinates, without offset coordinates of the viewport.
     *  These are compared to the scaled version of the tiles.
     *
     * What that means is that a scaled map results in different results!
     *
     **/
    public ArrayList<Tile> getTilesOnPosition(int x, int y)
    {
        retTiles.clear();

        int mapX = unscaleIt(x) + map.getCurrentLayer().getData().moffsetX;
        int mapY = unscaleIt(y) + map.getCurrentLayer().getData().moffsetY;
        
        Tile tile1 = map.getCurrentLayer().isTileOnPosition( mapX,  mapY,  false);//true);

        if (tile1 != null)
        {
            retTiles.add(tile1);
            return retTiles;
        }

        return retTiles;
    }

    /** In Map XY pos, input are the direct input from mouse, that is in screen coordinates, without offset coordinates of the viewport.
     *  These are compared to the scaled version of the tiles.
     *
     * What that means is that a scaled map results in different results!
     *
     **/
    public ArrayList<Sprite> getSpritesOnPosition(int x, int y)
    {
        retSprites.clear();

        int mapX = unscaleIt(x) + map.getCurrentLayer().getData().moffsetX;
        int mapY = unscaleIt(y) + map.getCurrentLayer().getData().moffsetY;
        
        ArrayList<Sprite> sprites = map.getCurrentLayer().getSpritesOnPosition( mapX,  mapY);

        if (sprites != null)
        {
            for (Sprite s: sprites)
            {
               retSprites.add(s); 
            }
        }

        return retSprites;
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseExited(java.awt.event.MouseEvent evt) {
                formMouseExited(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 400, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 300, Short.MAX_VALUE)
        );
    }// </editor-fold>//GEN-END:initComponents

    private void formMouseExited(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseExited
        lastMouseX = -1;
        lastMouseY = -1;
    }//GEN-LAST:event_formMouseExited

    // Variables declaration - do not modify//GEN-BEGIN:variables
    // End of variables declaration//GEN-END:variables

    public void updateNeeded(boolean b)
    {
        if (!b) return;
        updateNeeded = b;
    }

    public void updateNeeded(Paintable painting)
    {
        // if in viewport
        if (painting.layer == -1) return;
        Layer layer = map.mLayers.get(painting.layer);

        if (!layer.getData().misVisible) return;
        int offsetXScaled = scaleIt(layer.getData().moffsetX);
        int offsetYScaled = scaleIt(layer.getData().moffsetY);
        
        // for info:
        // maxWidth & MaxHeight include if neccesarry inImage offsets (ox)
        // so we dont use tile.getPaintXScaled(), which includes the offset also!

        // sprite not displayed out of bounds BEFOR_DISPLAY
        if ( (!(painting.getXDirectScale(scale)+painting.getMaxWidthDirectScale(scale) < offsetXScaled) )
        &&    (!(painting.getYDirectScale(scale)+painting.getMaxHeightDirectScale(scale) < offsetYScaled))
        &&    (!(painting.getXDirectScale(scale) > offsetXScaled+visibleWidth))
        &&    (!(painting.getYDirectScale(scale) > offsetYScaled+visibleHeight))
           )
        {
            updateNeeded(true);
            spriteChanged = true;
        }
    }
    
    public void visibilityChanged()
    {
        updateNeeded(true);
        setMapChanged(true, true);
        initBackground();
    }
    public void highliteTile(int layer, int tileNo)
    {
        highlightLayer = layer;
        highlightTile = tileNo;
        updateNeeded(true);
    }
    public void highliteSprite(int layer, int spriteNo)
    {
        highlightLayer = layer;
        highlightSprite = spriteNo;
        updateNeeded(true);
    }
    synchronized public void deleteSprite(int l, int spriteNo)
    {
        highlightLayer = -1;
        highlightSprite = -1;
        selectSprite = -1;
        selectLayer = -1;
        Layer layer = map.mLayers.get(l);
        layer.deleteSprite(spriteNo);

        setMapChanged(true, false); // otherwise paintlist is not rebuild!
        updateNeeded(true);
    }

    synchronized public Sprite selectSprite(int l, int spriteNo)
    {
        selectSprite = spriteNo;
        selectLayer = l;
        if (spriteNo == -1)
        {
            return null;
        }
        Layer layer = map.mLayers.get(l);
        return layer.getSprite(spriteNo);
    }

    synchronized public void deleteTile(int l, int tileNo)
    {
        highlightLayer = -1;
        highlightTile = -1;
        Layer layer = map.mLayers.get(l);
        layer.deleteTile(tileNo);

        setMapChanged(true, true);
        updateNeeded(true);
    }
    public void upTile(int l, int tileNo)
    {
        Layer layer = map.mLayers.get(l);
        layer.upTile(tileNo);
        setMapChanged(true, true);
        updateNeeded(true);
        highlightTile = tileNo++;
    }
    public void downTile(int l, int tileNo)
    {

        Layer layer = map.mLayers.get(l);
        layer.downTile(tileNo);
        setMapChanged(true, true);
        updateNeeded(true);
        highlightTile = tileNo--;
    }
    public void downLayer(int l)
    {
        if (l == 0) return;
        Layer layer = map.mLayers.get(l);
        map.mLayers.remove(l);
        map.mLayers.add(l-1, layer);
        layer.getData().mLayerNumber--;
        map.mLayers.get(l).getData().mLayerNumber++;

        setMapChanged(true, true);
        updateNeeded(true);
        highlightLayer = -1;
        highlightTile = -1;
    }
    public void upLayer(int l)
    {
        if (l == map.mLayers.size()-1) return;
        Layer layer = map.mLayers.get(l);
        map.mLayers.remove(l);
        map.mLayers.add(l+1, layer);
        layer.getData().mLayerNumber++;
        map.mLayers.get(l).getData().mLayerNumber--;

        setMapChanged(true, true);
        updateNeeded(true);
        highlightLayer = -1;
        highlightTile = -1;
    }

    public void repainterStart()
    {
        if (started) return;
        started = true;
        mapChanged = true;
        updateNeeded = true;
        if (delay==-1) return;
        final TimingTriggerer timer = TimingTriggerer.getTimer();
        timer.setResolution(10); //100*1ms resoltuion is 0,1seconds
        tt = new TriggerCallback()
        {
            @Override
            public void doIt(int state, Object o)
            {
                if (started)
                    timer.addTrigger(tt, delay, 0, null);
                if (updateNeeded)
                    repaint();
                updateNeeded = false;
            }
        };
        timer.addTrigger(tt, delay, 0, null);
    }
    public void repainterStop()
    {
        if (!started) return;
        started = false;
        if (delay==-1) 
        {
        }
        final TimingTriggerer timer = TimingTriggerer.getTimer();
        timer.removeTrigger(tt);
    }

    //
    // point at which the figure is drawn is allways the Hotsspot
    private void drawFigure(Graphics2D g2, int figureType, int x, int y)
    {
        if (figureType ==FIGURE_DOTS)
        {
            //if (x<0) return;
            //if (y<0) return;
            drawDots(g2, x, y);
        }

        if (figureType ==FIGURE_SQUARE)
        {
            g2.drawRect(scaleIt(x), scaleIt(y), scaleIt(grid.meveryX.elementAt(0)), scaleIt(grid.meveryY.elementAt(0)));
        }
        if (figureType ==FIGURE_HOT_SPOT)
        {
            if (x<0) return;
            if (y<0) return;
            g2.drawLine(scaleIt(x), scaleIt(y), scaleIt(x), scaleIt(y));
        }
        if (figureType ==FIGURE_HEX)
        {
            drawHex(g2, x, y);
        }
        if (figureType ==FIGURE_ISO)
        {
            drawISO(g2, x, y);
        }
    }

    // unscaled!
    // but with offset!
    private Vector<Point> generateVisibleHotSpots()
    {
        Vector<Point> hotSpots = new Vector<Point>();

        boolean iso = grid.mDrawTyp == FIGURE_ISO;
        // iso is special, since we double everything with half settings!
        
        int yLine=0;
        //int xLine=0;

        //int xCount = 0; // ROW COUNT OF FIGURES DRAWN at current drawing
        //int yCount = 0; // Columns count of figures drawn  at current drawing

        // in respect to offset calculate first xCount and ycount
        int maxWidth=0;
        int maxHeight=0;
        for (int i=0; i<grid.meveryX.size();i++ )maxWidth+=grid.meveryX.elementAt(i);
        for (int i=0; i<grid.meveryY.size();i++ )maxHeight+=grid.meveryY.elementAt(i);

        int usedOffsetX = - offsetX;
        int usedOffsetY = - offsetY;

        // TODO: scaling not done
        // example offsetx = 2
        // everyX = 10
        // startx = 2
        // difx = 8
        if (grid.meveryX.isEmpty()) return hotSpots;
        if (grid.meveryY.isEmpty()) return hotSpots;

        // allways offset minus one maxwidth, that the parts of
        // previous grid settings are also drawn, not just the "full" grids
        //startx = startx -(maxWidth);
        //starty = starty -(maxHeight);
        int startx = usedOffsetX;
        while (startx>0)
        {
            startx -= maxWidth;
            //xLine = (xCount+1) % grid.meveryX.size();
        }
        int startXOrg = startx;

        int starty = usedOffsetY;
        while (starty>0)
        {
            // for y we start TWO lines early,
            // since we have alternating X lines with every other y line
            // if we only go back 1 line, than we might lose a hotspot
            starty -= maxHeight;
            starty -= maxHeight;
            yLine+=2;
        }

        if (!iso)
        {
            int yAdd  = grid.meveryY.elementAt(0);
            for (int y = starty; y <unscaleIt(visibleHeight); y += yAdd)
            {
                if (grid.mXAlternating)
                {
                    startx = startXOrg;
                    if (yLine % 2 != 0)
                    {
                        startx += grid.meveryX.elementAt(grid.meveryX.size()-1);
                        startx -= grid.getXOffset().elementAt(0);
                    }
                }

                for (int x = startx; x <unscaleIt(visibleWidth); x+=maxWidth)
                {
                        hotSpots.addElement(new Point(x,y));
                }
                yLine++;
            }
        }
        else
        {
            
            int yAdd  = grid.meveryY.elementAt(0)/2;

            for (int y = starty; y <unscaleIt(visibleHeight); y += yAdd)
            {
                boolean inyHalf = (yLine%2) == 1;
                for (int x = startx; x <unscaleIt(visibleWidth); x+=maxWidth)
                {
                    if (inyHalf)
//                        hotSpots.addElement(new Point(x+(maxWidth/2),y));
                        hotSpots.addElement(new Point(x+(Math.round((float) (((float)maxWidth)/2.0))),y));
                    else
                        hotSpots.addElement(new Point(x,y));
                }
                yLine++;
            }
            
        }
        return hotSpots;
    }

    private void drawGridByHotSpot(Graphics2D g2, int figureType)
    {
        Vector<Point> hotSpots = generateVisibleHotSpots();
        Color hotColor = Color.red;
        Color gridColor = new Color(grid.mGridColor);

        if (figureType == FIGURE_HOT_SPOT)
            g2.setColor(hotColor);
        else
            g2.setColor(gridColor);

        for (int i = 0; i < hotSpots.size(); i++)
        {
            Point point = hotSpots.elementAt(i);
            drawFigure(g2, figureType, point.x, point.y);
        }
    }

    // entry is UNSCALED!
    private void drawDots(Graphics2D g2, int x, int y)
    {
        int plotY = y;
        int yLen = grid.meveryY.size();
        if (grid.getXOffset().size() > yLen) {
            yLen = grid.getXOffset().size();
        }


        for (int yy=0; yy<yLen; yy++)
        {
            boolean alternating = false;
            if (grid.mXAlternating)
            {
                if (yy%2 != 0) alternating = true;
            }
            int plotX = x;
            if (alternating)
            {
                for (int xx=grid.meveryX.size()-1; xx>=0; xx--)
                {
                    int offsetXNow=0;
                    if (grid.getXOffset().size() >0)
                    {
                        offsetXNow = grid.getXOffset().elementAt( (yy%grid.getXOffset().size())  );
                    }
                    drawLineScaled(g2,plotX+offsetXNow, plotY, plotX+offsetXNow, plotY);
                    plotX+=grid.meveryX.elementAt(xx);
                }

            }
            else
            {
                for (int xx=0; xx<grid.meveryX.size(); xx++)
                {
                    int offsetXNow=0;
                    if (grid.getXOffset().size() >0)
                    {
                        offsetXNow = grid.getXOffset().elementAt( (yy%grid.getXOffset().size())  );
                    }
                    drawLineScaled(g2,plotX+offsetXNow, plotY, plotX+offsetXNow, plotY);
                    plotX+=grid.meveryX.elementAt(xx);
                }
            }
            plotY+=grid.meveryY.elementAt(yy%grid.meveryY.size());
        }
    }

    double getDistance(Point p1, Point p2)
    {
        int y2 = (p2.y-p1.y) * (p2.y-p1.y);
        int x2 = (p2.x-p1.x) * (p2.x-p1.x);
        double d = Math.sqrt(y2+x2);
        return d;
    }

    Point scalePoint(Point p)
    {
        //Point ret = new Point(scaleIt(p.x), scaleIt(p.y));
        //return ret;
        return p;
    }
    Point checkPoint(int x, int y, Point input, Point ret, int middleOffsetX, int middleOffsetY)
    {
        double oldDistance = getDistance((input), (new Point(ret.x+middleOffsetX,ret.y+middleOffsetY)));
        double newDistance = getDistance((input), (new Point(x+middleOffsetX,y+middleOffsetY)));
        if (oldDistance>newDistance)
            return new Point(x,y);
        return ret;
    }
    boolean isPointOverdrawnByGrid(Point hotspotPoint, Point pos, int type)
    {
        if (type == FIGURE_SQUARE) // Square
        {
            return (((pos.x >= hotspotPoint.x) && (pos.x < hotspotPoint.x+grid.meveryX.elementAt(0)))
                &&  ((pos.y >= hotspotPoint.y) && (pos.y < hotspotPoint.y+grid.meveryY.elementAt(0))));

        }
        return false;
    }

    // looks for nearest the distance to the next hotspot "middlepoints"
    private Point getHotSpotUnderCursorByMidPoint(int mx, int my, boolean display)
    {
        
        
        if (grid == null) return new Point(unscaleIt(mx),unscaleIt(my));
        if (!snapToGrid) return new Point(unscaleIt(mx),unscaleIt(my));
        int type=FIGURE_SQUARE;
        int middleOffsetX = 0;
        int middleOffsetY = 0;
        if (grid.mDrawTyp == 0) // DOTS
        {
            type = FIGURE_DOTS;
            if (grid.geteveryX().size()>1)
                middleOffsetX = grid.geteveryX().elementAt(0)+grid.geteveryX().elementAt(1);
            else
                middleOffsetX = grid.geteveryX().elementAt(0);
            middleOffsetX /= 2;
 
            if (grid.geteveryY().size()>1)
                middleOffsetY = grid.geteveryY().elementAt(0)+grid.geteveryY().elementAt(1);
            else
            {
                if (grid.mXOffset.size()>1)
                {
                    middleOffsetY = 2*grid.geteveryY().elementAt(0);
                }
                else
                {
                    middleOffsetY = grid.geteveryY().elementAt(0);
                }
                
            }
            middleOffsetY /= 2;
        }
        else if (grid.mDrawTyp == 1) // Square
        {
            type = FIGURE_SQUARE;
            middleOffsetY = grid.meveryY.elementAt(0);
            middleOffsetX = grid.meveryX.elementAt(0);
            middleOffsetX /= 2;
            middleOffsetY /= 2;
        }
        else if (grid.mDrawTyp == 2) // Hex
        {
            type = FIGURE_HEX;
            middleOffsetX = grid.geteveryX().elementAt(0)+grid.geteveryX().elementAt(1);
            middleOffsetY = 2*grid.geteveryY().elementAt(0);
            middleOffsetX /= 2;
            middleOffsetY /= 2;
        }
        else if (grid.mDrawTyp == 3) // ISO
        {
            
            
            type = FIGURE_ISO;
            middleOffsetY = grid.meveryY.elementAt(0);
            middleOffsetX = grid.meveryX.elementAt(0);
            middleOffsetX /= 2;
            middleOffsetY /= 2;
            middleOffsetX /= 2;
            middleOffsetY /= 2;
            
        }
        else if (grid.mSHowHotSpot)
        {
            type = FIGURE_HOT_SPOT;
        }

        return getNearestHotSpot_org(mx, my, display,middleOffsetX, middleOffsetY);
    }
    
    // hotspot as PAINTED
    // for map coordinates, the hotspot must be
    // "repaired" with offsets!
    // return is WITH offset, but UNSCALED! when display = true -> in display coordinates
    // return is WITHOUT offset, and UNSCALED! when display = false -> in map coordinates

    // input is mouse coordinate
    // meaning, input is
    // a) scaled
    // b) with offset
    private Point getHotSpotUnderCursor(int mx, int my, boolean display)
    {
        if (grid == null) return new Point(unscaleIt(mx),unscaleIt(my));
        if (!snapToGrid) return new Point(unscaleIt(mx),unscaleIt(my));

        int type=FIGURE_SQUARE;
        if (grid.mDrawTyp == 0) // DOTS
        {
            type = FIGURE_DOTS;
        }
        else if (grid.mDrawTyp == 1) // Square
        {
            type = FIGURE_SQUARE;
        }
        else if (grid.mDrawTyp == 2) // Hex
        {
            type = FIGURE_HEX;
        }
        else if (grid.mDrawTyp == 3) // ISO
        {
            type = FIGURE_ISO;
        }
        else if (grid.mSHowHotSpot)
        {
            type = FIGURE_HOT_SPOT;
        }

        if (type!=FIGURE_SQUARE)
        {
            return getNearestHotSpot_org(mx, my, display);
        }
        
        
        
        Point ret = new Point(0,0);
        Point mousePos = new Point(unscaleIt(mx),unscaleIt(my));

        Vector<Point> hotSpots = generateVisibleHotSpots();

        for (int i = 0; i < hotSpots.size(); i++)
        {
            Point hotspotPoint = hotSpots.elementAt(i);
            if (type==FIGURE_SQUARE)
            {
                if (isPointOverdrawnByGrid(hotspotPoint, mousePos, type))
                {
                    ret = hotspotPoint;
                    break;
                }
            }
        }
        if (!display)
        {
            ret.x = ret.x + offsetX;
            ret.y = ret.y + offsetY;
        }
        return ret;
    }
    
    // org gets actually the NEAREST (distance) hotspot
    private Point getNearestHotSpot_org(int mx, int my, boolean display)
    {
        return getNearestHotSpot_org(mx, my, display, 0, 0);
    }

    private Point getNearestHotSpot_org(int mx, int my, boolean display, int middleOffsetX, int middleOffsetY)
    {
        if (grid == null) return new Point(unscaleIt(mx),unscaleIt(my));
        if (!snapToGrid) return new Point(unscaleIt(mx),unscaleIt(my));
        Point ret = new Point(mx+10000, mx+10000); // a BIG distance from the one to be checked
        Point input;
        
        if (!display)
        {
            input = new Point(unscaleIt(mx)-offsetX,unscaleIt(my)-offsetY);
            input = new Point(unscaleIt(mx),unscaleIt(my));
        }
        else
        {
            input = new Point(unscaleIt(mx),unscaleIt(my));
            
        }
        
        Vector<Point> hotSpots = generateVisibleHotSpots();

        for (int i = 0; i < hotSpots.size(); i++)
        {
            Point point = hotSpots.elementAt(i);
            ret = checkPoint(point.x, point.y, input, ret, middleOffsetX, middleOffsetY);
        }
        if (ret.x ==0+middleOffsetX )
        {
            if (ret.y ==0+middleOffsetY )
            {
                ret.x = 0;
                ret.y = 0;
            }
        }
        if (!display)
        {
            ret.x = ret.x + offsetX;
            ret.y = ret.y + offsetY;
        }
        return ret;
    }
    // exepcting like:
    // X every:  32, 64
    // X Offset: 16,0
    //
    // Y every:  32
    // Y Offset: -

    void drawLineScaled(Graphics2D g2, int x, int y, int x1, int y1)
    {
        g2.drawLine(scaleIt(x), scaleIt(y), scaleIt(x1), scaleIt(y1));
    }
    private void drawHex(Graphics2D g2, int xStart, int yStart)
    {
        if (grid.geteveryX().size()<2) return;
        if (grid.getXOffset().size()<2) return;
        if (grid.geteveryY().size()<1) return;
                
        drawLineScaled(g2, xStart+grid.mXOffset.elementAt(0),
                    yStart,
                    xStart+grid.geteveryX().elementAt(0)+grid.mXOffset.elementAt(0),
                    yStart);

        drawLineScaled(g2, xStart+grid.geteveryX().elementAt(0)+grid.mXOffset.elementAt(0),
                    yStart,
                    xStart+grid.geteveryX().elementAt(1),
                    yStart+grid.geteveryY().elementAt(0));

        drawLineScaled(g2, xStart+grid.geteveryX().elementAt(1),
                    yStart+grid.geteveryY().elementAt(0),
                    xStart+grid.geteveryX().elementAt(0)+grid.mXOffset.elementAt(0),
                    yStart+2*grid.geteveryY().elementAt(0)
                );

        drawLineScaled(g2, xStart+grid.geteveryX().elementAt(0)+grid.mXOffset.elementAt(0),
                    yStart+2*grid.geteveryY().elementAt(0),
                    xStart+grid.mXOffset.elementAt(0),
                    yStart+2*grid.geteveryY().elementAt(0)
                );

        drawLineScaled(g2, xStart+grid.mXOffset.elementAt(0),
                    yStart+2*grid.geteveryY().elementAt(0),
                    xStart,
                    yStart+grid.geteveryY().elementAt(0)
                );
        drawLineScaled(g2, xStart,
                    yStart+grid.geteveryY().elementAt(0),
                    xStart+grid.mXOffset.elementAt(0),
                    yStart);
    }
    // exepcting like: 4:3
    // X every:  128
    // Y every: 96
    private void drawISO(Graphics2D g2, int xStart, int yStart)
    {
        if (grid.geteveryX().size()<1) return;
        if (grid.geteveryY().size()<1) return;

        drawLineScaled(g2, xStart+grid.geteveryX().elementAt(0)/2,
                    yStart,
                    xStart+grid.geteveryX().elementAt(0),
                    yStart+grid.geteveryY().elementAt(0)/2);

        drawLineScaled(g2, xStart+grid.geteveryX().elementAt(0),
                    yStart+grid.geteveryY().elementAt(0)/2,
                    xStart+grid.geteveryX().elementAt(0)/2,
                    yStart+grid.geteveryY().elementAt(0)
                    );
        drawLineScaled(g2, xStart+grid.geteveryX().elementAt(0)/2,
                    yStart+grid.geteveryY().elementAt(0),
                    xStart,
                    yStart+grid.geteveryY().elementAt(0)/2
                    );
        drawLineScaled(g2, xStart,
                    yStart+grid.geteveryY().elementAt(0)/2,
                    xStart+grid.geteveryX().elementAt(0)/2,
                    yStart
                    );
    }
    private void doGrid(Graphics2D g2)
    {
        Color gridColor = new Color(grid.mGridColor);
        Color hotColor = Color.red;

        g2.setColor(gridColor);
        if (grid.mDrawTyp == 0) // DOTS
        {
            drawGridByHotSpot(g2, FIGURE_DOTS);
        }
        if (grid.mDrawTyp == 1) // Square
        {
            drawGridByHotSpot(g2, FIGURE_SQUARE);
        }
        else if (grid.mDrawTyp == 2) // Hex
        {
            drawGridByHotSpot(g2, FIGURE_HEX);
        }
        else if (grid.mDrawTyp == 3) // ISO
        {
            drawGridByHotSpot(g2, FIGURE_ISO);
        }
        if (grid.mSHowHotSpot)
        {
            drawGridByHotSpot(g2, FIGURE_HOT_SPOT);
        }
    }
    @Override public void paintComponent(Graphics g1)
    {
        paintComponent(g1, true);
    }
    public void paintComponent(Graphics g1, boolean paintStuff)
    {
        if (!doPaint)
        {
            //super.paintComponent(g1);
            Graphics2D g2 = (Graphics2D) g1;
            g2.setColor(Color.BLACK);
            g2.fillRect(0, 0, getWidth(), getHeight());
            return;
        }
        Graphics2D g2 = (Graphics2D) g1;

        buildPaintList();

        int rectX=-1;
        int rectY=-1;
        int rectW=-1;
        int rectH=-1;

        int selrectX=-1;
        int selrectY=-1;
        int selrectW=-1;
        int selrectH=-1;
        
        
        if (!hasBackGroundLayer)
        {
            g2.setColor(backColor);
            g2.fillRect(0, 0, getWidth(), getHeight());
        }
        else
        {
            g2.setColor(backColor);
            g2.fillRect(0, 0, getWidth(), getHeight());
        }

        for (int i = 0; i < paintList[activePaintList].size(); i++)
        {
            Paintable p = ((ArrayList<Paintable>)paintList[activePaintList]).get(i);
            if (p instanceof Tile)
            {
                Tile tile; 
                tile = (Tile)p;
                Layer layer = map.getLayer(tile.layer);
                int offsetXScaled = scaleIt(layer.getData().moffsetX);
                int offsetYScaled = scaleIt(layer.getData().moffsetY);

                BufferedImage im = tile.getImage();

                g2.drawImage(im, tile.getPaintXScaled()-offsetXScaled, tile.getPaintYScaled()-offsetYScaled, null);
                if (!paintStuff) continue;

                if ((highlightLayer!=-1) && ((highlightLayer == tile.layer) && (highlightTile==tile.tileInLayer)))
                {
                    rectX=tile.getXScaled()-offsetXScaled;
                    rectY=tile.getYScaled()-offsetYScaled;

                    
                    int w = tile.getMaxWidthUnscaled();
                    int h = tile.getMaxHeightUnscaled();
                    int currentScale = tile.getScalePercent();
                    
                    
                    rectW=tile.getMaxWidth();
                    rectH=tile.getMaxHeight();
                    
                    rectW = Scaler.scalePercentToInt(w, currentScale);
                    rectH = Scaler.scalePercentToInt(h, currentScale);
                    /*
                    rectX=tile.getPaintXScaled()-offsetXScaled;
                    rectY=tile.getPaintYScaled()-offsetYScaled;

                    rectW=im.getWidth();
                    rectH=im.getHeight();
                    */
                    
                    
                    
                    
                }
                
            }
            else if (p instanceof Sprite)
            {
                Sprite sprite = (Sprite)p;
                Layer layer = map.getLayer(sprite.layer);
                int offsetXScaled = scaleIt(layer.getData().moffsetX);
                int offsetYScaled = scaleIt(layer.getData().moffsetY);

                BufferedImage im = sprite.getImage();

                g2.drawImage(im, sprite.getPaintXScaled()-offsetXScaled, sprite.getPaintYScaled()-offsetYScaled, null);
                if (!paintStuff) continue;

                if ((highlightLayer!=-1) && ((highlightLayer == sprite.layer) && (highlightSprite==sprite.spriteInLayer)))
                {
                    rectX=sprite.getXScaled()-offsetXScaled;
                    rectY=sprite.getYScaled()-offsetYScaled;

                    rectW=sprite.getMaxWidth();
                    rectH=sprite.getMaxHeight();
                }
                if ((selectLayer!=-1) && ((selectLayer == sprite.layer) && (selectSprite==sprite.spriteInLayer)))
                {
                    selrectX=sprite.getXScaled()-offsetXScaled;
                    selrectY=sprite.getYScaled()-offsetYScaled;

                    selrectW=sprite.getMaxWidth();
                    selrectH=sprite.getMaxHeight();
                }
            }

            // paint
        }

        if (!paintStuff) return;
        
        if (grid != null)
        {
            if (gridEnabled)
            {
                if ((grid.meveryY.size() > 0) && (grid.meveryX.size() > 0))
                {
                    doGrid(g2);
                }
            }
        }

        if (rectX!=-1)
        {
            g2.setColor(Color.red);
            g2.drawRect(rectX, rectY, rectW, rectH);
        }
        paintMouseGrab(g2);
        if (selrectX!=-1)
        {

            g2.setColor(Color.blue);
            g2.drawRect(selrectX, selrectY, selrectW, selrectH);
        }        
    }

    public int scaleIt(int coord)
    {
        return Scaler.scalePercentToInt(coord, scale);
        // return (coord*scale)/100;
    }

    public int unscaleIt(int coord)
    {
        return Scaler.unscalePercentToInt(coord, scale);
        //return (coord*100)/scale;
    }

    public Image getImage()
    {
        Image img = createImage(getWidth(),getHeight());
        Graphics g = img.getGraphics();
        paintComponent(g);
        return img;
    }




    GridSettingsData grid = null;
    boolean gridEnabled = false;
    boolean snapToGrid = false;
    public void setSnap(boolean s)
    {
        snapToGrid = s;
    }
    public void setGrid(GridSettingsData g, boolean e)
    {
        gridEnabled = e;
        grid = g;
        repaint();
    }

    void paintMouseGrab(Graphics2D g2)
    {
        if (lastMouseX == -1) return;
        if (lastMouseY == -1) return;
        if (!paintMouseGrab) return;

        if (mInSpriteEditMode)
        {
            Sprite sprite = mMouseGrabSprite;
            if (sprite == null)
            {
                return;
            }

            BufferedImage im;
            sprite.setScalePercent(scale);
            im = sprite.getImage();

            // todo
            // clip to visible
            sprite.setX(unscaleIt(mouseX)+offsetX);
            sprite.setY(unscaleIt(mouseY)+offsetY);
            int drawX = (mouseX);
            int drawY = (mouseY);

            // System.out.println("Draw      HS          : "+drawX+", "+drawY+"(" + scale+ ")");
            // System.out.println("Paint Grab HS (in Map): "+nearestHotSpot.x+", "+nearestHotSpot.y);

            if (im == null) return;

            int offsetXScaled = scaleIt(offsetX);
            int offsetYScaled = scaleIt(offsetY);

            g2.drawImage(im, sprite.getPaintXScaled()-offsetXScaled, sprite.getPaintYScaled()-offsetYScaled, null);


            g2.setColor(Color.green);
            g2.drawRect(drawX, drawY, sprite.getMaxWidth(), sprite.getMaxHeight());
        
        }
        else // else paint tile if available
        {
            Tile tile = mMouseGrabTile;
            if (KeyboardListener.isShiftDown())
                tile = mShiftMouseGrabTile;
            if (tile == null)
            {
                if (mMouseGrabCompositeTile == null)
                    return;
                tile = mMouseGrabCompositeTile;
            }

            BufferedImage im;
            if (tile.isCompositeTile())
            {
                im = ((CompositeTile)tile).getImage(scale);
            }
            else
            {
                tile.setScalePercent(scale);
                im = tile.getImage();
            }

            // todo
            // clip to visible
            tile.setX(nearestHotSpot.x);
            tile.setY(nearestHotSpot.y);
            int drawX = scaleIt(nearestHotSpot.x-offsetX);
            int drawY = scaleIt(nearestHotSpot.y-offsetY);

            // System.out.println("Draw      HS          : "+drawX+", "+drawY+"(" + scale+ ")");
            // System.out.println("Paint Grab HS (in Map): "+nearestHotSpot.x+", "+nearestHotSpot.y);

            if (im == null) return;

            int offsetXScaled = scaleIt(offsetX);
            int offsetYScaled = scaleIt(offsetY);

            
            int w = tile.getMaxWidthUnscaled();
            int h = tile.getMaxHeightUnscaled();
            int currentScale = tile.getScalePercent();
            
            if (currentScale<100)
                im = csa.util.UtilityImage.imageScale(im, Scaler.scalePercentToInt(w, currentScale), Scaler.scalePercentToInt(h, currentScale));
            

            
            // composite tile is never an ISO tile, therefor we can use here old drawing!
            if (tile.isCompositeTile())
            {
                g2.drawImage(im, drawX+tile.getCurrentOffsetX(), drawY+tile.getCurrentOffsetY(), null);
                g2.setColor(Color.green);
                g2.drawRect(drawX, drawY, Scaler.scalePercentToInt(w, currentScale), Scaler.scalePercentToInt(h, currentScale));
            }
            else
            {
                g2.drawImage(im, tile.getPaintXScaled()-offsetXScaled, tile.getPaintYScaled()-offsetYScaled, null);
                g2.setColor(Color.green);
                g2.drawRect(drawX, drawY, Scaler.scalePercentToInt(w, currentScale), Scaler.scalePercentToInt(h, currentScale));
            }


        }


    }

    public void mouseMoved(MapEvent e)
    {
        lastMouseX = e.mouseX;
        lastMouseY = e.mouseY;
        if (mInSpriteEditMode)
        {
            if (mMouseGrabSprite != null)
                updateNeeded(true);
        }
        else
        {
            if (mMouseGrabTile != null)
                updateNeeded(true);
        }
    }
    public void setMouseGrabSprite(Sprite s)
    {
        if (mMouseGrabSprite != null)
            mMouseGrabSprite.removeAnimationListener(this);

        mMouseGrabSprite = s;
        if (mMouseGrabSprite != null)
        {
            mMouseGrabSprite.setAutoMoveEnabled(false);
            mMouseGrabSprite.addAnimationListener(this);
            selectSprite = -1;
            selectLayer =-1;
        }
    }
    
    public void reinitSprite(int layer, int sprite)
    {
        map.getLayer(layer).reinitSprite(sprite);
        setMapChanged(true, false);
    }

    public void setMouseGrabTile(Tile t, Tile st)
    {
        if (mMouseGrabTile != null)
            mMouseGrabTile.removeAnimationListener(this);

        if (t!= null) if (t.noTile()) t=null;

        mMouseGrabTile = t;
        mMouseGrabCompositeTile = null;
        if (t != null)
            mMouseGrabTile.addAnimationListener(this);

        if (mShiftMouseGrabTile != null)
            mShiftMouseGrabTile.removeAnimationListener(this);

        if (st!= null) if (st.noTile()) st=null;
        mShiftMouseGrabTile = st;
        if (st != null)
            mShiftMouseGrabTile.addAnimationListener(this);
    }

    public void setMouseGrabCompositeTile(CompositeTile ct)
    {
        if (mMouseGrabTile != null)
            mMouseGrabTile.removeAnimationListener(this);
        mMouseGrabTile = null;

        mMouseGrabCompositeTile=ct;
        if (ct==null) return;
    }

    @Override
    public void animationStateChanged()
    {
        if (mInSpriteEditMode)
        {
            if (mMouseGrabSprite != null)
                updateNeeded(true);
        }
        else
        {
            if (mMouseGrabTile != null)
                updateNeeded(true);
        }
    }

    public void setSelectedLayer(int l)
    {
        mSelectedLayer = l;
        // also sets offsets of mapPanel to offsets of current layer

        offsetX = map.getLayer(l).getData().moffsetX;
        offsetY = map.getLayer(l).getData().moffsetY;
    }

    public void offsetXInc(int inc, boolean allLayers)
    {
        offsetX += inc;
        map.offsetXInc(inc, allLayers);
        updateNeeded(true);
        fireOffsetChanged(null);        
    }

    public void offsetYInc(int inc, boolean allLayers)
    {
        offsetY += inc;
        map.offsetYInc(inc, allLayers);
        updateNeeded(true);
        fireOffsetChanged(null);        
    }

    public void setOffsetX(int o, boolean allLayers)
    {
        offsetX = o;
        map.setOffsetX(o, allLayers);
        updateNeeded(true);
        fireOffsetChanged(null);        
    }

    public void setOffsetY(int o, boolean allLayers)
    {
        offsetY = o;
        map.setOffsetY(o, allLayers);
        updateNeeded(true);
        fireOffsetChanged(null);        
    }

    public int getOffsetX(boolean allLayers)
    {
        return map.getOffsetX(allLayers);
    }

    public int getOffsetY(boolean allLayers)
    {
        return map.getOffsetY(allLayers);
    }
    
    public String getMouseCoordinatesAsString()
    {
        int x = unscaleIt(mouseX)+getOffsetX(true);
        int y = unscaleIt(mouseY)+getOffsetY(true);
        return ""+x+"/"+y;
    }

    // most of the time this is a simple cache!
    int lastTileOnPosX=Integer.MAX_VALUE;
    int lastTileOnPosY=Integer.MAX_VALUE;
    boolean lastAnswer = false;
    // input is unscaled!
    protected boolean hasTileOnPos(int x, int y)
    {
        if ((x==lastTileOnPosX) && (lastTileOnPosY==y))
        {
            if (lastAnswer) return true;
        }
        lastTileOnPosX=x;
        lastTileOnPosY=y;
        
        Layer layer = map.mLayers.get(map.getCurrentLayerNo());
        for (int t=0; t<layer.mTiles.size(); t++)
        {
            Tile tile = layer.mTiles.get(t);
            // tile not displayed out of bounds BEFOR_DISPLAY
            if ((x==tile.getX()) && (y==tile.getY()))
            {
                lastAnswer = true;
                return true;
            }
        }
        lastAnswer = false;
        return false;
    }
    
    // input is unscaled!
    protected boolean isOverlaping(TileData tileData, int x, int y)
    {
        Tile checkTile = new Tile(tileData);
        return isOverlaping( checkTile,  x,  y);
    }

    // input is unscaled!
    protected boolean isOverlaping(Tile checkTile, int x, int y)
    {
//        Tile checkTile = new Tile(tileData);
        int minx = x;
        int miny = y;
        
        // the checkTile is NEW, it does not have any scalings set
        // therefor we must not unscale it, but rather use the unscaled data!
        int maxx = x+(checkTile.getMaxWidthUnscaled())-1;
        int maxy = y+(checkTile.getMaxHeightUnscaled())-1;

        Layer layer = map.mLayers.get(map.getCurrentLayerNo());
        for (int t=0; t<layer.mTiles.size(); t++)
        {
            Tile tile = layer.mTiles.get(t);
            // tile not displayed out of bounds BEFOR_DISPLAY

            int MAXX = tile.getX()+(tile.getMaxWidthUnscaled())-1;
            int MAXY = tile.getY()+(tile.getMaxHeightUnscaled())-1;
            int MINX = tile.getX();
            int MINY = tile.getY();


            if (MAXX < minx) continue;
            if (MAXY < miny) continue;

            // tile not displayed out of bounds AFTER_DISPLAY
            if (MINX > maxx) continue;
            if (MINY > maxy) continue;

            return true;
        }
        return false;
    }
    
    TileDataPool mTileDataPool = null;
    String poolname = "-1";

    void addTranistionTile(Layer transitionLayer, Tile originTile,int x,int y, int number)
    {
        String name = originTile.getData().getTransitionChildrenNames().elementAt(number);
        if (name.trim().length()==0)
            return; // no tranistion tile was set for this tile for this direction!
        String filename = originTile.getData().getTileSetFileName();
        if (!poolname.equals(("Tile"+File.separator+filename+".xml")))
        {
            poolname = "Tile"+File.separator+filename+".xml";
            mTileDataPool = new TileDataPool(poolname, true);
        }
        TileData tdata = mTileDataPool.getHashMap().get(name);

        if (transitionLayer.isEqualTileOnPos(tdata, x, y))
        {
            return;
        }

        Tile ntile = transitionLayer.addTile(tdata, x, y , true, null);
        ntile.setScalePercent (originTile.getScalePercent());
    }
    
    Tile addCopyTile(Layer transitionLayer, Tile originTile,int x,int y)
    {
        String name = originTile.getData().mName;
        if (name.trim().length()==0)
            return null; // no tranistion tile was set for this tile for this direction!
        String filename = originTile.getData().getTileSetFileName();
        if (!poolname.equals(("Tile"+File.separator+filename+".xml")))
        {
            poolname = "Tile"+File.separator+filename+".xml";
            mTileDataPool = new TileDataPool(poolname, true);
        }
        TileData tdata = mTileDataPool.getHashMap().get(name);

        if (transitionLayer.isEqualTileOnPos(tdata, x, y))
        {
            return null;
        }

        Tile ntile = transitionLayer.addTile(tdata, x, y , true, null);
        ntile.setScalePercent (originTile.getScalePercent());
        return ntile;
    }
    
    public void doTopDownTransition(Layer originLayer, Layer transitionLayer, int width, int height, Tile originTile, int x, int y)
    {
        int originPriority = originTile.getData().getTransitionPriority();

        // examine its surroundings

        Tile tN=originLayer.isTileOnPosition(x+1, y-1);
        Tile tS=originLayer.isTileOnPosition(x+1, y+1+height);
        Tile tW=originLayer.isTileOnPosition(x-1, y+1);
        Tile tE=originLayer.isTileOnPosition(x+1+width, y+1);
        Tile tNW=originLayer.isTileOnPosition(x-1, y-1);
        Tile tNE=originLayer.isTileOnPosition(x+1+width, y-1);
        Tile tSW=originLayer.isTileOnPosition(x-1, y+1+height);
        Tile tSE=originLayer.isTileOnPosition(x+1+width, y+1+height);

        boolean N=tN!=null&& tN.getData().getTransitionPriority()==originPriority;
        boolean S=tS!=null&& tS.getData().getTransitionPriority()==originPriority;
        boolean W=tW!=null&& tW.getData().getTransitionPriority()==originPriority;
        boolean E=tE!=null&& tE.getData().getTransitionPriority()==originPriority;
        boolean NE=tNE!=null&& tNE.getData().getTransitionPriority()==originPriority;
        boolean NW=tNW!=null&& tNW.getData().getTransitionPriority()==originPriority;
        boolean SE=tSE!=null&& tSE.getData().getTransitionPriority()==originPriority;
        boolean SW=tSW!=null&& tSW.getData().getTransitionPriority()==originPriority;

        if (SW)
        {
            if (tS != null)
                if (tS.getData().getTransitionPriority()>originPriority)
                    SW=false;
            if (tW != null)
                if (tW.getData().getTransitionPriority()>originPriority)
                    SW=false;
        }
        if (SE)
        {
            if (tS != null)
                if (tS.getData().getTransitionPriority()>originPriority)
                    SE=false;
            if (tE != null)
                if (tE.getData().getTransitionPriority()>originPriority)
                    SE=false;
        }
        if (NW)
        {
            if (tN != null)
                if (tN.getData().getTransitionPriority()>originPriority)
                    NW=false;
            if (tW != null)
                if (tW.getData().getTransitionPriority()>originPriority)
                    NW=false;
        }
        if (NE)
        {
            if (tN != null)
                if (tN.getData().getTransitionPriority()>originPriority)
                    NE=false;
            if (tE != null)
                if (tE.getData().getTransitionPriority()>originPriority)
                    NE=false;
        }

        // edges
        boolean westEdge=false;
        if (NW&&SW&&W) westEdge = true;
        boolean eastEdge=false;
        if (NE&&SE&&E) eastEdge = true;
        boolean southEdge=false;
        if (SE&&SW&&S) southEdge = true;
        boolean northEdge=false;
        if (NE&&NW&&N) northEdge = true;

        if (westEdge)  addTranistionTile(transitionLayer, originTile,x,y, 0);
        if (northEdge) addTranistionTile(transitionLayer, originTile,x,y, 1);
        if (eastEdge)  addTranistionTile(transitionLayer, originTile,x,y, 2);
        if (southEdge) addTranistionTile(transitionLayer, originTile,x,y, 3);

        // check halfling
        boolean nehEdge=false;
        if (N&&NE) nehEdge = true & (!northEdge);
        boolean nwhEdge=false;
        if (N&&NW) nwhEdge = true & (!northEdge);
        boolean sehEdge=false;
        if (S&&SE) sehEdge = true & (!southEdge);
        boolean swhEdge=false;
        if (S&&SW) swhEdge = true & (!southEdge);
        boolean enhEdge=false;
        if (E&&NE) enhEdge = true & (!eastEdge);
        boolean eshEdge=false;
        if (E&&SE) eshEdge = true & (!eastEdge);
        boolean wnhEdge=false;
        if (W&&NW) wnhEdge = true & (!westEdge);
        boolean wshEdge=false;
        if (W&&SW) wshEdge = true & (!westEdge);

        if (nehEdge)  addTranistionTile(transitionLayer, originTile,x,y, 4);
        if (nwhEdge) addTranistionTile(transitionLayer, originTile,x,y, 5);
        if (sehEdge)  addTranistionTile(transitionLayer, originTile,x,y, 6);
        if (swhEdge) addTranistionTile(transitionLayer, originTile,x,y, 7);
        if (wnhEdge)  addTranistionTile(transitionLayer, originTile,x,y, 8);
        if (wshEdge) addTranistionTile(transitionLayer, originTile,x,y, 9);
        if (enhEdge)  addTranistionTile(transitionLayer, originTile,x,y, 10);
        if (eshEdge) addTranistionTile(transitionLayer, originTile,x,y, 11);

        // check singles
        boolean se=false;
        if (SE) se = true & (!eastEdge) & (!southEdge)& (!sehEdge)& (!eshEdge);
        boolean ne=false;
        if (NE) ne = true & (!eastEdge) & (!northEdge)& (!nehEdge)& (!enhEdge);
        boolean sw=false;
        if (SW) sw = true & (!westEdge) & (!southEdge)& (!swhEdge)& (!wshEdge);
        boolean nw=false;
        if (NW) nw = true & (!westEdge) & (!northEdge)& (!nwhEdge)& (!wnhEdge);

        boolean n=false;
        if (N) n = true & (!northEdge)& (!nwhEdge)& (!nehEdge);
        boolean s=false;
        if (S) s = true & (!southEdge)& (!swhEdge)& (!sehEdge);
        boolean w=false;
        if (W) w = true & (!westEdge)& (!swhEdge)& (!nwhEdge);
        boolean e=false;
        if (E) e = true & (!eastEdge)& (!sehEdge)& (!nehEdge);

        if (sw)  addTranistionTile(transitionLayer, originTile,x,y, 12);
        if (nw)  addTranistionTile(transitionLayer, originTile,x,y, 13);
        if (se)  addTranistionTile(transitionLayer, originTile,x,y, 14);
        if (ne)  addTranistionTile(transitionLayer, originTile,x,y, 15);

        if (e)  addTranistionTile(transitionLayer, originTile,x,y, 16);
        if (w)  addTranistionTile(transitionLayer, originTile,x,y, 17);
        if (s)  addTranistionTile(transitionLayer, originTile,x,y, 18);
        if (n)  addTranistionTile(transitionLayer, originTile,x,y, 19);
    }
    public void doArtifactTransition(Layer originLayer, Layer transitionLayer, int width, int height, Tile originTile, int x, int y, boolean considerWrapping)
    {
        if (originLayer.getData().mISOBlockWrap)
        {
            doArtifactISOTransition( originLayer,  transitionLayer,  width,  height,  originTile,  x,  y,  considerWrapping);
            return;
        }
        
        int originPriority = originTile.getData().getTransitionPriority();

        // examine its surroundings
        Tile tN=originLayer.isTileOnPosition(x+1, y-1, considerWrapping);
        Tile tS=originLayer.isTileOnPosition(x+1, y+1+height, considerWrapping);
        Tile tW=originLayer.isTileOnPosition(x-1, y+1, considerWrapping);
        Tile tE=originLayer.isTileOnPosition(x+1+width, y+1, considerWrapping);
        Tile tNW=originLayer.isTileOnPosition(x-1, y-1, considerWrapping);
        Tile tNE=originLayer.isTileOnPosition(x+1+width, y-1, considerWrapping);
        Tile tSW=originLayer.isTileOnPosition(x-1, y+1+height, considerWrapping);
        Tile tSE=originLayer.isTileOnPosition(x+1+width, y+1+height, considerWrapping);

        boolean N=tN!=null&& tN.getData().getTransitionPriority()==originPriority;
        boolean S=tS!=null&& tS.getData().getTransitionPriority()==originPriority;
        boolean W=tW!=null&& tW.getData().getTransitionPriority()==originPriority;
        boolean E=tE!=null&& tE.getData().getTransitionPriority()==originPriority;
        boolean NE=tNE!=null&& tNE.getData().getTransitionPriority()==originPriority;
        boolean NW=tNW!=null&& tNW.getData().getTransitionPriority()==originPriority;
        boolean SE=tSE!=null&& tSE.getData().getTransitionPriority()==originPriority;
        boolean SW=tSW!=null&& tSW.getData().getTransitionPriority()==originPriority;

        if (SW)
        {
            if (tS != null)
                if (tS.getData().getTransitionPriority()>originPriority)
                    SW=false;
            if (tW != null)
                if (tW.getData().getTransitionPriority()>originPriority)
                    SW=false;
        }
        if (SE)
        {
            if (tS != null)
                if (tS.getData().getTransitionPriority()>originPriority)
                    SE=false;
            if (tE != null)
                if (tE.getData().getTransitionPriority()>originPriority)
                    SE=false;
        }
        if (NW)
        {
            if (tN != null)
                if (tN.getData().getTransitionPriority()>originPriority)
                    NW=false;
            if (tW != null)
                if (tW.getData().getTransitionPriority()>originPriority)
                    NW=false;
        }
        if (NE)
        {
            if (tN != null)
                if (tN.getData().getTransitionPriority()>originPriority)
                    NE=false;
            if (tE != null)
                if (tE.getData().getTransitionPriority()>originPriority)
                    NE=false;
        }

        // edges
        boolean westEdge=false;
        if (NW&&SW&&W) westEdge = true;
        boolean eastEdge=false;
        if (NE&&SE&&E) eastEdge = true;
        boolean southEdge=false;
        if (SE&&SW&&S) southEdge = true;
        boolean northEdge=false;
        if (NE&&NW&&N) northEdge = true;


        // enhanced, if only middle, than all!
        if (W) westEdge = true;
        if (E) eastEdge = true;
        if (S) southEdge = true;
        if (N) northEdge = true;

        // build a binary representation edge and corners...
        int edgeTileNumber = 0;
        if (westEdge) edgeTileNumber += 1;
        if (northEdge) edgeTileNumber += 2;
        if (eastEdge) edgeTileNumber += 4;
        if (southEdge) edgeTileNumber += 8;

        // corner west
        boolean NECorner=false;
        if (!northEdge)
            if (!eastEdge)
                if (NE) NECorner = true;

        boolean SECorner=false;
        if (!southEdge)
            if (!eastEdge)
                if (SE) SECorner = true;

        boolean NWCorner=false;

        if (!northEdge)
            if (!westEdge)
                if (NW) NWCorner = true;

        boolean SWCorner=false;
        if (!southEdge)
            if (!westEdge)
                if (SW) SWCorner = true;

        // build a binary representation edge and corners...
        int cornerTileNumber = 0;
        if (NWCorner) cornerTileNumber += 1;
        if (NECorner) cornerTileNumber += 2;
        if (SECorner) cornerTileNumber += 4;
        if (SWCorner) cornerTileNumber += 8;

        if (edgeTileNumber!=0) // add edge
        {
            addTranistionTile(transitionLayer, originTile,x,y, edgeTileNumber);
        }
        if (cornerTileNumber!=0) // add corner+16
        {
            cornerTileNumber+=16;
            addTranistionTile(transitionLayer, originTile,x,y, cornerTileNumber);
        }
    }

    // examines the given position according to its surroundings (originLayer)
    // and adds an appropriate transitiontile to position x,y  defined by origin Tile
    // surroundings ate examined with a tilings based on given width and height
    public void doTransition(Layer originLayer, Layer transitionLayer, int width, int height, Tile originTile, int x, int y, int type, boolean considerWrapping)
    {
        if (type == 1)  // artifact
        {
            if (originTile.getData().getTransitionChildrenNames().size()!=32) return;
            doArtifactTransition(originLayer, transitionLayer, width, height, originTile, x, y, considerWrapping);
        }
        else if (type == 3)  // artifact (ISO)
        {
            if (originTile.getData().getTransitionChildrenNames().size()!=32) return;
            doArtifactTransition(originLayer, transitionLayer, width, height, originTile, x, y, considerWrapping);
        }
        else if (type == 2) // top Down
        {
            if (originTile.getData().getTransitionChildrenNames().size()!=20) return;
            doTopDownTransition(originLayer, transitionLayer, width, height, originTile, x, y);
        }
    }
    // in order to do priorities of transitions correctly,
    // we do the transitions PER priority
    public void buildTransitions(Layer fromLayer, Layer transitionLayer, boolean considerWrapping)
    {
        HashMap<Integer, Integer> priorities = new HashMap<Integer, Integer>();

        if ( (!(fromLayer.getData().mWrapHorizontal)) && (!(fromLayer.getData().mWrapVertical)))
            considerWrapping = false;
        
        for (int t=0; t<fromLayer.mTiles.size(); t++)
        {
            Tile tile = fromLayer.mTiles.get(t);
            if (tile.getData().getIsTransitionSource())
                priorities.put(tile.getData().getTransitionPriority(), tile.getData().getTransitionPriority() );
        }
        Vector<Integer> vpriorities = new Vector<Integer>();
        Set entries = priorities.entrySet();
        Iterator it = entries.iterator();
        while (it.hasNext())
        {
                Map.Entry entry = (Map.Entry) it.next();
                Integer value = (Integer) entry.getValue();
                vpriorities.addElement(value);
        }
       Collections.sort(vpriorities, new Comparator<Integer>() {@Override public int compare(Integer s1, Integer s2) { return s1 - s2; } });

       for (int i=0; i<vpriorities.size(); i++)
       {
           if (fromLayer.getData().mISOBlockWrap)
           {
                buildISOTransitions(fromLayer, transitionLayer, vpriorities.elementAt(i), considerWrapping);
           }
           else
           {
                buildTransitions(fromLayer, transitionLayer, vpriorities.elementAt(i), considerWrapping);
           }
       }
      
       // if resulting layer is smaller than the original layer
       // copy a tile to the maximum position
       // that way the view does not change but the size is corrected....
       // actually the view MAY
       // change, if the tile in the spot has a low priority and 
       // should have been overwritten by 
       // a transition
       // - can be solved
       // - using a dummy invsisible tile (not done)
       // - quick resolution... add the new tiles FIRST
       // - thus they will be overdrawn -> result is an IRREGULAR map
       //   and if ordered -> the resulting map may not be a correct transition
       // map anmyore -> I dont bother
       
       if (considerWrapping)
       {
           if (transitionLayer.mTiles.size()>0)
           {
                Tile t1new = null;
                Tile tLastnew = null;
                if ((fromLayer.totalWidth != transitionLayer.getLayerWidth())
                    ||(fromLayer.totalHeight != transitionLayer.getLayerHeight()))
                {
                    transitionLayer.makeRegular(); // otherwise the new tile with lowest coordinates
                    //correct sizing -> we COPY tiles to the transition layer to fill it up!
                    Tile t1Org = fromLayer.mTiles.get(0);
                    t1new = transitionLayer.mTiles.get(0);
                    if ((t1Org.getX() != t1new.getX()) || (t1Org.getY() != t1new.getY()))
                    {
                         // add first tile
                         t1new = addCopyTile( transitionLayer, t1Org,t1Org.getX(),t1Org.getY());
                         transitionLayer.makeRegular(); // otherwise the new tile with lowest coordinates
                         // will be the last tile -> next if will allways be active!
                    }
                    else
                        t1new = null;

                    Tile tLastOrg = fromLayer.mTiles.get(fromLayer.mTiles.size()-1);
                    tLastnew = transitionLayer.mTiles.get(transitionLayer.mTiles.size()-1);
                    if ((tLastOrg.getX() != tLastnew.getX()) || (tLastOrg.getY() != tLastnew.getY()))
                    {
                         //add last tile
                         tLastnew = addCopyTile( transitionLayer, tLastOrg,tLastOrg.getX(),tLastOrg.getY());
                    }
                        else tLastnew = null;
                    
                    // see comment above!
                    transitionLayer.moveToFront(t1new);
                    transitionLayer.moveToFront(tLastnew);
                }               
           }

       }
       transitionLayer.getData().mISOBlockWrap = fromLayer.getData().mISOBlockWrap;
       transitionLayer.getData().monlyOneTileOnOnePosition = fromLayer.getData().monlyOneTileOnOnePosition;
       transitionLayer.getData().mWrapHorizontal = fromLayer.getData().mWrapHorizontal;
       transitionLayer.getData().mWrapVertical = fromLayer.getData().mWrapVertical;
       transitionLayer.getData().mallowTileOverlap = fromLayer.getData().mallowTileOverlap;
       transitionLayer.doCalcDefaults();

       updateNeeded(true);
       setMapChanged(true, true);
       
    }

    public void buildTransitions(Layer fromLayer, Layer transitionLayer, int currentPriority, boolean considerWrapping)
    {
        transitionLayer.getData().moffsetX = fromLayer.getData().moffsetX;
        transitionLayer.getData().moffsetY = fromLayer.getData().moffsetY;

        
        for (int t=0; t<fromLayer.mTiles.size(); t++)
        {
            Tile tile = fromLayer.mTiles.get(t);

            if (tile.getData().getTransitionPriority() != currentPriority)
            {
                continue;
            }

            if (tile.getData().getIsTransitionSource())
            {
                int x = tile.getX();
                int y = tile.getY();
                int width = tile.getMaxWidthUnscaled();
                int height = tile.getMaxHeightUnscaled();

                Tile tN=fromLayer.has_N_Neighbour(tile, considerWrapping);
                Tile tS=fromLayer.has_S_Neighbour(tile, considerWrapping);
                Tile tW=fromLayer.has_W_Neighbour(tile, considerWrapping);
                Tile tE=fromLayer.has_E_Neighbour(tile, considerWrapping);
                Tile tNW=fromLayer.has_NW_Neighbour(tile, considerWrapping);
                Tile tNE=fromLayer.has_NE_Neighbour(tile, considerWrapping);
                Tile tSW=fromLayer.has_SW_Neighbour(tile, considerWrapping);
                Tile tSE=fromLayer.has_SE_Neighbour(tile, considerWrapping);

                boolean N=(tN!=null) && tN.getData().getTransitionPriority()>=currentPriority;
                boolean S=tS!=null&& tS.getData().getTransitionPriority()>=currentPriority;
                boolean W=tW!=null&& tW.getData().getTransitionPriority()>=currentPriority;
                boolean E=tE!=null&& tE.getData().getTransitionPriority()>=currentPriority;
                boolean NW=tNW!=null&& tNW.getData().getTransitionPriority()>=currentPriority;
                boolean NE=tNE!=null&& tNE.getData().getTransitionPriority()>=currentPriority;
                boolean SW=tSW!=null&& tSW.getData().getTransitionPriority()>=currentPriority;
                boolean SE=tSE!=null&& tSE.getData().getTransitionPriority()>=currentPriority;

                int xEastWrapAdd=0;
                int yNorthWrapAdd=0;
                int xWestWrapAdd=0;
                int ySouthWrapAdd=0;
                if ((considerWrapping) && (fromLayer.isBorderTileNorth(tile))) 
                {
                    if (fromLayer.getData().mWrapVertical)
                        yNorthWrapAdd = fromLayer.getLayerHeight();
                }
                if ((considerWrapping) && (fromLayer.isBorderTileSouth(tile))) 
                {
                    if (fromLayer.getData().mWrapVertical)
                        ySouthWrapAdd = - fromLayer.getLayerHeight();
                }
                if ((considerWrapping) && (fromLayer.isBorderTileWest(tile))) 
                {
                    if (fromLayer.getData().mWrapHorizontal)
                        xWestWrapAdd = fromLayer.getLayerWidth();
                }
                if ((considerWrapping) && (fromLayer.isBorderTileEast(tile))) 
                {
                    if (fromLayer.getData().mWrapHorizontal)
                        xEastWrapAdd = -fromLayer.getLayerWidth();
                }
                if (!N)
                {
                    doTransition(fromLayer, transitionLayer, width, height, tile, x, y-height+yNorthWrapAdd, tile.getData().getTransitionType(), considerWrapping);
                }
                if (!S)
                {
                    doTransition(fromLayer, transitionLayer, width, height, tile, x, y+height+ySouthWrapAdd, tile.getData().getTransitionType(), considerWrapping);
                }
                if (!W)
                {
                    doTransition(fromLayer, transitionLayer, width, height, tile, x-width+xWestWrapAdd, y, tile.getData().getTransitionType(), considerWrapping);
                }
                if (!E)
                {
                    doTransition(fromLayer, transitionLayer, width, height, tile, x+width+xEastWrapAdd, y, tile.getData().getTransitionType(), considerWrapping);
                }
                if (!NE)
                {
                    doTransition(fromLayer, transitionLayer, width, height, tile, x+width+xEastWrapAdd, y-height+yNorthWrapAdd, tile.getData().getTransitionType(), considerWrapping);
                }
                if (!NW)
                {
                    doTransition(fromLayer, transitionLayer, width, height, tile, x-width+xWestWrapAdd, y-height+yNorthWrapAdd, tile.getData().getTransitionType(), considerWrapping);
                }
                if (!SE)
                {
                    doTransition(fromLayer, transitionLayer, width, height, tile, x+width+xEastWrapAdd, y+height+ySouthWrapAdd, tile.getData().getTransitionType(), considerWrapping);
                }
                if (!SW)
                {
                    doTransition(fromLayer, transitionLayer, width, height, tile, x-width+xWestWrapAdd, y+height+ySouthWrapAdd, tile.getData().getTransitionType(), considerWrapping);
                }
            }
        }
        updateNeeded(true);
        setMapChanged(true, true);
    }
    
    public int scaleToFit()
    {
        int width = this.getWidth();
        int height = this.getHeight();
        int minTileWidth = map.getMinTileWidth();
        int minTileHeight = map.getMinTileHeight();
        int maxMapWidth = map.getMapWidth();
        int maxMapHeight = map.getMapHeight();

        double scaleX = (double)(((double)width)/((double)maxMapWidth));
        double scaleY = (double)(((double)height)/((double)maxMapHeight));

        double scale;
        if (scaleX>scaleY) scale = scaleY; else scale = scaleX;

        if (scale * minTileWidth < 0)
        {
            // 0 sized tiles
            System.out.println("Underscale - error");
            return -1;
        }
        if (scale * minTileHeight < 0)
        {
            // 0 sized tiles
            System.out.println("Underscale - error");
            return -1;
        }
        int scaleP = (int)(scale*100);
        setScalingPercent(scaleP);

        setOffsetX(map.getMinX(), true);
        setOffsetY(map.getMinY(), true);

        return scaleP;
    }

    public BufferedImage getScaleImage(int width, int height)
    {
        // int width = this.getWidth();
        // int height = this.getHeight();
        int minTileWidth = map.getMinTileWidth();
        int minTileHeight = map.getMinTileHeight();
        int maxMapWidth = map.getMapWidth();
        int maxMapHeight = map.getMapHeight();

        double scaleX = (double)(((double)width)/((double)maxMapWidth));
        double scaleY = (double)(((double)height)/((double)maxMapHeight));

        
        int min;
        double adder =0.01;
        if (minTileWidth>minTileHeight) min = minTileHeight; else min = minTileWidth;
        double maxOneScale = (double)(((double)1)/((double)min));
        maxOneScale = (maxOneScale+adder)*100;
        
        //adder =0 manchmal, wenn scaling is up.
        adder=0;
        int scaleP;
        if (scaleX>scaleY) 
            scaleP = (int)((scaleY+adder)*100);
        else
            scaleP = (int)((scaleX+adder)*100);

        if (scaleP<maxOneScale)
            scaleP = (int)(maxOneScale);
        
        int saveScale = scale;
        int saveOffsetX =offsetX;
        int saveOffsetY =offsetY;
        boolean doPaintSave = doPaint;
        
        setScalingPercent(scaleP);
        setOffsetX(map.getMinX(), true);
        setOffsetY(map.getMinY(), true);

        int paintWidth = (maxMapWidth * scale)/100;
        int paintHeight = (maxMapHeight * scale)/100;
        
        if (paintWidth<=0) return null;
        if (paintHeight<=0) return null;
        
        doPaint = true;
        
        
        // you can create an image only on a visible entity!
        Image img = Global.mMainWindow.createImage(paintWidth, paintHeight);
        if (img == null) return null;
        Graphics g = img.getGraphics();
        
        
        //bounds must not be smaller than smallest scale!
        // otherwise the paintlist will not be generated correctly!
        setBounds(0, 0, paintWidth, paintHeight);
        paintComponent(g, false);
        setBounds(0, 0, width, height);
        doPaint = doPaintSave;

        setScalingPercent(saveScale);
        setOffsetX(saveOffsetX, true);
        setOffsetY(saveOffsetY, true);

        BufferedImage bimg = csa.util.UtilityImage.toBufferedImage(img);
        
        if (bimg.getWidth() > width)
        {
            double widthScale = (double)(((double)width)/((double)bimg.getWidth()));
            int widthScaleX = (int) (bimg.getWidth()*widthScale);
            int heightScaleX = (int) (bimg.getHeight()*widthScale);
            bimg = csa.util.UtilityImage.imageScale(bimg, widthScaleX, heightScaleX);
        }
            
        if (bimg.getHeight() > height)
        {
            double heightScale = (double)(((double)height)/((double)bimg.getHeight()));
            int widthScaleY = (int) (bimg.getWidth()*heightScale);
            int heightScaleY = (int) (bimg.getHeight()*heightScale);
            bimg = csa.util.UtilityImage.imageScale(bimg, widthScaleY, heightScaleY);
        }
        return bimg;
    }
    
    // ISO is handled by Layer internally if mISOBlockWrap  is set correctly!
    public void buildISOTransitions(Layer fromLayer, Layer transitionLayer, int currentPriority, boolean considerWrapping)
    {
        transitionLayer.getData().moffsetX = fromLayer.getData().moffsetX;
        transitionLayer.getData().moffsetY = fromLayer.getData().moffsetY;

        
        for (int t=0; t<fromLayer.mTiles.size(); t++)
        {
            Tile tile = fromLayer.mTiles.get(t);

            if (tile.getData().getTransitionPriority() != currentPriority)
            {
                continue;
            }

            if (tile.getData().getIsTransitionSource())
            {
                int x = tile.getX();
                int y = tile.getY();
                    
                    
                int width = tile.getMaxWidthUnscaled();
                int height = tile.getMaxHeightUnscaled();
                
                Tile tN=fromLayer.has_N_Neighbour(tile, considerWrapping);
                Tile tS=fromLayer.has_S_Neighbour(tile, considerWrapping);
                Tile tW=fromLayer.has_W_Neighbour(tile, considerWrapping);
                Tile tE=fromLayer.has_E_Neighbour(tile, considerWrapping);
                Tile tNW=fromLayer.has_NW_Neighbour(tile, considerWrapping);
                Tile tNE=fromLayer.has_NE_Neighbour(tile, considerWrapping);
                Tile tSW=fromLayer.has_SW_Neighbour(tile, considerWrapping);
                Tile tSE=fromLayer.has_SE_Neighbour(tile, considerWrapping);

                boolean N=tN!=null&& tN.getData().getTransitionPriority()>=currentPriority;
                boolean S=tS!=null&& tS.getData().getTransitionPriority()>=currentPriority;
                boolean W=tW!=null&& tW.getData().getTransitionPriority()>=currentPriority;
                boolean E=tE!=null&& tE.getData().getTransitionPriority()>=currentPriority;
                boolean NW=tNW!=null&& tNW.getData().getTransitionPriority()>=currentPriority;
                boolean NE=tNE!=null&& tNE.getData().getTransitionPriority()>=currentPriority;
                boolean SW=tSW!=null&& tSW.getData().getTransitionPriority()>=currentPriority;
                boolean SE=tSE!=null&& tSE.getData().getTransitionPriority()>=currentPriority;

                int xEWrapAdd=0;
                int yEWrapAdd=0;

                int xWWrapAdd=0;
                int yWWrapAdd=0;

                int xNWrapAdd=0;
                int yNWrapAdd=0;

                int xNEWrapAdd=0;
                int yNEWrapAdd=0;
                
                int xNWWrapAdd=0;
                int yNWWrapAdd=0;

                int xSWrapAdd=0;
                int ySWrapAdd=0;

                int xSEWrapAdd=0;
                int ySEWrapAdd=0;

                int xSWWrapAdd=0;
                int ySWWrapAdd=0;
                
                if (considerWrapping)
                {
                    boolean nBorder = fromLayer.isBorderTileNorth(tile);
                    boolean sBorder = fromLayer.isBorderTileSouth(tile);
                    boolean eBorder = fromLayer.isBorderTileEast(tile);
                    boolean wBorder = fromLayer.isBorderTileWest(tile);

                    // check the border we are wrapping from
                    // calculate offset to the positions for wrapping the found direction
                    
                    if (nBorder)
                    {
                        if (eBorder)
                        {
                            // NE
                            if (tile.getX() == fromLayer.totalWidth - tile.getMaxWidthUnscaled())
                                xNEWrapAdd =-fromLayer.getLayerWidth()+width/2;
                            if (tile.getY() == 0)
                                yNEWrapAdd = fromLayer.getLayerHeight()-height/2;
                        }
                        if (wBorder)
                        {
                            // NW
                            if (tile.getX() == 0)
                                xNWWrapAdd = fromLayer.getLayerWidth()-width/2;
                            if (tile.getY() == 0)
                                yNWWrapAdd = fromLayer.getLayerHeight()-height/2;
                        }
                        {
                            // N
                            yNWrapAdd = fromLayer.getLayerHeight()-height/2;
                            if (tile.getY() == 0)
                            {
                                yNEWrapAdd = fromLayer.getLayerHeight()-height/2;
                                yNWWrapAdd = fromLayer.getLayerHeight()-height/2;
                            }
                        }
                    }
                    if (sBorder)
                    {
                        if (eBorder)
                        {
                            // SE
                            if (tile.getX() == fromLayer.totalWidth - tile.getMaxWidthUnscaled())
                                xSEWrapAdd = -fromLayer.getLayerWidth()+width/2;
                            if (tile.getY() == fromLayer.totalHeight - tile.getMaxHeightUnscaled())
                                ySEWrapAdd = -fromLayer.getLayerHeight()+height/2;
                        }
                        if (wBorder)
                        {
                            // SW
                            if (tile.getX() == 0)
                                xSWWrapAdd = fromLayer.getLayerWidth()-width/2;
                            if (tile.getY() == fromLayer.totalHeight - tile.getMaxHeightUnscaled())
                                ySWWrapAdd = -fromLayer.getLayerHeight()+height/2;
                        }
                        {
                            // S
                            ySWrapAdd = -fromLayer.getLayerHeight()+height/2;
                            if (tile.getY() == fromLayer.totalHeight - tile.getMaxHeightUnscaled())
                            {
                                ySEWrapAdd = -fromLayer.getLayerHeight()+height/2;
                                ySWWrapAdd = -fromLayer.getLayerHeight()+height/2;
                            }
                        }
                    }
                    if (eBorder) 
                    {
                        // E
                        xEWrapAdd=-fromLayer.getLayerWidth()+width/2;
                        if (tile.getX() == fromLayer.totalWidth - tile.getMaxWidthUnscaled())
                        {
                            xSEWrapAdd = -fromLayer.getLayerWidth()+width/2;
                            xNEWrapAdd = -fromLayer.getLayerWidth()+width/2;
                            
                        }
                    }
                    if (wBorder) 
                    {
                        // W
                        xWWrapAdd=fromLayer.getLayerWidth()-width/2;
                        if (tile.getX() == 0)
                        {
                            xNWWrapAdd = fromLayer.getLayerWidth()-width/2;
                            xSWWrapAdd = fromLayer.getLayerWidth()-width/2;
                        }
                    }
                    
                    
                }
                
                
                // ISO Wrapping NOT done yet!
                // if wrapping is enabled -> strange results might occur!
                
                // do Transitions x,y Pos is the pos, on which the new tile will be placed!
                
                if (!N)
                {
                    doTransition(fromLayer, transitionLayer, width, height, tile, x+xNWrapAdd, y-height+yNWrapAdd, tile.getData().getTransitionType(), considerWrapping);
                }
                if (!S)
                {
                    doTransition(fromLayer, transitionLayer, width, height, tile, x+xSWrapAdd, y+height+ySWrapAdd, tile.getData().getTransitionType(), considerWrapping);
                }
                if (!W)
                {
                    doTransition(fromLayer, transitionLayer, width, height, tile, x-width+xWWrapAdd, y+yWWrapAdd, tile.getData().getTransitionType(), considerWrapping);
                }
                if (!E)
                {
                    doTransition(fromLayer, transitionLayer, width, height, tile, x+width+xEWrapAdd, y+yEWrapAdd, tile.getData().getTransitionType(), considerWrapping);
                }
                if (!NE)
                {
                    doTransition(fromLayer, transitionLayer, width, height, tile, x+width/2+xNEWrapAdd, y-height/2+yNEWrapAdd, tile.getData().getTransitionType(), considerWrapping);
                }
                if (!NW)
                {
                    doTransition(fromLayer, transitionLayer, width, height, tile, x-width/2+xNWWrapAdd, y-height/2+yNWWrapAdd, tile.getData().getTransitionType(), considerWrapping);
                }
                if (!SE)
                {
                    doTransition(fromLayer, transitionLayer, width, height, tile, x+width/2+xSEWrapAdd, y+height/2+ySEWrapAdd, tile.getData().getTransitionType(), considerWrapping);
                }
                if (!SW)
                {
                    doTransition(fromLayer, transitionLayer, width, height, tile, x-width/2+xSWWrapAdd, y+height/2+ySWWrapAdd, tile.getData().getTransitionType(), considerWrapping);
                }
            }
        }
        updateNeeded(true);
        setMapChanged(true, true);
    }    

    public void doArtifactISOTransition(Layer originLayer, Layer transitionLayer, int width, int height, Tile originTile, int x, int y, boolean considerWrapping)
    {
        int originPriority = originTile.getData().getTransitionPriority();

        // examine its surroundings
        Tile tN=originLayer.isTileOnPosition(x+width/2, y-height/2, considerWrapping);
        Tile tS=originLayer.isTileOnPosition(x+width/2, y+height/2+height, considerWrapping);
        Tile tW=originLayer.isTileOnPosition(x-width/2, y+height/2, considerWrapping);
        Tile tE=originLayer.isTileOnPosition(x+width+width/2, y+height/2, considerWrapping);
        Tile tNW=originLayer.isTileOnPosition(x, y, considerWrapping);
        Tile tNE=originLayer.isTileOnPosition(x+width, y, considerWrapping);
        Tile tSW=originLayer.isTileOnPosition(x, y+height+2, considerWrapping);
        Tile tSE=originLayer.isTileOnPosition(x+width, y+2+height, considerWrapping);

        boolean N=tN!=null&& tN.getData().getTransitionPriority()==originPriority;
        boolean S=tS!=null&& tS.getData().getTransitionPriority()==originPriority;
        boolean W=tW!=null&& tW.getData().getTransitionPriority()==originPriority;
        boolean E=tE!=null&& tE.getData().getTransitionPriority()==originPriority;
        boolean NE=tNE!=null&& tNE.getData().getTransitionPriority()==originPriority;
        boolean NW=tNW!=null&& tNW.getData().getTransitionPriority()==originPriority;
        boolean SE=tSE!=null&& tSE.getData().getTransitionPriority()==originPriority;
        boolean SW=tSW!=null&& tSW.getData().getTransitionPriority()==originPriority;

     
        if (W)
        {
            if (tNW != null)
                if (tNW.getData().getTransitionPriority()>originPriority)
                    W=false;
            if (tSW != null)
                if (tSW.getData().getTransitionPriority()>originPriority)
                    W=false;
        }
        if (E)
        {
            if (tNE != null)
                if (tNE.getData().getTransitionPriority()>originPriority)
                    E=false;
            if (tSE != null)
                if (tSE.getData().getTransitionPriority()>originPriority)
                    E=false;
        }
        if (S)
        {
            if (tSE != null)
                if (tSE.getData().getTransitionPriority()>originPriority)
                    S=false;
            if (tSW != null)
                if (tSW.getData().getTransitionPriority()>originPriority)
                    S=false;
        }
        if (N)
        {
            if (tNE != null)
                if (tNE.getData().getTransitionPriority()>originPriority)
                    N=false;
            if (tNW != null)
                if (tNW.getData().getTransitionPriority()>originPriority)
                    N=false;
        }

        
        
        // Ecke
        boolean NWKante=false;
        if (NW&&N&&W) NWKante = true;
        boolean NEKante=false;
        if (NE&&N&&E) NEKante = true;
        boolean SEKante=false;
        if (SE&&S&&E) SEKante = true;
        boolean SWKante=false;
        if (SW&&W&&S) SWKante = true;


        // enhanced, if only middle, than all!
        if (SW) SWKante = true;
        if (SE) SEKante = true;
        if (NE) NEKante = true;
        if (NW) NWKante = true;

        
        
        
        // build a binary representation edge and kantes...
        int kanteTileNumber = 0;
        if (NWKante) kanteTileNumber += 1;
        if (NEKante) kanteTileNumber += 2;
        if (SEKante) kanteTileNumber += 4;
        if (SWKante) kanteTileNumber += 8;

        // kante north
        boolean northEcke=false;
        if (!NEKante)
            if (!NWKante)
                if (N) northEcke = true;

        boolean westEcke=false;
        if (!NWKante)
            if (!SWKante)
                if (W) westEcke = true;

        boolean eastEcke=false;

        if (!NEKante)
            if (!SEKante)
                if (E) eastEcke = true;

        boolean southEcke=false;
        if (!SEKante)
            if (!SWKante)
                if (S) southEcke = true;

        // build a binary representation edge and kantes...
        int eckeTileNumber = 0;
        if (westEcke) eckeTileNumber += 8;
        if (northEcke) eckeTileNumber += 1;
        if (eastEcke) eckeTileNumber += 2;
        if (southEcke) eckeTileNumber += 4;

        if (eckeTileNumber!=0) // add Ecke
        {
            
            eckeTileNumber+=16;
            addTranistionTile(transitionLayer, originTile,x,y, eckeTileNumber);
        }
        if (kanteTileNumber!=0) // add kante+16
        {
            addTranistionTile(transitionLayer, originTile,x,y, kanteTileNumber);
        }
    }    
    
    WPanel naviPanel = null;
    boolean navigationAcitve = false;
    int lastUsedSizeWidth = 0;
    int lastUsedSizeHeight = 0;

    public void unsetNaviPanel()
    {
        // un init navipanel
        // remove listeners etc
        // todo
        navigationAcitve = false;
        if(naviPanel==null) return;
        
        SingleImagePanel ipanel = (SingleImagePanel) naviPanel.getComponent(0);
        ipanel.removeAllListeners();
        
        naviPanel=null;
        
        
    }
    
    class WPanel extends JPanel implements Windowable
    {
        private CSAView mParent = null;
        private javax.swing.JMenuItem mParentMenuItem = null;
        
        int sizeW = 0;
        int sizeH = 0;
        
        @Override
        public void setSize(int width, int height)
        {
            super.setSize(width, height);
            sizeW = width;
            sizeH = height;
        }
        
        @Override
        public void closing()
        {
            unsetNaviPanel();
        }
        @Override
        public void setParentWindow(CSAView jpv)
        {
            mParent = jpv;
        }
        @Override
        public void setMenuItem(javax.swing.JMenuItem item)
        {
            mParentMenuItem = item;
            mParentMenuItem.setText("Navi");
        }
        @Override
        public javax.swing.JMenuItem getMenuItem()
        {
            return mParentMenuItem;
        }
        @Override
        public javax.swing.JPanel getPanel()
        {
            return this;
        }        
    }
    
    // only one allowed!
    // if already initialized null is returned!
    // sets up a new navi window
    // and registeres listeners etc
    public Windowable getNaviPanel(int width, int height)
    {
        // only one allowed!
        if (naviPanel != null) return null;
        naviPanel = new WPanel();
        naviPanel.setLayout(null);

        SingleImagePanel ipanel = new SingleImagePanel();
        ipanel.setDrawCheckers(false);
        ipanel.setCrossDrawn(false);
        
        ipanel.addMouseMovedListener(new MovedListener() {
        
            public void moved(MouseEvent evt)
            {
                if (!( (evt.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK))
                    return;
//                    if (evt.getButton() == 0) return;
                //System.out.println("Moving " + evt.getLocationOnScreen());
                updateNaviViewport(evt);
            }
        });
        
        ipanel.addClickListener(new ClickListener() {
        
            public void clicked(MouseEvent evt)
            {
                updateNaviViewport(evt);
            }
        });
        
        
        naviPanel.add(ipanel);

        navigationAcitve = true;
        resizedNaviWindow(width, height);
        
        return naviPanel;
    }

    // accessed from mouse movement on navi window
    // changes the offset of the map 
    // according to selected "viewport" in navi window
    private void updateNaviViewport(MouseEvent evt)
    {
        if ((naviPanel==null)||(!navigationAcitve)) return;
        SingleImagePanel ipanel = (SingleImagePanel) naviPanel.getComponent(0);

        int mX=evt.getX();
        int mY=evt.getY();
        Dimension d = ipanel.getSelectionLock();
        
        int mHeight = map.getMapHeight();
        int mWidth = map.getMapWidth();

        float heightScaleUnscaled = (float) ((float)(naviPanel.getHeight())/(float)((mHeight)));
        float widthScaleUnscaled = (float) ((float)(naviPanel.getWidth())/(float)((mWidth)));
        
        int pWidth = ipanel.getWidth();
        int pHeight = ipanel.getHeight();
        
        int newOffsetX = (int) ((mX-(widthScaleUnscaled-1))/widthScaleUnscaled);
        int newOffsetY = (int) ((mY-(heightScaleUnscaled-1))/heightScaleUnscaled);
        
        newOffsetX += map.getMinX();
        newOffsetY += map.getMinY();
        
        setOffsetX(newOffsetX, true);
        setOffsetY(newOffsetY, true);
        
       
    }
    
    // the size of the navi area changed -> so get a new map image
    // in the new correct scale ratio
    public void resizedNaviWindow(int width, int height)
    {
        if ((naviPanel==null)||(!navigationAcitve)) return;

        lastUsedSizeWidth = width;
        lastUsedSizeHeight = height;
        navigationAcitve = false;
        SingleImagePanel ipanel = (SingleImagePanel) naviPanel.getComponent(0);
        
        
        BufferedImage image = getScaleImage(width, height);
        
        width =image.getWidth();
        height =image.getHeight();

        naviPanel.setSize(width, height);
        naviPanel.setPreferredSize(new Dimension(width, height));
        naviPanel.setMaximumSize(new Dimension(width, height));
        naviPanel.setMinimumSize(new Dimension(width, height));
        
        ipanel.setBounds(0,0,width,height);
        ipanel.setImage(image);
        navigationAcitve = true;
        updateNaviPosition(false);
    }
    
    
    // positioning / offset has changed
    // -> draws new rectangle in navi window
    // if new image is needed (because map has changed (new tile/ tile deleted...)
    // a new image is created and set to navi window to reflect the changes
    // updating the navi window with each
    // change might in editing mode be quite time consuming and
    // result in lags
    private void updateNaviPosition(boolean newImageNeeded)
    {
        if ((naviPanel==null)||(!navigationAcitve)) return;
        if (newImageNeeded)
        {
            resizedNaviWindow(lastUsedSizeWidth, lastUsedSizeHeight);
            return;
        }
        // calculate Map size
        // viewport size
        // -> set lock - selection to scaled viewport size
        
        
        // absolut map heights
        int mHeight = map.getMapHeight();
        int mWidth = map.getMapWidth();
        
        
        
        float heightScale = (float) ((float)(naviPanel.getHeight())/(float)(scaleIt(mHeight)));
        float widthScale = (float) ((float)(naviPanel.getWidth())/(float)(scaleIt(mWidth)));

        float heightScaleUnscaled = (float) ((float)(naviPanel.getHeight())/(float)((mHeight)));
        float widthScaleUnscaled = (float) ((float)(naviPanel.getWidth())/(float)((mWidth)));
        
        lastPaintScale = scale;
        
        
        int viewportScaledWidth = (int) (visibleWidth * widthScale);
        int viewportScaledheight = (int) (visibleHeight * heightScale);
        if (naviPanel.getComponentCount()==0) 
            return;
        
        SingleImagePanel ipanel = (SingleImagePanel) naviPanel.getComponent(0);
        ipanel.setSelectionLock(true, viewportScaledWidth, viewportScaledheight);

        
        int usedOffsetX = offsetX - map.getMinX();
        int usedOffsetY = offsetY - map.getMinY();
        
        int scaledOffsetX = (int) (usedOffsetX * widthScaleUnscaled);
        int scaledOffsetY = (int) (usedOffsetY * heightScaleUnscaled);
        ipanel.setSelection(scaledOffsetX, scaledOffsetY, viewportScaledWidth, viewportScaledheight);
        
        CSAMainFrame frame = (CSAMainFrame) Global.mMainWindow;
        
        // needed on my MAC
        // otherwise the internal frames are messed up :(
        // did not try on windows!
        frame.correctGarbledDisplay();
    }
    
    
    public void setSpriteEditMode(boolean b)
    {
        mInSpriteEditMode = b;
        if (!b)
            selectSprite = -1;
        updateNeeded(true);
    }
    
    public boolean isSpriteSelected()
    {
        return selectSprite!=-1;
    }
    
    public int getSelectedSpriteNo()
    {
        return selectSprite;
    }
    public int getSelectedSpriteLayerNo()
    {
        return selectLayer;
    }
    
    Tile checkTile(Tile tile ,String fogID)
    {
        if (tile != null)
        {
            if (tile.isEmptyTile()) 
                tile = null;
            else
            {
                String tileID = tile.getData().getTileSetFileName()+"_"+tile.getData().getName();
                if (fogID.equals(tileID)) tile=null; // if tile is solid, there is nothing to transition!
            }
        }
        return tile;
    }
    
    
    public void liftFog(int x, int y, int radius)
    {
        Tile source = map.getCurrentLayer().isTileOnPosition( x,  y,  true);//true);
        if (source ==null) return;
        if (radius == 1)
        {
            liftFog(x, y);
            return;
        }

        int width = source.getMaxWidthUnscaled();
        int height = source.getMaxHeightUnscaled();

        int offsetMul = radius-1;
        int ny=y-offsetMul*height;
        
        for (; ny<=y+offsetMul*height; )
        {
            int nx=x-offsetMul*width;
            for (; nx<=x+offsetMul*width; )
            {
                liftFog(nx, ny);
                nx+=width;
            }
            ny+=height;
        }
    }
    // input is hotspot
    // unscaled and and in map coordinates
    public void liftFog(int x, int y)
    {
        // assuming current layer is fog layer!
        
        
        Tile source = map.getCurrentLayer().isTileOnPosition( x,  y,  true);//true);
        if (source == null) return;
        if (source.isEmptyTile()) return;
        
        String fogID = map.getCurrentLayer().getData().getFogFileName()+"_"+map.getCurrentLayer().getData().getFogName();
        String tileID = source.getData().getTileSetFileName()+"_"+source.getData().getName();
        
        // only fog tiles can be "lifted"
        if (!fogID.equals(tileID)) return;
        
        int parentPrio = source.getData().getTransitionPriority();
        
        Tile tile = new EmptyTile(source.getMaxWidthUnscaled(), source.getMaxHeightUnscaled());
        String tiles = tile.toString();
        map.getCurrentLayer().replaceTileAt(source, tile, false, true);
        
        // first step -> only supported Artifact transition, no ISO
        if (map.getCurrentLayer().getData().mISOBlockWrap)
        {
            ShowInfoDialog.showInfoDialog("Fog - ISO Transition not supported - yet!");
            return;
        }
        if (!map.getCurrentLayer().getData().misRegularGrid)
        {
            ShowInfoDialog.showInfoDialog("Fog - Transition only supported with regular FOG-layer.");
            return;
        }
/*        
        if (!map.getCurrentLayer().getData().misLayerCompleteOrdered)
        {
            ShowInfoDialog.showInfoDialog("Fog - Transition only supported with completely ordered FOG-layer.");
            return;
        }
*/
        ArrayList<Tile> testTranstionTiles = new ArrayList<Tile>();
        Layer layer = map.getCurrentLayer();
        boolean considerWrapping = true;

        
        // only empty or part transitioned tiles must be transitioned
        // if not center, only non empty tiles must be transitioned
        
        Tile tC=tile;
        Tile tN=checkTile(layer.has_N_Neighbour(tile, considerWrapping),fogID);
        Tile tS=checkTile(layer.has_S_Neighbour(tile, considerWrapping),fogID);
        Tile tW=checkTile(layer.has_W_Neighbour(tile, considerWrapping),fogID);
        Tile tE=checkTile(layer.has_E_Neighbour(tile, considerWrapping),fogID);
        Tile tNW=checkTile(layer.has_NW_Neighbour(tile, considerWrapping),fogID);
        Tile tNE=checkTile(layer.has_NE_Neighbour(tile, considerWrapping),fogID);
        Tile tSW=checkTile(layer.has_SW_Neighbour(tile, considerWrapping),fogID);
        Tile tSE=checkTile(layer.has_SE_Neighbour(tile, considerWrapping),fogID);

        
        
        //only transition tiles? not transitioned tiles? 
        
        if (tC != null) testTranstionTiles.add(tC);
        if (tS != null) testTranstionTiles.add(tS);
        if (tN != null) testTranstionTiles.add(tN);
        if (tW != null) testTranstionTiles.add(tW);
        if (tE != null) testTranstionTiles.add(tE);
        if (tNE != null) testTranstionTiles.add(tNE);
        if (tNW != null) testTranstionTiles.add(tNW);
        if (tSW != null) testTranstionTiles.add(tSW);
        if (tSE != null) testTranstionTiles.add(tSE);
  
       // doReplacementTransitions(map.getCurrentLayer(), testTranstionTiles, true, parentPrio);
        doReplacementTransitionsNew(map.getCurrentLayer(), testTranstionTiles, parentPrio, source);
        
        
        setMapChanged(true, true);
        updateNeeded(true);
    }

    
    public void doReplacementTransitionsNew(Layer originLayer, ArrayList<Tile> tiles, int orgPrio, Tile originTile)
    {
        boolean considerWrapping=true;

        if (originLayer.getData().mISOBlockWrap)
        {
            // TODO doArtifactReplacmentISOTransition( originLayer, width,  height,  originTile,  x,  y,  considerWrapping);
            return;
        }
        for (int t=0; t<tiles.size(); t++)
        {
            Tile toReplace = tiles.get(t);
        
            int width = toReplace.getMaxWidthUnscaled();
            int height = toReplace.getMaxHeightUnscaled();
            int x = toReplace.getX();
            int y = toReplace.getY();
        
            int thisPriority = toReplace.getData().getTransitionPriority();

            // examine its surroundings
            Tile tN=originLayer.isTileOnPosition(x+1, y-1, considerWrapping);
            Tile tS=originLayer.isTileOnPosition(x+1, y+1+height, considerWrapping);
            Tile tW=originLayer.isTileOnPosition(x-1, y+1, considerWrapping);
            Tile tE=originLayer.isTileOnPosition(x+1+width, y+1, considerWrapping);
            Tile tNW=originLayer.isTileOnPosition(x-1, y-1, considerWrapping);
            Tile tNE=originLayer.isTileOnPosition(x+1+width, y-1, considerWrapping);
            Tile tSW=originLayer.isTileOnPosition(x-1, y+1+height, considerWrapping);
            Tile tSE=originLayer.isTileOnPosition(x+1+width, y+1+height, considerWrapping);

            boolean N=tN!=null&&!tN.isEmptyTile() && tN.getData().getTransitionPriority()==orgPrio;
            boolean S=tS!=null&& !tS.isEmptyTile() && tS.getData().getTransitionPriority()==orgPrio;
            boolean W=tW!=null&& !tW.isEmptyTile() && tW.getData().getTransitionPriority()==orgPrio;
            boolean E=tE!=null&& !tE.isEmptyTile() && tE.getData().getTransitionPriority()==orgPrio;
            boolean NE=tNE!=null&& !tNE.isEmptyTile() && tNE.getData().getTransitionPriority()==orgPrio;
            boolean NW=tNW!=null&& !tNW.isEmptyTile() && tNW.getData().getTransitionPriority()==orgPrio;
            boolean SE=tSE!=null&& !tSE.isEmptyTile() && tSE.getData().getTransitionPriority()==orgPrio;
            boolean SW=tSW!=null&& !tSW.isEmptyTile() && tSW.getData().getTransitionPriority()==orgPrio;


            

            // edges
            boolean westEdge=false;
            if (NW&&SW&&W) westEdge = true;
            boolean eastEdge=false;
            if (NE&&SE&&E) eastEdge = true;
            boolean southEdge=false;
            if (SE&&SW&&S) southEdge = true;
            boolean northEdge=false;
            if (NE&&NW&&N) northEdge = true;


            // enhanced, if only middle, than all!
            if (W) westEdge = true;
            if (E) eastEdge = true;
            if (S) southEdge = true;
            if (N) northEdge = true;

            // build a binary representation edge and corners...
            int edgeTileNumber = 0;
            if (westEdge) edgeTileNumber += 1;
            if (northEdge) edgeTileNumber += 2;
            if (eastEdge) edgeTileNumber += 4;
            if (southEdge) edgeTileNumber += 8;

            // corner west
            boolean NECorner=false;
            if (!northEdge)
                if (!eastEdge)
                    if (NE) NECorner = true;

            boolean SECorner=false;
            if (!southEdge)
                if (!eastEdge)
                    if (SE) SECorner = true;

            boolean NWCorner=false;

            if (!northEdge)
                if (!westEdge)
                    if (NW) NWCorner = true;

            boolean SWCorner=false;
            if (!southEdge)
                if (!westEdge)
                    if (SW) SWCorner = true;

            // build a binary representation edge and corners...
            int cornerTileNumber = 0;
            if (NWCorner) cornerTileNumber += 1;
            if (NECorner) cornerTileNumber += 2;
            if (SECorner) cornerTileNumber += 4;
            if (SWCorner) cornerTileNumber += 8;

        
            // get Tile on position
            // origin tile describes the transitioning, not the tile which that is replaced! 

            boolean done = false;
            Tile  nTile = toReplace;
            if (edgeTileNumber!=0) // add edge
            {
                nTile = replaceTranistionTile(originLayer, originTile,x,y, edgeTileNumber, toReplace, done);
                done = true;
            }
            if (cornerTileNumber!=0) // add corner+16
            {
                cornerTileNumber+=16;
                replaceTranistionTile(originLayer, originTile,x,y, cornerTileNumber, nTile, done);
                done=true;
            }
            if ((edgeTileNumber==0) && (cornerTileNumber==0) && (!toReplace.isEmptyTile()))
            {
                // replace with empty tile
                replaceTranistionTile(originLayer, originTile,x,y, 0, nTile, done);
            }
        }
    }    
    
    Tile replaceTranistionTile(Layer originLayer, Tile originTile,int x,int y, int number, Tile toReplace, boolean add)
    {
        Tile ntile = new EmptyTile(toReplace.getMaxWidthUnscaled(), toReplace.getMaxHeightUnscaled());
        if (number !=0)
        {
            String name = originTile.getData().getTransitionChildrenNames().elementAt(number);
            if (name.trim().length()==0)
                return null; // no tranistion tile was set for this tile for this direction!
            String filename = originTile.getData().getTileSetFileName();
            if (!poolname.equals(("Tile"+File.separator+filename+".xml")))
            {
                poolname = "Tile"+File.separator+filename+".xml";
                mTileDataPool = new TileDataPool(poolname, true);
            }
            TileData tdata = mTileDataPool.getHashMap().get(name);
            ntile = new Tile(tdata);
        }
        originLayer.replaceTileAt(toReplace, ntile, add, true);//!add);
        
        ntile.setScalePercent (toReplace.getScalePercent());
        return ntile;
    }
    
}
