package com.clearlyspam23.GLE.basic.layers.tile.gui;
import java.util.HashMap;
import java.util.Map;
import org.piccolo2d.PNode;
import com.clearlyspam23.GLE.basic.layers.tile.Tile;
import com.clearlyspam23.GLE.basic.layers.tile.TileData;
import com.clearlyspam23.GLE.basic.layers.tile.TileLayer;
import com.clearlyspam23.GLE.basic.layers.tile.TileLocation;
import com.clearlyspam23.GLE.basic.layers.tile.TilesetHandle;
import com.clearlyspam23.GLE.basic.layers.tile.TilesetManager;
import com.clearlyspam23.GLE.util.Vector2;
public class TileLayerPNode extends PNode implements TilePNode.TileChangeListener{
/**
*
*/
private static final long serialVersionUID = 1L;
public static final String PROPERTY_GRID_DIMENSIONS = "gridDimensions";
private TilePNode[][] nodeGrid;
private static final double epsilon = 1e-4;
private Vector2 gridDimensions = new Vector2();
private TileLocation gridOffset = new TileLocation();
private TileLayer layer;
private BasePNode base;
private boolean silentlyIgnoringInput = false;
public TileLayerPNode(double gridWidth, double gridHeight){
this(gridWidth, gridHeight, null, null);
}
public TileLayerPNode(double gridWidth, double gridHeight, TileLayer layer, BasePNode base)
{
this.setBase(base);
this.setLayer(layer);
gridDimensions.set(gridWidth, gridHeight);
nodeGrid = new TilePNode[0][0];
}
public double getGridWidth(){
return gridDimensions.x;
}
public double getGridHeight(){
return gridDimensions.y;
}
public Vector2 getGridDimensions(){
return gridDimensions.copy();
}
public void setGridDimensions(double width, double height){
Vector2 old = gridDimensions.copy();
gridDimensions.set(width, height);
if(!old.equals(gridDimensions)){
refreshNodes();
firePropertyChange(0, PROPERTY_GRID_DIMENSIONS, old, gridDimensions.copy());
}
}
@Override
public boolean setBounds(double x, double y, double width, double height){
if(!super.setBounds(x, y, width, height))
return false;
this.removeAllChildren();
refreshNodes();
return true;
}
private void refreshNodes(){
double gridWidth = gridDimensions.x;
double gridHeight = gridDimensions.y;
double rowsd = getWidth()/gridWidth;
double columnsd = getHeight()/gridHeight;
int rows = (int) rowsd;
int columns = (int) columnsd;
TilePNode[][] oldNodeGrid = nodeGrid;
nodeGrid = new TilePNode[rows + (rowsd-epsilon < rows ? 0 : 1)][columns + (columnsd-epsilon < columns ? 0 : 1)];
for(int i = 0; i < rows; i++){
for(int j = 0; j < columns; j++){
if(oldNodeGrid!=null&&i<oldNodeGrid.length&&j<oldNodeGrid[i].length)
addPNode(i, j, gridWidth, gridHeight, oldNodeGrid[i][j]);
else
addNewPNode(i, j, gridWidth, gridHeight);
}
}
if(rows<nodeGrid.length){
double remainder = (rowsd-rows)*gridWidth;
for(int j = 0; j < nodeGrid[rows].length; j++){
if(oldNodeGrid!=null&&rows<oldNodeGrid.length&&j<oldNodeGrid[rows].length)
addPNode(rows, j, remainder, gridHeight, gridWidth, gridHeight, oldNodeGrid[rows][j]);
else
addNewPNode(rows, j, remainder, gridHeight, gridWidth, gridHeight);
}
}
if(nodeGrid.length>0&&columns<nodeGrid[0].length){
double remainder = (columnsd-columns)*gridHeight;
for(int i = 0; i < nodeGrid.length; i++){
if(oldNodeGrid!=null&&i<oldNodeGrid.length&&columns<oldNodeGrid[i].length)
addPNode(i, columns, gridWidth, remainder, gridWidth, gridHeight, oldNodeGrid[i][columns]);
else
addNewPNode(i, columns, gridWidth, remainder, gridWidth, gridHeight);
}
}
}
public void resize(double width, double height){
setBounds(getX(), getY(), width, height);
}
private void addNewPNode(int i, int j, double width, double height){
addNewPNode(i, j, width, height, width, height);
}
private void addNewPNode(int i, int j, double width, double height, double gridWidth, double gridHeight){
addPNode(i, j, width, height, gridWidth, gridHeight, new TilePNode());
}
private void addPNode(int i, int j, double width, double height, TilePNode pnode){
addPNode(i, j, width, height, width, height, pnode);
}
private void addPNode(int i, int j, double width, double height, double gridWidth, double gridHeight, TilePNode pnode){
pnode.silentlyIgnoreInput(silentlyIgnoringInput);
pnode.setTileOffsetReference(this.gridOffset);
pnode.addChangeListener(this);
nodeGrid[i][j] = pnode;
pnode.setGridLocation(i, j);
addChild(nodeGrid[i][j]);
nodeGrid[i][j].setBounds(gridWidth*i, gridHeight*j, width, height);
onNodeAdd(pnode);
}
public TilePNode[][] getNodeGrid(){
return nodeGrid;
}
public TilePNode getNodeAt(int x, int y){
return nodeGrid[x-gridOffset.gridX][y-gridOffset.gridY];
}
public TilePNode getNodeAt(TileLocation location){
return getNodeAt(location.gridX, location.gridY);
}
public TilePNode getNodeAtRaw(int x, int y){
return nodeGrid[x][y];
}
public TilePNode getNodeAtRaw(TileLocation location){
return getNodeAtRaw(location.gridX, location.gridY);
}
public boolean isValidLocation(int x, int y){
return (x-gridOffset.gridX)>=0&&(x-gridOffset.gridX)<nodeGrid.length&&
(y-gridOffset.gridY)>=0&&(y-gridOffset.gridY)<nodeGrid[x-gridOffset.gridX].length;
}
public int getNodeGridWidth(){
return nodeGrid.length;
}
public int getNodeGridHeight(){
return nodeGrid.length > 0 ? nodeGrid[0].length : 0;
}
public boolean isValidLocation(TileLocation location){
return isValidLocation(location.gridX, location.gridY);
}
public void setTile(Tile t){
getNodeAt(t.getLocation()).setTileset(t);
}
public void setTileHard(Tile t){
getNodeAt(t.getLocation()).setTilesetHard(t);
}
public Tile getTile(TileLocation location){
return getNodeAt(location).getTile();
}
public TileData[][] getTiles(){
TileData[][] ans = new TileData[nodeGrid.length][];
for(int i = 0; i < nodeGrid.length; i++){
ans[i] = new TileData[nodeGrid[i].length];
for(int j = 0; j < nodeGrid[i].length; j++){
ans[i][j] = nodeGrid[i][j].getTileData();
}
}
return ans;
}
/**
* calculates a mapping of TilePNodes to their appropriate Tilesets, returning a full grid (width x height of this layer) per each Tileset
* @return a mapping of Tilesets to all the tiles in this grid, in their appropriate spot
*/
public Map<TilesetHandle, TilePNode[][]> getMappedNodeGrid(){
Map<TilesetHandle, TilePNode[][]> ans = new HashMap<TilesetHandle, TilePNode[][]>();
for(int i = 0; i < nodeGrid.length; i++)
{
for(int j = 0; j < nodeGrid[i].length; j++)
{
TilePNode node = nodeGrid[i][j];
TilesetHandle t = node.getTileset();
if(t==null)
continue;
if(!ans.containsKey(t))
ans.put(t, new TilePNode[nodeGrid.length][nodeGrid[i].length]);
ans.get(t)[i][j] = node;
}
}
return ans;
}
/**
* try and refresh all of the nodes in this TileLayer. if there happens to be a problem, clear out that node and report it here
* this should be called whenever new tilesets are added, or old tilesets are modified.
* if an existing tileset in use has been changed such that existing tiles are no longer valid, this method will zero out those tiles and return false
* @param manager the tileset manager to use for updates
* @return whether or not this refresh completed without incident
*/
public boolean refreshNodes(TilesetManager manager){
boolean output = true;
for(int i = 0; i < nodeGrid.length; i++){
for(int j = 0; j < nodeGrid[i].length; j++){
TilePNode node = nodeGrid[i][j];
try{
if(node.getTileset()==null)
continue;
TilesetHandle h = manager.getTilesetByName(node.getTileset().getName());
if(h==null){
throw new NullPointerException();
}
node.setTileset(h, node.getTilesetX(), node.getTilesetY());
}
catch(Exception e){
e.printStackTrace();
node.resetTileset();
output = false;
}
}
}
return output;
}
public void setGridOffset(int x, int y){
gridOffset.set(x, y);
}
public void setGridOffset(TileLocation loc){
setGridOffset(loc.gridX, loc.gridY);
}
public void setGridOffsetX(int x){
setGridOffset(x, gridOffset.gridY);
}
public void setGridOffsetY(int y){
setGridOffset(gridOffset.gridX, y);
}
public int getGridOffsetX(){
return gridOffset.gridX;
}
public int getGridOffsetY(){
return gridOffset.gridY;
}
public TileLocation getGridOffset(){
return gridOffset.copy();
}
public void clearAllTiles(){
for(TilePNode[] p : nodeGrid){
for(TilePNode t : p){
t.resetTilesetHard();
}
}
}
/**
* toggles the silentlyIgnoreInput flag for all nodes in this layer
* this makes it so that any nodes in question will ignore attempts to change their tile
* @param flag whether or not all nodes should silently ignore input
*/
public void silentlyIgnoreInput(boolean flag){
silentlyIgnoringInput = flag;
for(TilePNode[] p : nodeGrid){
for(TilePNode t : p){
t.silentlyIgnoreInput(flag);
}
}
}
public void setTilesPickable(boolean flag){
for(TilePNode[] p : nodeGrid){
for(TilePNode t : p){
t.setPickable(flag);
}
}
}
@Override
public void onChange(TilePNode changedNode, TileData previous, TileData next) {
}
protected void onNodeAdd(TilePNode node) {
}
public TileLayer getLayer() {
return layer;
}
public void setLayer(TileLayer layer) {
this.layer = layer;
}
public BasePNode getBase() {
return base;
}
public void setBase(BasePNode base) {
this.base = base;
}
}