package tc.oc.pgm.structure;
import java.util.Optional;
import javax.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import org.bukkit.World;
import org.bukkit.block.BlockImage;
import org.bukkit.geometry.CoarseTransform;
import org.bukkit.util.ImVector;
import tc.oc.commons.core.inject.InnerFactory;
import tc.oc.pgm.features.FeatureDefinition;
import tc.oc.pgm.features.FeatureFactory;
import tc.oc.pgm.features.FeatureInfo;
import tc.oc.pgm.features.MatchFeatureContext;
import tc.oc.pgm.filters.Filter;
import tc.oc.pgm.filters.FilterMatchModule;
import tc.oc.pgm.filters.query.TransientQuery;
import tc.oc.pgm.match.Match;
import static com.google.common.base.Preconditions.checkNotNull;
@FeatureInfo(name = "dynamic")
public interface DynamicDefinition extends FeatureDefinition, FeatureFactory<Dynamic> {}
class DynamicDefinitionImpl extends FeatureDefinition.Impl implements DynamicDefinition {
interface Factory {
DynamicDefinitionImpl create(StructureDefinition structure,
@Assisted("trigger") Filter trigger,
@Assisted("passive") Filter passive,
@Assisted("position") Optional<ImVector> position,
@Assisted("offset") Optional<ImVector> offset);
}
private final @Inspect StructureDefinition structure;
private final @Inspect Filter trigger;
private final @Inspect Filter passive;
private final @Inspect Optional<ImVector> position;
private final @Inspect Optional<ImVector> offset;
private final InnerFactory<DynamicDefinitionImpl, DynamicImpl> factory;
@Inject DynamicDefinitionImpl(@Assisted StructureDefinition structure,
@Assisted("trigger") Filter trigger,
@Assisted("passive") Filter passive,
@Assisted("position") Optional<ImVector> position,
@Assisted("offset") Optional<ImVector> offset,
InnerFactory<DynamicDefinitionImpl, DynamicImpl> factory) {
this.structure = checkNotNull(structure);
this.trigger = checkNotNull(trigger);
this.passive = checkNotNull(passive);
this.position = position;
this.offset = offset;
this.factory = factory;
}
@Override
public Dynamic createFeature(Match match) {
return factory.create(this);
}
@Override
public void load(Match match) {
match.features().get(this);
}
class DynamicImpl implements Dynamic {
final World world;
final Structure structure;
final ImVector offset;
final BlockImage clearImage;
// Since the passive filter can skip placing the structure,
// we need to keep track of whether its placed or not if we
// want to avoid unnecessary clears.
boolean placed;
@Inject DynamicImpl(Match match, World world, MatchFeatureContext features, DynamicScheduler scheduler, FilterMatchModule fmm) {
this.world = world;
final DynamicDefinitionImpl def = DynamicDefinitionImpl.this;
this.structure = features.get(def.structure);
this.offset = position.map(p -> p.minus(def.structure.origin()))
.orElse(def.offset.orElse(ImVector.ofZero()));
this.clearImage = world.copyBlocks(structure.dynamicBlocks().transform(CoarseTransform.translation(offset)), true, false);
fmm.onChange(Match.class, trigger, (m, response) -> {
if(response) {
// This is the passive filter, not the dynamic one
if(passive.query(new TransientQuery(match)).toBoolean(true)) {
scheduler.queuePlace(this);
}
} else {
scheduler.queueClear(this);
}
});
}
@Override
public DynamicDefinition getDefinition() {
return DynamicDefinitionImpl.this;
}
@Override
public void place() {
if(!placed) {
placed = true;
structure.place(world, offset);
}
}
@Override
public void clear() {
if(placed) {
placed = false;
world.pasteBlocks(clearImage);
}
}
}
}