package rabbitescape.engine;
import java.util.ArrayList;
import java.util.List;
import rabbitescape.engine.ChangeDescription.State;
import rabbitescape.engine.World.CantAddTokenOutsideWorld;
import rabbitescape.engine.World.NoBlockFound;
import rabbitescape.engine.World.NoSuchAbilityInThisWorld;
import rabbitescape.engine.World.NoneOfThisAbilityLeft;
import rabbitescape.engine.World.UnableToAddToken;
import rabbitescape.engine.util.Position;
public class WorldChanges
{
private final World world;
public final WorldStatsListener statsListener;
private final List<Rabbit> rabbitsToEnter = new ArrayList<Rabbit>();
private final List<Rabbit> rabbitsToKill = new ArrayList<Rabbit>();
private final List<Rabbit> rabbitsToSave = new ArrayList<Rabbit>();
private final List<Token> tokensToAdd = new ArrayList<Token>();
public final List<Token> tokensToRemove = new ArrayList<Token>();
public final List<Fire> fireToRemove = new ArrayList<Fire>();
private final List<Block> blocksToAdd = new ArrayList<Block>();
private final List<Block> blocksToRemove = new ArrayList<Block>();
public final List<Position> blocksJustRemoved = new ArrayList<Position>();
private final List<Position> waterPointsToRecalculate = new ArrayList<>();
private boolean explodeAll = false;
private List<Rabbit> rabbitsJustEntered = new ArrayList<Rabbit>();
public WorldChanges( World world, WorldStatsListener statsListener )
{
this.world = world;
this.statsListener = statsListener;
updateStats();
}
public synchronized void apply()
{
// Add any new things
for ( Rabbit rabbit : rabbitsToEnter )
{
rabbit.calcNewState( world );
}
world.rabbits.addAll( rabbitsToEnter );
world.things.addAll( tokensToAdd );
world.blockTable.addAll( blocksToAdd );
// Remove dead/saved rabbits, used tokens, dug out blocks
world.rabbits.removeAll( rabbitsToKill );
world.rabbits.removeAll( rabbitsToSave );
world.things.removeAll( tokensToRemove );
world.things.removeAll( fireToRemove );
world.blockTable.removeAll( blocksToRemove );
for ( Position point : waterPointsToRecalculate )
{
world.recalculateWaterRegions( point );
}
if ( rabbitsToSave.size() > 0 )
{
updateStats();
}
rabbitsToEnter.clear();
rabbitsToKill.clear();
rabbitsToSave.clear();
tokensToAdd.clear();
tokensToRemove.clear();
fireToRemove.clear();
blocksToAdd.clear();
blocksToRemove.clear();
waterPointsToRecalculate.clear();
if ( explodeAll )
{
doExplodeAll();
}
}
private void updateStats()
{
statsListener.worldStats( world.num_saved, world.num_to_save );
}
private void doExplodeAll()
{
world.num_waiting = 0;
for ( Rabbit rabbit : world.rabbits )
{
rabbit.state = State.RABBIT_EXPLODING;
}
}
public synchronized void revert()
{
revertEnterRabbits();
revertKillRabbits();
revertSaveRabbits();
revertAddTokens();
tokensToRemove.clear();
blocksToAdd.clear();
blocksToRemove.clear();
waterPointsToRecalculate.clear();
}
private synchronized void revertEnterRabbits()
{
world.num_waiting += rabbitsToEnter.size();
rabbitsToEnter.clear();
}
public synchronized void enterRabbit( Rabbit rabbit )
{
--world.num_waiting;
rabbitsToEnter.add( rabbit );
}
private synchronized void revertKillRabbits()
{
world.num_killed -= rabbitsToKill.size();
rabbitsToKill.clear();
}
public synchronized void killRabbit( Rabbit rabbit )
{
++world.num_killed;
rabbitsToKill.add( rabbit );
}
private void revertSaveRabbits()
{
world.num_saved -= rabbitsToSave.size();
rabbitsToSave.clear();
}
public synchronized void saveRabbit( Rabbit rabbit )
{
++world.num_saved;
rabbitsToSave.add( rabbit );
}
private synchronized void revertAddTokens()
{
for ( Token t : tokensToAdd )
{
world.abilities.put( t.type, world.abilities.get( t.type ) + 1 );
}
tokensToAdd.clear();
}
public synchronized void addToken( int x, int y, Token.Type type )
throws UnableToAddToken
{
Integer numLeft = world.abilities.get( type );
if ( numLeft == null )
{
throw new NoSuchAbilityInThisWorld( type );
}
if ( numLeft == 0 )
{
throw new NoneOfThisAbilityLeft( type );
}
if ( x < 0 || y < 0 || x >= world.size.width || y >= world.size.height )
{
throw new CantAddTokenOutsideWorld( type, x, y, world.size );
}
Block block = world.getBlockAt( x, y );
if ( BehaviourTools.s_isFlat( block ) )
{
return;
}
tokensToAdd.add( new Token( x, y, type, world ) );
world.abilities.put( type, numLeft - 1 );
}
public synchronized void removeToken( Token thing )
{
tokensToRemove.add( thing );
}
public synchronized void removeFire( Fire thing )
{
fireToRemove.add( thing );
}
public synchronized void addBlock( Block block )
{
blocksToAdd.add( block );
waterPointsToRecalculate.add( new Position( block.x, block.y ) );
}
public synchronized void removeBlockAt( int x, int y )
{
Block block = world.getBlockAt( x, y );
if ( block == null )
{
throw new NoBlockFound( x, y );
}
blocksJustRemoved.add( new Position( x, y ) );
blocksToRemove.add( block );
waterPointsToRecalculate.add( new Position( x, y ) );
}
public synchronized List<Thing> tokensAboutToAppear()
{
return new ArrayList<Thing>( tokensToAdd );
}
public synchronized void explodeAllRabbits()
{
explodeAll = true;
}
public List<Rabbit> rabbitsJustEntered()
{
return rabbitsJustEntered;
}
public void rememberWhatWillHappen()
{
rabbitsJustEntered = new ArrayList<Rabbit>( rabbitsToEnter );
}
}