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

import csa.tiletools.sprites.Sprite;
import csa.tiletools.sprites.SpriteDataPool;
import csa.tiletools.tiles.CompositeTile;
import csa.tiletools.tiles.EmptyTile;
import csa.tiletools.tiles.Tile;
import csa.tiletools.tiles.TileData;
import csa.tiletools.tiles.TileDataPool;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Vector;

/**
 *
 * @author salchr
 */
public class Layer
{
    private LayerData mData;
    MapPanel mDisplayPanel = null;
    public void setDisplay(MapPanel panel)
    {
        mDisplayPanel = panel;
        for(int i=0; i< mTiles.size(); i++)
        {
            mTiles.get(i).setDisplay(panel);
        }
        for (int i=0; i< mSprites.size(); i++)
        {
            mSprites.get(i).setDisplay(panel);
        }
    }
    ArrayList<Tile> mTiles = new ArrayList<Tile>();
    ArrayList<Sprite> mSprites = new ArrayList<Sprite>();

    public boolean isInMap = false;
    boolean layerChanged = true;

    // these are ok, if layerChanged= false!
    int totalWidth = 0;
    int totalHeight = 0;
    int maxYPos = 0;
    int minYPos = Integer.MAX_VALUE;
    int maxXPos = 0;
    int minXPos = Integer.MAX_VALUE;
    
    int minTileWidth = Integer.MAX_VALUE;
    int minTileHeight = Integer.MAX_VALUE;
    boolean tilesOverlap = true; // is also true if unkown!
    
    
    boolean halfWidth = true; // if half width = true all rows have the same count of tiles
    boolean halfHeight = true;// if half height = true all columns have the same count of tiles
    boolean firstRowSmall = false; // if not halfWidth, and firstRowSmall=true, than first and very other row has one tile less
    boolean firstColumnSmall = false; // if not halfHeight, and firstColumnSmall=true, than first and very other column has one tile less

    
    public int getMaxX()
    {
        doCalcDefaults();
        return maxXPos;
    }
    public int getMaxY()
    {
        doCalcDefaults();
        return maxYPos;
    }

    public int getMinX()
    {
        doCalcDefaults();
        return minXPos;
    }
    public int getMinY()
    {
        doCalcDefaults();
        return minYPos;
    }
    
    public int getMinTileWidth()
    {
        doCalcDefaults();
        return minTileWidth;
    }
    public int getMinTileHeight()
    {
        doCalcDefaults();
        return minTileHeight;
    }

    public LayerData getData()
    {
        return mData;
    }

    public Layer()
    {
        mData = new LayerData();
        mData.misVisible = true;
        init();
    }
    public Layer(LayerData data)
    {
        mData = data;
        if (data == null) 
        {
            mData = new LayerData();
            mData.misVisible = true;
        }
        init();
    }

    public void setLayerNumber(int pos)
    {
        for(int i=0; i< mTiles.size(); i++)
        {
            mTiles.get(i).layer = pos;
        }
    }

    private void init()
    {
        // load tiles!
        String oldTileCollectionName = "";
        TileDataPool tileDataPool = null;
        for (int i=0; i< mData.mtileX.size(); i++)
        {
            String colName = mData.musedTileCollection.elementAt(i);
            if (colName.indexOf("EMPTY:") != -1)
            {
                // Empty Tile!
                int w=0;
                int h=0;
                String t = colName.substring(("EMPTY:").length());
                String ws = t.substring(0, t.indexOf("x"));
                String hs = t.substring(t.indexOf("x")+1);
                
                w = csa.util.UtilityString.Int0(ws);
                h = csa.util.UtilityString.Int0(hs);
                Tile tile = new EmptyTile(w, h);
                tile.setX(mData.mtileX.elementAt(i));
                tile.setY(mData.mtileY.elementAt(i));

                mTiles.add(tile);
                continue;
                
            }
            
            
            if ((tileDataPool==null) || (!(oldTileCollectionName.equals(colName))))
            {
                tileDataPool = new TileDataPool("Tile"+File.separator+colName+".xml");
            }
            oldTileCollectionName = colName;
            String tileName = mData.mtileName.elementAt(i);
            TileData tiledata = tileDataPool.get(tileName);
            if (tiledata != null)
            {
                Tile tile = new Tile(tiledata);

                tile.setX(mData.mtileX.elementAt(i));
                tile.setY(mData.mtileY.elementAt(i));

                // todo must be sorted!
                mTiles.add(tile);
            }
        }
        orderTiles();
        testRegular();
        
        for (int i=0; i< mData.mSpriteFilename.size(); i++)
        {
            String filename = mData.mSpriteFilename.elementAt(i);
            String name = mData.mSpriteName.elementAt(i);
            try
            {
                Sprite sprite = Sprite.buildSprite(filename, name);

                sprite.setActiveAction(mData.mSpriteStartAction.elementAt(i));
                sprite.setActiveDirection(mData.mSpriteStartDirection.elementAt(i));

                sprite.setX(mData.mSpriteStartX.elementAt(i));
                sprite.setY(mData.mSpriteStartY.elementAt(i));
                mSprites.add(sprite);
            }
            catch (Exception ex)
            {
                // todo give user feedback that a sprite could not be loaded!
            }
        }
    }
    public void deinit()
    {
        for (int i=0; i< mTiles.size(); i++)
        {
            mTiles.get(i).deinit();
        }
        for (int i=0; i< mSprites.size(); i++)
        {
            mSprites.get(i).deinit();
        }
    }
    
    public void reinitSprite(int spriteNo)
    {
        Sprite oldSprite = mSprites.get(spriteNo);
        oldSprite.deinit();


        String filename = mData.mSpriteFilename.elementAt(spriteNo);
        String name = mData.mSpriteName.elementAt(spriteNo);
        try
        {
            Sprite sprite = Sprite.buildSprite(filename, name);

            sprite.setActiveAction(mData.mSpriteStartAction.elementAt(spriteNo));
            sprite.setActiveDirection(mData.mSpriteStartDirection.elementAt(spriteNo));

            sprite.setX(mData.mSpriteStartX.elementAt(spriteNo));
            sprite.setY(mData.mSpriteStartY.elementAt(spriteNo));

            mSprites.add(spriteNo, sprite);
            mSprites.remove(oldSprite);
            sprite.setDisplay(mDisplayPanel);

        }
        catch (Exception ex)
        {
            // todo give user feedback that a sprite could not be loaded!
        }
    }
    

    public boolean isEqualTileOnPos(TileData tdata,int  x, int y)
    {
        for(int i=0; i< mTiles.size(); i++)
        {
            if ((mTiles.get(i).getX() == x) && (mTiles.get(i).getY() == y))
                if (mTiles.get(i).getData().getTileSetFileName().equals(tdata.getTileSetFileName()))
                    if (mTiles.get(i).getData().getName().equals(tdata.getName()))
                        return true;
        }
        return false;
    }

    public void addTile(CompositeTile ct, int x, int y, boolean inMapCoordinates)
    {
        Layer compositeLayer = ct.getLayer();
        
        // layer is saved with original coordinates
        // if there is an offset in the layer
        // compensate it!
        x = x-compositeLayer.minXPos;
        y = y-compositeLayer.minYPos;
        
        for (int t=0; t< compositeLayer.mTiles.size(); t++)
        {
            Tile orgTile = compositeLayer.mTiles.get(t); 
            Tile tile = new Tile(Tile.cloneTileData(orgTile.getData()));
            if (!inMapCoordinates)
            {
                tile.setX(orgTile.getX()+x+mData.moffsetX);
                tile.setY(orgTile.getY()+y+mData.moffsetY);
            }
            else
            {
                tile.setX(orgTile.getX()+x);
                tile.setY(orgTile.getY()+y);
            }
            mTiles.add(tile);

            mData.mtileName.add(tile.getData().mName);
            mData.musedTileCollection.add(tile.getData().getTileSetFileName());
            mData.mtileX.add(tile.getX());
            mData.mtileY.add(tile.getY());
            mData.misLayerCompleteOrdered = false;
            mData.misLayerOrdered = false;
            mData.misAllTilesSameSize=false;
            mData.misRegularGrid=false;
            tile.setDisplay(mDisplayPanel);
            layerChanged = true;
        }
        
        // include offset! -> offset is here now LAYER based

        // todo must be sorted!
        //if (doOrder)
        orderTiles();
    }
    
    // adds a "copy" of the given sprite to the layer
    public Sprite addSprite(Sprite s)
    {
        Sprite sprite = Sprite.cloneSprite(s);

        mSprites.add(sprite);

        if (mData == null)
            System.out.println("Buh!");
        if (mData.mtileName == null)
            System.out.println("Buh!");
        if (s == null)
            System.out.println("Buh!");

        mData.mSpriteFilename.add(s.data.getPoolName());
        mData.mSpriteName.add(s.data.mName);
        mData.mSpriteStartX.add(s.getX());
        mData.mSpriteStartY.add(s.getY());
        mData.mSpriteStartAction.add(s.getActiveAction());
        mData.mSpriteStartDirection.add(s.getActiveDirection());
        mData.mSpriteCoordinator.add(s.getActiveCoordinator());
        sprite.setDisplay(mDisplayPanel);
        
        layerChanged = true;
        return sprite;
    }

    // coordinates are in map coordinates, and not scaled!
    // offset is not included in coordinates!
    public Tile addTile(TileData t, int x, int y, boolean inMapCoordinates)
    {
        return addTile(t, x, y, inMapCoordinates, null);
    }
    
    public Tile addTile(TileData t, int x, int y, boolean inMapCoordinates, TileDataPool tileDataPool)
    {
        return addTile(t, x, y, inMapCoordinates, tileDataPool, true, false);
    }
    public Tile _addTile(TileData t, int x, int y, boolean inMapCoordinates, TileDataPool tileDataPool, boolean replace)
    {
        return addTile(t, x, y, inMapCoordinates, tileDataPool, true, replace);
    }
    public Tile addTile(TileData t, int x, int y, boolean inMapCoordinates, TileDataPool tileDataPool, boolean doOrder, boolean replace)
    {
        Tile tile;
        
        if (t.getIsCompositeTile())
        {
            CompositeTile ct = new CompositeTile(t);
            addTile(ct, x, y, inMapCoordinates);
            return ct;
        }
        
        if (tileDataPool == null)
            tile = new Tile(t);
        else
            tile = new Tile(t, true, tileDataPool);
        
        t = tile.getData(); // data may be different after init, since there may be variants!
        
        Tile source=null;
        // include offset! -> offset is here now LAYER based
        if (!inMapCoordinates)
        {
            tile.setX(x+mData.moffsetX);
            tile.setY(y+mData.moffsetY);
            if (replace)
                source = isTileOnPosition(x+mData.moffsetX, y+mData.moffsetY, true);
        }
        else
        {
            tile.setX(x);
            tile.setY(y);
            if (replace)
                source = isTileOnPosition(x, y, true);
        }
        if (source != null)
        {
            replaceTileAt(source, tile);
            return tile;
        }
        
        
        
        
        // todo must be sorted!
        mTiles.add(tile);

        if (mData == null)
            System.out.println("Buh!");
        if (mData.mtileName == null)
            System.out.println("Buh!");
        if (t == null)
            System.out.println("Buh!");

        mData.mtileName.add(t.mName);
        mData.musedTileCollection.add(t.getTileSetFileName());
        mData.mtileX.add(tile.getX());
        mData.mtileY.add(tile.getY());
        mData.misLayerCompleteOrdered = false;
        mData.misLayerOrdered = false;
        mData.misAllTilesSameSize=false;
        mData.misRegularGrid=false;
        tile.setDisplay(mDisplayPanel);
        layerChanged = true;

        if (doOrder)
            orderTiles();
        return tile;
    }
    
    public void moveTiles(Layer otherLayer)
    {
        for (int tc=0; tc<otherLayer.mTiles.size(); tc++)
        {
            mTiles.add(otherLayer.mTiles.get(tc));
            TileData t = otherLayer.mTiles.get(tc).getData();
            
            mData.mtileName.add(t.mName);
            mData.musedTileCollection.add(t.getTileSetFileName());
            mData.mtileX.add(otherLayer.mTiles.get(tc).getX());
            mData.mtileY.add(otherLayer.mTiles.get(tc).getY());
        }
        otherLayer.mTiles.clear();
        otherLayer.getData().mtileName.clear();
        otherLayer.getData().musedTileCollection.clear();
        otherLayer.getData().mtileX.clear();
        otherLayer.getData().mtileY.clear();
        
        mData.misLayerCompleteOrdered = false;
        mData.misLayerOrdered = false;
        mData.misAllTilesSameSize=false;
        mData.misRegularGrid=false;
        layerChanged = true;
    }
    
    
    public void moveSprites(Layer otherLayer)
    {
        for (int sc=0; sc<otherLayer.mSprites.size(); sc++)
        {
            Sprite sprite = otherLayer.mSprites.get(sc);
            mSprites.add(sprite);

            mData.mSpriteFilename.addElement(otherLayer.getData().mSpriteFilename.elementAt(sc));
            mData.mSpriteName.addElement(otherLayer.getData().mSpriteName.elementAt(sc));
            mData.mSpriteStartX.addElement(otherLayer.getData().mSpriteStartX.elementAt(sc));
            mData.mSpriteStartY.addElement(otherLayer.getData().mSpriteStartY.elementAt(sc));
            mData.mSpriteStartAction.addElement(otherLayer.getData().mSpriteStartAction.elementAt(sc));
            mData.mSpriteStartDirection.addElement(otherLayer.getData().mSpriteStartDirection.elementAt(sc));
            mData.mSpriteCoordinator.addElement(otherLayer.getData().mSpriteCoordinator.elementAt(sc));
        }
        otherLayer.mSprites.clear();
        otherLayer.getData().mSpriteFilename.clear();
        otherLayer.getData().mSpriteName.clear();
        otherLayer.getData().mSpriteStartX.clear();
        otherLayer.getData().mSpriteStartY.clear();
        otherLayer.getData().mSpriteStartAction.clear();
        otherLayer.getData().mSpriteStartDirection.clear();
        otherLayer.getData().mSpriteCoordinator.clear();
        
        layerChanged = true;
    }    

    public void deleteTile(int row)
    {
        if (row>=mTiles.size()) return;
        Tile tile = mTiles.get(row);
        tile.deinit();
        
        mTiles.remove(row);
        mData.mtileName.remove(row);
        mData.musedTileCollection.remove(row);
        mData.mtileX.remove(row);
        mData.mtileY.remove(row);

        mData.misLayerCompleteOrdered = false;
        layerChanged = true;
        
        orderTiles();
    }
    
    public Sprite getSprite(int row)
    {
        if (row>=mSprites.size()) return null;
        // 
        return mSprites.get(row);
    }

    public void deleteSprite(int row)
    {
        if (row>=mSprites.size()) return;
        // 
        Sprite sprite = mSprites.get(row);
        sprite.deinit();
        
        mSprites.remove(row);
        mData.mSpriteFilename.remove(row);
        mData.mSpriteName.remove(row);
        mData.mSpriteStartAction.remove(row);
        mData.mSpriteStartDirection.remove(row);
        mData.mSpriteStartX.remove(row);
        mData.mSpriteStartY.remove(row);
        mData.mSpriteCoordinator.remove(row);
        layerChanged = true;
    }
    
    // replaces data at spriteSel
    public void setSpriteData(int spriteSel, String action, String direction, int x,int y, String coordinator, String pool, String name)
    {
        mData.mSpriteFilename.insertElementAt(pool, spriteSel);
        mData.mSpriteFilename.removeElementAt(spriteSel+1);
        mData.mSpriteName.insertElementAt(name, spriteSel);
        mData.mSpriteName.removeElementAt(spriteSel+1);
        mData.mSpriteStartAction.insertElementAt(action, spriteSel);
        mData.mSpriteStartAction.removeElementAt(spriteSel+1);
        mData.mSpriteStartDirection.insertElementAt(direction, spriteSel);
        mData.mSpriteStartDirection.removeElementAt(spriteSel+1);
        mData.mSpriteStartX.insertElementAt(x, spriteSel);
        mData.mSpriteStartX.removeElementAt(spriteSel+1);
        mData.mSpriteStartY.insertElementAt(y, spriteSel);
        mData.mSpriteStartY.removeElementAt(spriteSel+1);
        mData.mSpriteCoordinator.insertElementAt(coordinator, spriteSel);
        mData.mSpriteCoordinator.removeElementAt(spriteSel+1);
    }
    
    
    public int getSpriteStartX(int row)
    {
        return mData.mSpriteStartX.elementAt(row);
    }
    public int getSpriteStartY(int row)
    {
        return mData.mSpriteStartY.elementAt(row);
    }
    public String getCoordinatorName(int row)
    {
        return mData.mSpriteCoordinator.elementAt(row);
    }
    
    
    public void downTile(int row)
    {
        downTile(row, true);
    }
    private void downTile(int row, boolean reorder)
    {
        if (row == 0) return;
        Tile t = mTiles.get(row);
        mTiles.remove(row);
        mTiles.add(row-1, t);

        String name = mData.mtileName.elementAt(row);
        mData.mtileName.remove(row);
        mData.mtileName.add(row-1, name);

        String col = mData.musedTileCollection.elementAt(row);
        mData.musedTileCollection.remove(row);
        mData.musedTileCollection.add(row-1, col);

        int x = mData.mtileX.elementAt(row);
        mData.mtileX.remove(row);
        mData.mtileX.add(row-1, x);

        int y = mData.mtileY.elementAt(row);
        mData.mtileY.remove(row);
        mData.mtileY.add(row-1, y);
        mData.misLayerCompleteOrdered = false;
        mData.misLayerOrdered = false;
        mData.misAllTilesSameSize=false;
        mData.misRegularGrid=false;
        layerChanged = true;
        if (reorder)
            orderTiles();
    }
    
    // not tested - hope this works!
    void moveToFront(Tile tile)
    {
        if (tile == null) return;
        int index = mTiles.indexOf(tile);
        while (index >0)
        {
            downTile(index, false);
            index = mTiles.indexOf(tile);
        }
        orderTiles();
    }

    public void upTile(int row)
    {
        if (row == mTiles.size()-1) return;

        Tile t = mTiles.get(row);
        mTiles.remove(row);
        mTiles.add(row+1, t);

        String name = mData.mtileName.elementAt(row);
        mData.mtileName.remove(row);
        mData.mtileName.add(row+1, name);

        String col = mData.musedTileCollection.elementAt(row);
        mData.musedTileCollection.remove(row);
        mData.musedTileCollection.add(row+1, col);

        int x = mData.mtileX.elementAt(row);
        mData.mtileX.remove(row);
        mData.mtileX.add(row+1, x);

        int y = mData.mtileY.elementAt(row);
        mData.mtileY.remove(row);
        mData.mtileY.add(row+1, y);
        mData.misLayerCompleteOrdered = false;
        mData.misLayerOrdered = false;
        mData.misAllTilesSameSize=false;
        mData.misRegularGrid=false;
        layerChanged = true;
        orderTiles();
    }

    // does NOT order tiles, only correcty the numbering in layer!
    public void orderTiles()
    {
        //mData.misLayerCompleteOrdered = false;
        //mData.misLayerOrdered = false;
        //tilesOverlap = true; // is also true if unkown!

        // also should reset all tiles so that upper left is 0

        // do examine!
        correctTileNumbering();
    }
    
    public void correctTileNumbering()
    {
        for (int t=0; t<mTiles.size(); t++)
        {
            Tile tile = mTiles.get(t);
            tile.tileInLayer=t;
        }
    }
    
    public void toZero(int minX, int minY)
    {
        for (int t=0; t<mTiles.size(); t++)
        {
            Tile tile = mTiles.get(t);
            tile.setX(tile.getX()-minX);
            tile.setY(tile.getY()-minY);
        }
        layerChanged=true;
        doCalcDefaults();
    }
    
    
    public void setISOAssumed(boolean iso)
    {
        layerChanged=true;
        mData.mISOBlockWrap = iso;
        testRegular();
    }
    // result is the difference between minX and max(X+width)
    public int getLayerWidth()
    {
        doCalcDefaults();
        return totalWidth;
    }
    // result is the difference between minY and max(Y+Height)
    public int getLayerHeight()
    {
        doCalcDefaults();
        return totalHeight;
    }

    public int getTileCount()
    {
        return mTiles.size();
    }
    
    public void doCalcDefaults()
    {
        if (!layerChanged) return;
        maxYPos = Integer.MIN_VALUE;
        minYPos = Integer.MAX_VALUE;
        maxXPos = Integer.MIN_VALUE;
        minXPos = Integer.MAX_VALUE;

        minTileWidth = Integer.MAX_VALUE;
        minTileHeight = Integer.MAX_VALUE;
        
        for (int i = 0; i < mTiles.size(); i++)
        {
            Tile tile = mTiles.get(i);
            int maxX = tile.getX() + tile.getMaxWidthUnscaled();
            if (maxX>maxXPos) maxXPos=maxX;
            
            if (minXPos>tile.getX()) minXPos=tile.getX();
            
            if (minTileWidth>tile.getMinWidthUnscaled()) minTileWidth=tile.getMinWidthUnscaled();
            if (minTileHeight>tile.getMinHeightUnscaled()) minTileHeight=tile.getMinHeightUnscaled();
            
        }
        totalWidth = -minXPos+maxXPos;
        for (int i = 0; i < mTiles.size(); i++)
        {
            Tile tile = mTiles.get(i);
            int maxY = tile.getY() + tile.getMaxHeightUnscaled();
            if (maxY>maxYPos) maxYPos=maxY;
            if (minYPos>tile.getY()) minYPos=tile.getY();
        }
        totalHeight = -minYPos+maxYPos;
        layerChanged = false;
    }
    
    
    public boolean isRegular()
    {
        if ((mData.misAllTilesSameSize) && (mData.misLayerCompleteOrdered))
            return true;
        if  ((mData.misLayerOrdered) && (mData.misAllTilesSameSize) && (mData.misRegularGrid))
            return true;
        return false;
    }
    
    // complete AND ordered
    public boolean isCompletlyOrdered()
    {
        return mData.misLayerCompleteOrdered;
    }
    
    public void testRegular()
    {
        if (mTiles.size()==0)
        {
            mData.misAllTilesSameSize = true;
            mData.misLayerCompleteOrdered = true;
            mData.misLayerOrdered = true;
            mData.misRegularGrid = true;
            return;

        }
        int width =mTiles.get(0).getMaxWidthUnscaled();
        int height =mTiles.get(0).getMaxHeightUnscaled();

        boolean test = true;

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

            if (width != +tile.getMaxWidthUnscaled())
            {
                test=false;
                break;
            }
            if (height != +tile.getMaxHeightUnscaled())
            {
                test=false;
                break;
            }
        }
        mData.misAllTilesSameSize=test;
        mData.misRegularGrid=test;
        doCalcDefaults();

/*
        Collections.sort(mTiles, new Comparator<Tile>()
            {
                public int compare(Tile a1, Tile a2){

                    int bigPos1=a1.getY()*totalWidth+a1.getX();
                    int bigPos2=a2.getY()*totalWidth+a2.getX();

                    return bigPos1-bigPos2;

                }
            });

	mData.mtileX=new Vector<Integer>();
	mData.mtileY=new Vector<Integer>();
	mData.mtileName=new Vector<String>();
	mData.musedTileCollection=new Vector<String>();
*/

        boolean ordered = true;
        for (int i = 0; ((i < mTiles.size()-1) && (ordered)); i++)
        {
            int bigPos1=mTiles.get(i).getY()*totalWidth+mTiles.get(i).getX();
            int bigPos2=mTiles.get(i+1).getY()*totalWidth+mTiles.get(i+1).getX();
            ordered = (bigPos1<=bigPos2);
        }

        mData.misLayerOrdered = ordered;
        mData.misLayerCompleteOrdered = false; // complete map is filled with tiles

        if ((height==0) || (width == 0)) return;
        
        if ((totalHeight/height) * (totalWidth/width) == mTiles.size())
            mData.misLayerCompleteOrdered = ordered;

        checkISORegular();
    }
    
    
    public boolean makeRegular()
    {
        if (mTiles.size()==0)
        {
            mData.misAllTilesSameSize = true;
            mData.misLayerCompleteOrdered = true;
            mData.misLayerOrdered = true;
            mData.misRegularGrid = true;
            return true;
        }
        int width =mTiles.get(0).getMaxWidthUnscaled();
        int height =mTiles.get(0).getMaxHeightUnscaled();

        boolean test = true;

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

            if (width != +tile.getMaxWidthUnscaled())
            {
                test=false;
                break;
            }
            if (height != +tile.getMaxHeightUnscaled())
            {
                test=false;
                break;
            }
        }
        mData.misAllTilesSameSize=test;
        mData.misRegularGrid=test;
        doCalcDefaults();


        Collections.sort(mTiles, new Comparator<Tile>()
            {
                public int compare(Tile a1, Tile a2){

                    int bigPos1=a1.getY()*totalWidth+a1.getX();
                    int bigPos2=a2.getY()*totalWidth+a2.getX();

                    return bigPos1-bigPos2;

                }
            });


	mData.mtileX=new Vector<Integer>();
	mData.mtileY=new Vector<Integer>();
	mData.mtileName=new Vector<String>();
	mData.musedTileCollection=new Vector<String>();

        for (int i = 0; i < mTiles.size(); i++)
        {
            Tile tile = mTiles.get(i);
            mData.mtileX.addElement(tile.getX());
            mData.mtileY.addElement(tile.getY());
            mData.mtileName.addElement(tile.getData().mName);
            mData.musedTileCollection.addElement(tile.getData().getTileSetFileName());
        }

        mData.misLayerOrdered = true;
        mData.misLayerCompleteOrdered = false; // complete map is filled with tiles

        if ((totalHeight/height) * (totalWidth/width) == mTiles.size())
            mData.misLayerCompleteOrdered = true;

        checkISORegular();
        

        
        
        return true;
    }

    private void checkISORegular()
    {
        if (mData.mISOBlockWrap) // assumed ISO
        {
            int width =0;
            int height =0;
            if (mTiles.size()>0)
            {
                width =mTiles.get(0).getMaxWidthUnscaled();
                height =mTiles.get(0).getMaxHeightUnscaled();
            }
            else
            {
                return;
            }

            // total width = (countX+1/2) * width of tile
            // total height = (countY+1/2) * height of tile
            int w = ((totalWidth)/width);
            int h = ((totalHeight)/height);
            
            halfWidth = !(totalWidth%width==0);
            halfHeight = !(totalHeight%height==0);
            
            int totalTileCount = w*h*2;
            
            if (!halfWidth) totalTileCount-=totalHeight/height;
            if (!halfHeight) totalTileCount-=totalWidth/width;
            if ((!halfWidth) && (!halfHeight)) totalTileCount++;
            mData.misLayerCompleteOrdered = false;
            
            boolean ordered = true;
            for (int i = 0; ((i < mTiles.size()-1) && (ordered)); i++)
            {
                int bigPos1=mTiles.get(i).getY()*totalWidth+mTiles.get(i).getX();
                int bigPos2=mTiles.get(i+1).getY()*totalWidth+mTiles.get(i+1).getX();
                ordered = (bigPos1<=bigPos2);
            }

            mData.misLayerOrdered = ordered;
            mData.misLayerCompleteOrdered = false; // complete map is filled with tiles

            if (totalTileCount == mTiles.size())
                mData.misLayerCompleteOrdered = ordered;
            
            if (!halfWidth)
            {
                firstRowSmall = mTiles.get(0).getY() != mTiles.get(w-1).getY();
            }
            if (!halfHeight)
            {
                int lastRowFirstTile = totalTileCount-w;
                if (lastRowFirstTile>=mTiles.size()) return;
                Tile firstColumnTile;
                if (mTiles.get(0).getX()==getMinX()) firstColumnTile = mTiles.get(0);
                else
                {
                    if (firstRowSmall) 
                        firstColumnTile = mTiles.get(w-1);
                    else 
                        firstColumnTile = mTiles.get(w);
                }
                
                firstColumnSmall = firstColumnTile.getX() != mTiles.get(lastRowFirstTile).getX();
            }
            
        }        
    }
    
    // unscaled
    // if priority == -1 than it is ignored
    //
    public Tile isTileOnPosition(int x, int y)
    {
        return isTileOnPosition(x, y, false);
    }
    
     // works good!
     // input x,y are coordinates WITHIN one ISO tile!
     // coordinates 0,0 ist upper/left edge
     // return 0 is interior to ISO
     // 5 - if out of bounds
     // 1 if in first quadrant
     // 2 if in second quadrant
     // 3 if in third quadrant
     // 4 if in fourth quadrant
     private int isISOInterior(int x, int y, int width, int height)
     {
         if (x<0) return 5;
         if (x>=width) return 5;
         if (y<0) return 5;
         if (y>=height) return 5;
         
         
         //     0            1          columnIndex
         //   #####################
         // 0 ###1#####   #####2###
         //   #######       #######
         //   ####             ####
         //   #         0         #
         //   #                   #
         //   ####             ####
         //   #######       #######
         // 1 ##3######   #####4###
         //   #####################
         // rowIndex

/*
 * Vektorprodukt:
 u x v = u.x * v.y - v.x * u.y
         
         
Das geht auch über das Vektorprodukt.
Seien

    u = p(i+1) - pi // line definition pi and p(i+1) are the two endpoints defining the line
    v = p - pi      // p is the point we are interested in


Dann gilt:

    u x v < 0 => p liegt links von der orientierten Geraden duch pi und p(i+1)
    u x v = 0 => p liegt auf der Geraden
    u x v > 0 => p liegt rechts von der Geraden         
*/                         
         
        int quadrantColumnIndex = (x / (width/2))%2; // dividiable by 2 means first halfcolumn, otherwise second half column
        int quadrantRowIndex = (y / (height/2))%2; // dividiable by 2 means first halfrow, otherwise second half row
         
        // testing is done via above formular
        // whether a given point is left or right
        // of a line
        if ((quadrantColumnIndex==0) && (quadrantRowIndex==0))
        {
            int diagX1 = 0;
            int diagY1 = height/2;
            int diagX2 = width/2;
            int diagY2 = 0;
            
            
            
            int ux = diagX2-diagX1;
            int uy = diagY2-diagY1;
            
            int vx = x-diagX1;
            int vy = y-diagY1;
            
            int uxv = ux*vy-vx*uy;
            
            if (uxv<0) return 1;
            return 0; // drauf ist "drin"
        }                 
            
        if ((quadrantColumnIndex==1) && (quadrantRowIndex==0))
        {
            int diagX1 = width/2;
            int diagY1 = 0;
            int diagX2 = width;
            int diagY2 = height/2;
            
            
            
            int ux = diagX2-diagX1;
            int uy = diagY2-diagY1;
            
            int vx = x-diagX1;
            int vy = y-diagY1;
            
            int uxv = ux*vy-vx*uy;
            
            if (uxv<0) return 2;
            return 0; // drauf ist "drin"
        }                 
            
        if ((quadrantColumnIndex==0) && (quadrantRowIndex==1))
        {
            int diagX1 = 0;
            int diagY1 = height/2;
            int diagX2 = width/2;
            int diagY2 = height;
            
            
            
            int ux = diagX2-diagX1;
            int uy = diagY2-diagY1;
            
            int vx = x-diagX1;
            int vy = y-diagY1;
            
            int uxv = ux*vy-vx*uy;
            
            if (uxv>0) return 3;
            return 0; // drauf ist "drin"
        }                 
            
        if ((quadrantColumnIndex==1) && (quadrantRowIndex==1))
        {
            int diagX1 = width/2;
            int diagY1 = height;
            int diagX2 = width;
            int diagY2 = height/2;
            
            
            
            int ux = diagX2-diagX1;
            int uy = diagY2-diagY1;
            
            int vx = x-diagX1;
            int vy = y-diagY1;
            
            int uxv = ux*vy-vx*uy;
            
            if (uxv>0) return 4;
            return 0; // drauf ist "drin"
        }                 
                         
         return 5;
     }
    
    
    // returns the position in the tiles
    // array for a given x, y position for an ISO tile
    // x,y must be in relation to minX and minY
    private int getISOArrayPos(int x, int y)
    {
        if ((x<0)||(y<0)) return -1;
        
        int width =mTiles.get(0).getMaxWidthUnscaled();
        int height =mTiles.get(0).getMaxHeightUnscaled();
        int tile1X = x/(width);
        int tile1Y = y/(height/2);
        int arrayPos = tile1Y*(totalWidth/width);
        
        int tileXPosInArray = tile1X;
        int tileYPosInArray = tile1Y;
        
        // if half width, all rows have the same width
        // if half height all columns have the height
        // if not, than every other row column has one tile less (but we dont know which row/column, first or second)
        if (!halfHeight)
        {
            arrayPos = 0;
            for(int i=0; i<tileYPosInArray;i++)
            {
                if (i%2 == 0) 
                    if (firstRowSmall)
                        arrayPos += (totalWidth/width)-1;
                    else
                        arrayPos += (totalWidth/width);
                else
                    if (firstRowSmall)
                        arrayPos += (totalWidth/width);
                    else
                        arrayPos += (totalWidth/width)-1;
            }

        }
        arrayPos += tileXPosInArray;
        if (arrayPos>=mTiles.size()) return -1;
        return arrayPos;
    }

     

    class ISOPos
    {
        int tilePosX;
        int tilePosY;
        int offsetX;
        int offsetY;
        ISOPos(int x, int y, int ox, int oy)
        {
            tilePosX=x;
            tilePosY=y;
            offsetX=ox;
            offsetY=oy;
        }
    }
     
    

    private ISOPos wrapToEvenISO(int x, int y, boolean considerWrapping)
    {
        int width =mTiles.get(0).getMaxWidthUnscaled();
        int height =mTiles.get(0).getMaxHeightUnscaled();


        int baseTilePosX = (x/(width))*(width);
        int baseTilePosY = (y/(height))*(height);
        // consider negative rounding!
        if (x<0) baseTilePosX-=width;
        if (y<0) baseTilePosY-=height;
        
        int baseTileOffsetX = x-baseTilePosX;
        int baseTileOffsetY = y-baseTilePosY;
        
        if (baseTileOffsetX>=width)
        {
            baseTileOffsetX -= width;
            baseTilePosX += width;
        }
        if (baseTileOffsetX<=-width)
        {
            baseTileOffsetX += width;
            baseTilePosX -= width;
        }
        if (baseTileOffsetY>=height)
        {
            baseTileOffsetY -= height;
            baseTilePosY += height;
        }
        if (baseTileOffsetY<=-height)
        {
            baseTileOffsetX += height;
            baseTilePosY -= height;
        }

        // assuming halfHeight...
        // -> full array, all rows / columns equally filled

        if (considerWrapping)
        {
            if (baseTilePosX >totalWidth-width-width/2) 
                baseTilePosX-=(totalWidth-width/2);
            if (baseTilePosX<0) 
                baseTilePosX+=totalWidth-width/2;

            if (baseTilePosY >totalHeight-height-height/2) 
                baseTilePosY-=(totalHeight-height/2);
            if (baseTilePosY<0) 
                baseTilePosY+=totalHeight-height/2;
        }
        
        
        
        return new ISOPos(baseTilePosX,baseTilePosY,baseTileOffsetX,baseTileOffsetY);
    }    
    
    private ISOPos wrapToOddISO(int x, int y, boolean considerWrapping)
    {
        int width =mTiles.get(0).getMaxWidthUnscaled();
        int height =mTiles.get(0).getMaxHeightUnscaled();


        int baseTilePosX = ((x-width/2)/(width))*(width);
        int baseTilePosY = ((y-height/2)/(height))*(height);
        
        
        // consider negative rounding!
        if ((x-width/2)<0) baseTilePosX-=width;
        if ((y-height/2)<0) baseTilePosY-=height;
        baseTilePosX += (width/2);
        baseTilePosY += (height/2);
        
        int baseTileOffsetX = x-baseTilePosX;
        int baseTileOffsetY = y-baseTilePosY;

        if (baseTileOffsetX>=width)
        {
            baseTileOffsetX -= width;
            baseTilePosX += width;
        }
        if (baseTileOffsetX<=-width)
        {
            baseTileOffsetX += width;
            baseTilePosX -= width;
        }
        if (baseTileOffsetY>=height)
        {
            baseTileOffsetY -= height;
            baseTilePosY += height;
        }
        if (baseTileOffsetY<=-height)
        {
            baseTileOffsetX += height;
            baseTilePosY -= height;
        }
        
        
        // assuming halfHeight...
        // -> full array, all rows / columns equally filled

        if (considerWrapping)
        {
            if (baseTilePosX >totalWidth-width) 
                baseTilePosX-=(totalWidth-width/2);
            if (baseTilePosX<width/2) 
                baseTilePosX+=totalWidth-width/2;

            if (baseTilePosY >totalHeight-height) 
                baseTilePosY-=(totalHeight-height/2);
            if (baseTilePosY<height/2) 
                baseTilePosY+=totalHeight-height/2;
        }
        
        
        
        return new ISOPos(baseTilePosX,baseTilePosY,baseTileOffsetX,baseTileOffsetY);
    }    
    
    
    public ArrayList<Sprite> getSpritesOnPosition( int x,  int y)
    {
        ArrayList<Sprite> ret = new ArrayList<Sprite>();
        int compareCount = 0;
        
        
        // go thru all tiles this is VERY slow
        for (int i = 0; i < mSprites.size(); i++)
        {
            compareCount++;
            Sprite sprite = mSprites.get(i);
            int tx = sprite.getX();
            int ty = sprite.getY();
            
            if ((tx>x) || (tx+sprite.getMaxWidthUnscaled()<=x)) continue;
            if ((ty>y) || (ty+sprite.getMaxHeightUnscaled()<=y)) continue;
            ret.add(sprite);
        }        
        
        return ret;
    }
    
    // ccordinates are unscaled!
    // wrapping is only considdered for now for (mData.misAllTilesSameSize) && (mData.misLayerCompleteOrdered)
    // Wrap - Check is not done for ISO!
    
    public Tile isTileOnPosition(int x, int y, boolean considerWrapping)
    {
        int compareCount = 0;
        int startAtTile =0;

        if (mTiles.isEmpty()) return null;

        //consider ISO

        // fastest way, a full map of equal tiles
        // we can calculate the pos, and look if a tile is there!
        if ((mData.misAllTilesSameSize) && (mData.misLayerCompleteOrdered))
        {
            // if ISO map
            // and full map we calculate ISO array
            
            
            // Wrap - Check is not done for ISO!
            if (mData.mISOBlockWrap) // assumed ISO
            {
                //adjust ccordinates to min
                // all times have same size!
                int width =mTiles.get(0).getMaxWidthUnscaled();
                int height =mTiles.get(0).getMaxHeightUnscaled();

                x=x-minXPos;
                y=y-minYPos;
                
                // this part only for NON wrapping
                // start
                
                boolean negativPos = false;
                // get the two possible tiles
                // in ISO each point in the map
                // is overlapped by exactly two tiles
                // one tile is with FULL coordinates
                // this will be tile1
                // look via quadrant information
                // on which side the other
                // tile is and find that as well
                
                
                
                // regular base!
                int tileBaseX = (x/width)*width;
                int tileBaseY = (y/height)*height;

                // consider negative rounding!
                if (x<0)tileBaseX-=width;
                if (y<0)tileBaseY-=height;
                
                
                // offset if xy from base 1
                int baseOffsetX1=x;
                int baseOffsetY1=y;
                
                if (tileBaseX!=0)
                    baseOffsetX1=x%tileBaseX;
                if (tileBaseY!=0)
                    baseOffsetY1=y%tileBaseY;
                
                if (baseOffsetX1<0) baseOffsetX1 += width;
                if (baseOffsetY1<0) baseOffsetY1 += height;
                

                // tile that overlaps base, allways in some way "half" off the above!
                int tileBaseX2 = -1;
                int tileBaseY2 = -1;                
                int baseOffsetX2 = -1;
                int baseOffsetY2 = -1;
                int x2 = -1;
                int y2 = -1;

                // end
                
                
                ISOPos even = wrapToEvenISO(x,y, considerWrapping);
                ISOPos odd = wrapToOddISO(x,y, considerWrapping);

                tileBaseX = even.tilePosX;
                tileBaseY = even.tilePosY;
                baseOffsetX1 = even.offsetX;
                baseOffsetY1 = even.offsetY;

                tileBaseX2 = odd.tilePosX;
                tileBaseY2 = odd.tilePosY;
                baseOffsetX2 = odd.offsetX;
                baseOffsetY2 = odd.offsetY;

                x = tileBaseX+baseOffsetX1;
                y = tileBaseY+baseOffsetY1;
                x2 = tileBaseX2+baseOffsetX2;
                y2 = tileBaseY2+baseOffsetY2;
                
                
                
                
                if (x<0) negativPos=true;
                if (y<0) negativPos=true;
                
                Tile tile1 = null;
                Tile tile2 = null;
                Tile returnTile = null;

                if (!negativPos)
                {
                    int arrayPos = getISOArrayPos(tileBaseX, tileBaseY);
                    if (arrayPos>=0)
                        tile1 = mTiles.get(arrayPos);
                    int arrayPos2 = getISOArrayPos(tileBaseX2, tileBaseY2);
                    if (arrayPos2>=0)
                        tile2 = mTiles.get(arrayPos2);
                    
                    // now check if we are in the ISO area of tile1 (full tile)
                    // than return tile1
                    // otherwise -> NO and return half tile (tile 2)
                    int iI = isISOInterior(x%width, y%height, width, height);
                    if (iI == 0)
                    {
                        if (tile1!=null)
                        {
                            if (considerWrapping)
                            {
                                if ((tile1.getX() == tileBaseX) && ((tile1.getY() == tileBaseY)))
                                    returnTile = tile1;
                            
                            }
                            else
                            {
                                if ((tile1.getX()-minXPos == tileBaseX) && ((tile1.getY()-minYPos == tileBaseY)))
                                    returnTile = tile1;
                            }
                        }
                    }
                    else 
                    if (iI < 5)
                    {
                        if (tile2!=null)
                        {
                            if (considerWrapping)
                            {
                                if ((tile2.getX() == tileBaseX2) && ((tile2.getY() == tileBaseY2)))
                                    returnTile = tile2;
                                
                            }
                            else
                            {
                                if ((tile2.getX()-minXPos == tileBaseX2) && ((tile2.getY()-minYPos == tileBaseY2)))
                                    returnTile = tile2;
                                
                            }
                        }
                    }
                }
                

               return returnTile;
            }
            
            // Non ISO
            
            // complete ordered and all tiles same size Hurray - now we can calculate!
            int tileWidth = mTiles.get(0).getMaxWidthUnscaled();
            int tileHeight = mTiles.get(0).getMaxHeightUnscaled();
            int layerWidth = getLayerWidth();
            int layerHeight = getLayerHeight();

            
            int orgX = x;
            int orgY = y;
            // now layerChanged = false!
            x=x +(-minXPos); // now position is 0 based!
            y=y +(-minYPos);
            
            // runden auf position in dem sich x, y befindet!
            // sonst ist xPOs und yPOs falsch!
            if (x<0)
            {
                x=(x-(tileWidth-1));
            }
            if (x>0)
            {
                ;//x=x;
            }
            x = x / tileWidth;
            x = x * tileWidth;
            
            if (y<0)
            {
                y=(y-(tileHeight-1));
            }
            if (y>0)
            {
                ;//y=(y+(tileHeight-1));
            }
            y = y / tileHeight;
            y = y * tileHeight;
            

            int tilesInRow = layerWidth/tileWidth;
            int tilesInColumn = layerHeight/tileHeight;
            int xPos = x/tileWidth;
            int yPos = y/tileHeight;

            boolean possibleBorder = false;
            
            if (xPos <0) possibleBorder = true;
            if (yPos <0) possibleBorder = true;

            if (x/tileWidth>=tilesInRow) possibleBorder = true;
            if (y/tileHeight>=tilesInColumn) possibleBorder = true;
            if (possibleBorder) 
            {
                if (!considerWrapping) 
                    return null;
                // now get border tile
            
                int newX=x;
                int newY=y;
                
                // test X
                if (x <0) 
                {
                    if (!mData.mWrapHorizontal) return null;
                    newX =x+ layerWidth;
                    // build X
                }
                if (x/tileWidth>=tilesInRow) 
                {
                    if (!mData.mWrapHorizontal) return null;
                    newX = x- layerWidth;
                    // build X
                }

                // test Y
                if (y <0) 
                {
                    if (!mData.mWrapVertical) return null;
                    newY =y+ layerHeight;
                    // build Y
                }
                if (y/tileHeight>=tilesInColumn) 
                {
                    if (!mData.mWrapVertical) return null;
                    newY =y- layerHeight;
                    // build Y
                }
// hm, shouldnt this allways be + (PLUS) min#Pos?
                return isTileOnPosition(newX+minXPos, newY+minYPos, false);
            }

            
            Tile tile = mTiles.get(yPos*tilesInRow+xPos);
            compareCount++;
            return tile;
        }


        // nearly the same as above,
        // all tiles have the same size
        // all tiles are ordered
        // BUT, the difference is, that the map may not be completely filled!
        // (ISO not done yet for not fully filled maps!)
        if  ((!mData.mISOBlockWrap) && (mData.misLayerOrdered) && (mData.misAllTilesSameSize) && (mData.misRegularGrid))
        {
            // tiles in layer are ordered in ascending order.
            // meaning for each tile it is true that
            // tilePosA > tilePosB ->  tileAY*xWidth+tileAX > tileBY*xWidth+tileBX
            // this should imply some faster algorythm than going thru all tiles
            // allways half and ...
            doCalcDefaults();
            int tileWidth = mTiles.get(0).getMaxWidthUnscaled();
            int tileHeight = mTiles.get(0).getMaxHeightUnscaled();

            int regularX; // contains x,y coordinates of tile (if available) that "contains" x,y pos
            int regularY;

            if (x>0)
            {
                regularX = x / tileWidth;
            }
            else
            {
                regularX = (x -(tileWidth-1)) / (tileWidth);
            }
            if (y>0)
            {
                regularY = y / tileHeight;
            }
            else
            {
                regularY = (y -(tileHeight-1)) / (tileHeight);
            }


            regularX *= tileWidth; 
            regularY *= tileHeight;
            int bigPos=regularY*totalWidth+regularX;

            int sizeAll = mTiles.size();
            int currentSize =sizeAll;
            int currentPos = 0;
            boolean tryUp = true;
            boolean notInRange = true;

            while (notInRange)
            {
                compareCount++;
                currentSize/=2;
                if (currentSize==0)
                {
                    currentSize=1;
                }

                if (tryUp)
                {
                    currentPos = currentPos + (currentSize);
                }
                else
                {
                    currentPos = currentPos - (currentSize);
                }

                if (currentPos < 0)
                {
                    return null;
                }
                if (currentPos >=sizeAll)
                {
                    return null;
                }

                int tmpBigPos = mTiles.get(currentPos).getY()*totalWidth+mTiles.get(currentPos).getX();


                if (tmpBigPos>bigPos)
                {
                    if ((tryUp)&&(currentSize==1))
                    {
                        //System.out.println("Missing Tile: " +x+", "+y);
                        return null;
                    }
                    tryUp = false;
                    continue;
                }
                if (tmpBigPos<bigPos)
                {
                    if ((!tryUp)&&(currentSize==1))
                    {
                        //System.out.println("Missing Tile: " +x+", "+y);
                        return null;
                    }
                    tryUp = true;
                    continue;
                }
                notInRange=false;
            }
            return mTiles.get(currentPos);
        }


        // go thru all tiles this is VERY slow
        for (int i = startAtTile; i < mTiles.size(); i++)
        {
            compareCount++;
            Tile tile = mTiles.get(i);
            int tx = tile.getX();
            int ty = tile.getY();
            
            
            if (mData.mISOBlockWrap) // assumed ISO
            {
                int width =mTiles.get(0).getMaxWidthUnscaled();
                int height =mTiles.get(0).getMaxHeightUnscaled();

                boolean iI = isISOInterior(x-tx, y-ty, width, height)==0;
                if (iI) 
                    return tile;
                continue;
            }
            
            
            

            if ((tx>x) || (tx+tile.getMaxWidthUnscaled()<=x)) continue;
            if ((ty>y) || (ty+tile.getMaxHeightUnscaled()<=y)) continue;
//System.out.println("CompareCount:" +compareCount);
                return tile;
        }
        return null;
    }

    boolean isBorderTile(Tile tile)
    {
        if (isBorderTileNorth(tile)) return true;
        if (isBorderTileSouth(tile)) return true;
        if (isBorderTileEast(tile)) return true;
        if (isBorderTileWest(tile)) return true;
        return false;
    }

    boolean isBorderTileNorth(Tile tile)
    {
        if (!mData.mISOBlockWrap)
            return (tile.getY() == getMinY());
        int height = mTiles.get(0).getMaxHeightUnscaled();
        return (tile.getY() <= getMinY()+height/2);
    }

    boolean isBorderTileWest(Tile tile)
    {
        if (!mData.mISOBlockWrap)
            return (tile.getX() == getMinX());
        int width = mTiles.get(0).getMaxWidthUnscaled();
        return (tile.getX() <= getMinX()+width/2);
    }

    boolean isBorderTileEast(Tile tile)
    {
        if (!mData.mISOBlockWrap)
            return (tile.getX()+tile.getMaxWidthUnscaled() == getMaxX());
        int width = mTiles.get(0).getMaxWidthUnscaled();
        return (tile.getX()+width >= getMaxX()-width/2);
    }

    boolean isBorderTileSouth(Tile tile)
    {
        if (!mData.mISOBlockWrap)
            return (tile.getY()+tile.getMaxHeightUnscaled() == getMaxY());
        int height = mTiles.get(0).getMaxHeightUnscaled();
        return (tile.getY()+height >= getMaxY()-height/2);
    }
            
    // unscaled
    public Tile has_N_Neighbour(Tile tile, boolean considerWrapping)
    {
        if (!mData.mISOBlockWrap)
            return isTileOnPosition(tile.getX()+1, tile.getY()-1, considerWrapping);
        int width = mTiles.get(0).getMaxWidthUnscaled();
        int height = mTiles.get(0).getMaxHeightUnscaled();
        return isTileOnPosition(tile.getX()+width/2-2, tile.getY()-height/2+2, considerWrapping);
    }

    public Tile has_NE_Neighbour(Tile tile, boolean considerWrapping)
    {
        if (!mData.mISOBlockWrap)
            return isTileOnPosition(tile.getX()+1+tile.getMaxWidthUnscaled(), tile.getY()-1, considerWrapping);
        int width = mTiles.get(0).getMaxWidthUnscaled();
        int height = mTiles.get(0).getMaxHeightUnscaled();
        return isTileOnPosition(tile.getX()+width+1, tile.getY()-2, considerWrapping);
    }
    public Tile has_NW_Neighbour(Tile tile, boolean considerWrapping)
    {
        if (!mData.mISOBlockWrap)
            return isTileOnPosition(tile.getX()-1, tile.getY()-1, considerWrapping);
        int width = mTiles.get(0).getMaxWidthUnscaled();
        int height = mTiles.get(0).getMaxHeightUnscaled();
        return isTileOnPosition(tile.getX()-2, tile.getY()-2, considerWrapping);
    }
    public Tile has_S_Neighbour(Tile tile, boolean considerWrapping)
    {
        if (!mData.mISOBlockWrap)
            return isTileOnPosition(tile.getX()+1, tile.getY()+1+tile.getMaxHeightUnscaled(), considerWrapping);
        int width = mTiles.get(0).getMaxWidthUnscaled();
        int height = mTiles.get(0).getMaxHeightUnscaled();
        return isTileOnPosition(tile.getX()+width/2, tile.getY()+height+height/2, considerWrapping);
    }
    public Tile has_SE_Neighbour(Tile tile, boolean considerWrapping)
    {
        if (!mData.mISOBlockWrap)
            return isTileOnPosition(tile.getX()+1+tile.getMaxWidthUnscaled(), tile.getY()+1+tile.getMaxHeightUnscaled(), considerWrapping);
        int width = mTiles.get(0).getMaxWidthUnscaled();
        int height = mTiles.get(0).getMaxHeightUnscaled();
        return isTileOnPosition(tile.getX()+width+2, tile.getY()+height+2, considerWrapping);
    }
    public Tile has_SW_Neighbour(Tile tile, boolean considerWrapping)
    {
        if (!mData.mISOBlockWrap)
            return isTileOnPosition(tile.getX()-1, tile.getY()+1+tile.getMaxHeightUnscaled(), considerWrapping);
        int width = mTiles.get(0).getMaxWidthUnscaled();
        int height = mTiles.get(0).getMaxHeightUnscaled();
        return isTileOnPosition(tile.getX()-2, tile.getY()+height+2, considerWrapping);
    }
    public Tile has_W_Neighbour(Tile tile, boolean considerWrapping)
    {
        if (!mData.mISOBlockWrap)
            return isTileOnPosition(tile.getX()-1, tile.getY()+1, considerWrapping);
        int width = mTiles.get(0).getMaxWidthUnscaled();
        int height = mTiles.get(0).getMaxHeightUnscaled();
        return isTileOnPosition(tile.getX()-width/2, tile.getY()+height/2, considerWrapping);
    }
    public Tile has_E_Neighbour(Tile tile, boolean considerWrapping)
    {
        if (!mData.mISOBlockWrap)
            return isTileOnPosition(tile.getX()+1+tile.getMaxWidthUnscaled(), tile.getY()+1, considerWrapping);
        int width = mTiles.get(0).getMaxWidthUnscaled();
        int height = mTiles.get(0).getMaxHeightUnscaled();
        return isTileOnPosition(tile.getX()+width+width/2, tile.getY()+height/2, considerWrapping);
    }
    
    public boolean saveAsCompositeTileLayer(String layerFileName, String layerClassName, String layerName)
    {
        // in directory XML/TILE
        String layerPoolName = "Tile"+File.separator+layerFileName+".xml";
        LayerDataPool mLayerDataPool = new LayerDataPool(layerPoolName);
        
        mLayerDataPool.put(getData());
        mLayerDataPool.save(0);
        return true;
    }

    public static Layer loadAsCompositeTile(String layerFileName, String layerClassName, String layerName)
    {
        // in directory XML/TILE
        String layerPoolName = "Tile"+File.separator+layerFileName+".xml";
        LayerDataPool mLayerDataPool = new LayerDataPool(layerPoolName);
        Layer l = new Layer(mLayerDataPool.get(layerName));
        l.getData().misBackground=false;
        return l;
    }
    
    // NONE deep clone
    // tiles in layer are the SAME
    // data is different, but vectors in data are the same!
    public Layer cloneLayer()
    {
        Layer l = new Layer();
        l.mTiles = mTiles;
        l.mSprites = mSprites;
        l.isInMap = isInMap;
        l.layerChanged = layerChanged;
        l.totalWidth = totalWidth;
        l.totalHeight = totalHeight;
        l.maxYPos = maxYPos;
        l.minYPos = minYPos;
        l.maxXPos = maxXPos;
        l.minXPos = minXPos;
        l.minTileWidth = minTileWidth;
        l.minTileHeight = minTileHeight;
        l.tilesOverlap = tilesOverlap;

        l.mData.mFogFileName = mData.mFogFileName;
        l.mData.mFogName = mData.mFogName;

        l.mData.mClass = mData.mClass;
        l.mData.mName = mData.mName;
        l.mData.mWrapHorizontal = mData.mWrapHorizontal;
        l.mData.mWrapVertical = mData.mWrapVertical;
        l.mData.mLayerName = mData.mLayerName;
        l.mData.mverticalSpeed = mData.mverticalSpeed;
        l.mData.mhorizontalSpeed = mData.mhorizontalSpeed;
        l.mData.mmustBePainted = mData.mmustBePainted;
        l.mData.misBackgroundCollision = mData.misBackgroundCollision;
        l.mData.misForgroundCollision = mData.misForgroundCollision;
        l.mData.mpurposeTyp = mData.mpurposeTyp;
        l.mData.misChangeable = mData.misChangeable;
        l.mData.mbackgroundColor = mData.mbackgroundColor;
        l.mData.mLayerNumber = mData.mLayerNumber;
        l.mData.mtileX = mData.mtileX;
        l.mData.mtileY = mData.mtileY;
        l.mData.misForgroundCollision = mData.misForgroundCollision;
        l.mData.misBackground = mData.misBackground;
        l.mData.mtileName = mData.mtileName;
        l.mData.misVisible = mData.misVisible;
        l.mData.mscrollBehaviour = mData.mscrollBehaviour;
        l.mData.mallowTileOverlap = mData.mallowTileOverlap;
        l.mData.musedTileCollection = mData.musedTileCollection;
        l.mData.moffsetX = mData.moffsetX;
        l.mData.moffsetY = mData.moffsetY;
        l.mData.misLayerOrdered = mData.misLayerOrdered;
        l.mData.misLayerCompleteOrdered = mData.misLayerCompleteOrdered;
        l.mData.misAllTilesSameSize = mData.misAllTilesSameSize;
        l.mData.misRegularGrid = mData.misRegularGrid;
        l.mData.msaveTyp = mData.msaveTyp;
        l.mData.monlyOneTileOnOnePosition = mData.monlyOneTileOnOnePosition;
        l.mData.mISOBlockWrap = mData.mISOBlockWrap;


        l.mData.mSpriteFilename = mData.mSpriteFilename;
        l.mData.mSpriteName = mData.mSpriteName;
        l.mData.mSpriteStartX = mData.mSpriteStartX;
        l.mData.mSpriteStartY = mData.mSpriteStartY;
        l.mData.mSpriteStartAction = mData.mSpriteStartAction;
        l.mData.mSpriteStartDirection = mData.mSpriteStartDirection;
        l.mData.mSpriteCoordinator = mData.mSpriteCoordinator;
        
        return l;
    }
    
    public void replaceTileAt(Tile source, Tile tile)
    {
        replaceTileAt(source, tile, false, false);
    }
    
    // if add = false, removeAll = false, source is replaced with tile
    // if add = true new tile is added, nothing is removed
    // if removeAll = true (and not add), all tiles on position of source are removed!
    // remove ALL tiles on that pos, not only source
    public void replaceTileAt(Tile source, Tile tile, boolean add, boolean removeAll)
    {
        int index = mTiles.indexOf(source);
        if (index == -1)
        {
            System.out.println("Replace not possible, tile not in list!");
            return;
        }
        if (tile.isCompositeTile())
        {
            System.out.println("Replace not with composite tiles not supported!");
            return;
        }

        int x = source.getX();
        int y = source.getY();
        if (!add)
        {
            if (removeAll)
            {
                Tile toDelete = isTileOnPosition(x, y, true);
                do
                {
                    toDelete.deinit();
                    int deleteIndex = mTiles.indexOf(toDelete);
                    
                    mTiles.remove(deleteIndex);
                    mData.mtileName.remove(deleteIndex);
                    mData.musedTileCollection.remove(deleteIndex);
                    mData.mtileX.remove(deleteIndex);
                    mData.mtileY.remove(deleteIndex);
                    mData.misLayerCompleteOrdered = false;
                    toDelete = isTileOnPosition(x, y, true);
                } while (toDelete != null);
            }
        }
        
        
        
        tile.setX(x);
        tile.setY(y);

        // todo must be sorted!
        mTiles.add(index, tile);
        mData.mtileX.add(index, tile.getX());
        mData.mtileY.add(index, tile.getY());

        if (tile.isEmptyTile())
        {
            mData.mtileName.add(index, tile.toString()); // has size!
            mData.musedTileCollection.add(index,tile.toString());
        }
        else
        {
            mData.mtileName.add(index, tile.getData().mName);
            mData.musedTileCollection.add(index, tile.getData().getTileSetFileName());
        }
        if (!((source.getMaxWidthUnscaled() == tile.getMaxWidthUnscaled()) && (source.getMaxHeightUnscaled() == tile.getMaxHeightUnscaled())))
        {
            mData.misLayerCompleteOrdered = false;
            mData.misLayerOrdered = false;
            mData.misAllTilesSameSize=false;
            mData.misRegularGrid=false;
        }
        tile.setDisplay(mDisplayPanel);
        layerChanged = true;

        if (!add)
        {
            if (!removeAll)
            {
                source.deinit();

                mTiles.remove(index+1);
                mData.mtileName.remove(index+1);
                mData.musedTileCollection.remove(index+1);
                mData.mtileX.remove(index+1);
                mData.mtileY.remove(index+1);
            }
            else
            {
                // todo remove all on pos
            }
        }
        else
        {
            mData.misLayerCompleteOrdered = false;
            mData.misLayerOrdered = false;
        }
    }


}
