package tc.oc.pgm.spawns;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import javax.annotation.Nullable;
import javax.inject.Inject;
import com.google.common.collect.Lists;
import org.jdom2.Element;
import java.time.Duration;
import tc.oc.pgm.features.FeatureParser;
import tc.oc.pgm.filters.Filter;
import tc.oc.pgm.filters.matcher.StaticFilter;
import tc.oc.pgm.filters.matcher.party.TeamFilter;
import tc.oc.pgm.filters.operator.AllFilter;
import tc.oc.pgm.filters.parser.FilterParser;
import tc.oc.pgm.kits.KitParser;
import tc.oc.pgm.map.MapModuleContext;
import tc.oc.pgm.map.ProtoVersions;
import tc.oc.pgm.map.inject.MapScoped;
import tc.oc.pgm.points.PointParser;
import tc.oc.pgm.points.PointProvider;
import tc.oc.pgm.points.RandomPointProvider;
import tc.oc.pgm.points.SequentialPointProvider;
import tc.oc.pgm.points.SpreadPointProvider;
import tc.oc.pgm.teams.TeamFactory;
import tc.oc.pgm.utils.XMLUtils;
import tc.oc.pgm.xml.InvalidXMLException;
import tc.oc.pgm.xml.Node;
import tc.oc.pgm.xml.property.DurationProperty;
import tc.oc.pgm.xml.property.PropertyBuilderFactory;
@MapScoped
public class SpawnParser {
protected final MapModuleContext context;
protected final PointParser pointParser;
protected final FeatureParser<TeamFactory> teamParser;
protected final PropertyBuilderFactory<Boolean, ?> booleans;
protected final PropertyBuilderFactory<Duration, DurationProperty> durations;
protected final FilterParser filters;
protected final KitParser kits;
protected @Nullable Spawn defaultSpawn;
@Inject private SpawnParser(MapModuleContext context, PointParser pointParser, FeatureParser<TeamFactory> teamParser, PropertyBuilderFactory<Boolean, ?> booleans, PropertyBuilderFactory<Duration, DurationProperty> durations, FilterParser filters, KitParser kits) {
this.context = context;
this.pointParser = pointParser;
this.teamParser = teamParser;
this.booleans = booleans;
this.durations = durations;
this.filters = filters;
this.kits = kits;
}
public @Nullable Spawn getDefaultSpawn() {
return defaultSpawn;
}
public Spawn parse(Element el, SpawnAttributes attributes) throws InvalidXMLException {
attributes = this.parseAttributes(el, attributes);
List<PointProvider> providers;
if(context.getProto().isOlderThan(ProtoVersions.MODULE_SUBELEMENT_VERSION)) {
providers = this.pointParser.parse(el, attributes.providerAttributes);
} else {
providers = new ArrayList<>(pointParser.parseMultiProperty(el, attributes.providerAttributes, "region"));
for(Element elRegions : XMLUtils.getChildren(el, "regions")) {
providers.addAll(this.pointParser.parseChildren(elRegions, attributes.providerAttributes));
}
}
PointProvider provider;
if(attributes.sequential) {
provider = new SequentialPointProvider(providers);
} else if(attributes.spread) {
provider = new SpreadPointProvider(providers);
} else {
provider = new RandomPointProvider(providers);
}
return context.features().define(el, Spawn.class, new SpawnImpl(attributes, provider));
}
public List<Spawn> parseChildren(Element parent, SpawnAttributes attributes) throws InvalidXMLException {
attributes = this.parseAttributes(parent, attributes);
List<Spawn> spawns = Lists.newArrayList();
for(Element spawnsEl : parent.getChildren("spawns")) {
spawns.addAll(this.parseChildren(spawnsEl, attributes));
}
for(Element spawnEl : parent.getChildren("spawn")) {
spawns.add(this.parse(spawnEl, attributes));
}
for(Element defaultEl : parent.getChildren("default")) {
if(defaultSpawn != null) {
throw new InvalidXMLException("Cannot have multiple default spawns", defaultEl);
}
this.defaultSpawn = parse(defaultEl, attributes);
}
return spawns;
}
private SpawnAttributes parseAttributes(Element el, SpawnAttributes parent) throws InvalidXMLException {
return parent.merge(
pointParser.parseAttributes(el, parent.providerAttributes),
parseFilter(el),
kits.property(el).optional(),
booleans.property(el, "sequential").optional(),
booleans.property(el, "spread").optional(),
booleans.property(el, "exclusive").optional(),
booleans.property(el, "persistent").optional()
);
}
private Filter parseFilter(Element el) throws InvalidXMLException {
Filter filter = StaticFilter.ABSTAIN;
final Node nodeTeam = Node.fromAttr(el, "team");
if(nodeTeam != null) {
filter = AllFilter.of(filter, new TeamFilter(teamParser.parseReference(nodeTeam)));
}
final Optional<Filter> prop = filters.property(el).optional();
if(prop.isPresent()) {
filter = AllFilter.of(filter, prop.get());
}
return filter;
}
}