package framework.collision;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import main.L;
import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.geom.Vector2f;
import framework.component.Component;
import framework.component.ComponentSystem;
import framework.component.filter.ComponentFilter;
import framework.component.filter.ComponentPathFilter;
import framework.component.filter.SpacialFilter;
import framework.component.list.MultiTypeComponentList;
import framework.component.list.SingleTypeComponentList;
import framework.component.path.RelativeComponentPath;
import framework.event.Event;
import framework.event.EventListener;
import framework.event.EventSender;
import framework.event.EventSystem;
import framework.event.filter.EventSenderComponentFilter;
import framework.event.util.ValueChangedEvent;
import framework.io.CustomInputStream;
import framework.rendering.RenderableComponent;
import framework.spacial.OrientationComponent;
import framework.spacial.OrientationComponent.OrientationChangedEvent;
import framework.spacial.PositionComponent;
public class CollisionComponent extends RenderableComponent implements EventListener{
private CollisionMask mask;
private boolean ignoreCollisions = false;
private HashSet<CollisionComponent> inContactWith = new HashSet<CollisionComponent>();
public CollisionComponent(CollisionMask mask){
super();
this.mask = mask;
RelativeComponentPath posPath = new RelativeComponentPath(PositionComponent.class.getName(), this, 1, new int[]{0});
ComponentFilter posFilter = new ComponentPathFilter(posPath);
EventSystem.getInstance().registerEventListener(this, "PositionChanged", new EventSenderComponentFilter(posFilter));
RelativeComponentPath orientPath = new RelativeComponentPath(OrientationComponent.class.getName(), this, 1, new int[]{0});
ComponentFilter orientFilter = new ComponentPathFilter(orientPath);
EventSystem.getInstance().registerEventListener(this, "OrientationChanged", new EventSenderComponentFilter(orientFilter));
}
public CollisionComponent(CustomInputStream in, int baseID, byte versionNum) throws IOException {
super(in, baseID, versionNum);
}
@Override
public boolean allowSameTypedSiblings() {
return true;
}
@Override
protected byte getDataFormatVersion() {
return 0;
}
@Override
public void onEvent(Event e) {
if(e != null && !ignoreCollisions){
if(e.getType().equals("PositionChanged") && e.getSender() != null){
checkForCollisions((PositionComponent) e.getSender());
}else if (e.getType().equals("OrientationChanged")){
OrientationChangedEvent o = (OrientationChangedEvent) e;
mask.rotate(o.getDelta());
checkForCollisions();
}
}
}
private void checkForCollisions(){
checkForCollisions((PositionComponent) getSiblingByType(PositionComponent.class.getName()));
}
private void checkForCollisions(PositionComponent pos) {
if(pos != null && pos.getVector() != null && this.mask != null){
checkOldCollisionsAreStillHappening(pos.getVector());
MultiTypeComponentList allComps = ComponentSystem.getInstance().getAllComponents();
SingleTypeComponentList allCollisComps = allComps.getComponentsOfType(CollisionComponent.class.getName());
if(allCollisComps != null){
SpacialFilter filter = new SpacialFilter(pos.getVector(), getMask());
Collection<Component> allColliding = allCollisComps.getComponents(filter);
if(allColliding != null){
for(Component comp:allColliding){
if(!comp.equals(this)){
CollisionComponent c = (CollisionComponent) comp;
if(!isInContactWith(c, false)){
inContactWith.add(c);
c.getAllComponentsInContact().add(this);
EventSystem.getInstance().pushEvent(new CollisionStartedEvent(this, c));
EventSystem.getInstance().pushEvent(new CollisionStartedEvent(c, this));
}
EventSystem.getInstance().pushEvent(new CollisionEvent(this,c));
EventSystem.getInstance().pushEvent(new CollisionEvent(c, this));
}
}
}
}
}else{
inContactWith.clear();
}
}
private void checkOldCollisionsAreStillHappening(Vector2f thisOrigin) {
ArrayList<CollisionComponent> toRemove = new ArrayList<CollisionComponent>();
for(CollisionComponent c:inContactWith){
if(!isCollision(thisOrigin,c)){
toRemove.add(c);
}
}
for(CollisionComponent c:toRemove){
inContactWith.remove(c);
c.getAllComponentsInContact().remove(this);
EventSystem.getInstance().pushEvent(new CollisionStoppedEvent(this, c));
EventSystem.getInstance().pushEvent(new CollisionStoppedEvent(c, this));
}
}
private boolean isCollision(Vector2f thisOrigin, CollisionComponent otherComp){
SpacialFilter filter = new SpacialFilter(thisOrigin, this.mask);
return filter.isValidComponent(otherComp);
}
public boolean isCollision(Vector2f thisOrigin, Vector2f otherOrigin, CollisionMask otherMask) {
return mask == null ? false : mask.isCollision(thisOrigin, otherOrigin, otherMask);
}
public float getMaxWidth() {
return mask != null ? mask.getMaxWidth() : 0;
}
public float getMaxHeight() {
return mask != null ? mask.getMaxHeight() : 0;
}
public CollisionMask getMask() {
return mask;
}
public void setMask(CollisionMask mask) {
this.mask = mask;
EventSystem.getInstance().pushEvent(new MaskChangedEvent(this, this.mask, mask));
checkForCollisions();
}
public Set<CollisionComponent> getAllComponentsInContact(){
return inContactWith;
}
public boolean isInContactWith(CollisionComponent collisComp, boolean recheckCollisions){
if(recheckCollisions){
checkForCollisions();
}
return inContactWith.contains(collisComp);
}
public class MaskChangedEvent extends ValueChangedEvent<CollisionMask>{
public MaskChangedEvent(EventSender sender, CollisionMask from,
CollisionMask to) {
super(sender, from, to);
}
@Override
protected String getEventTypeID() {
return "MaskChanged";
}
}
public static class CollisionEvent extends Event{
private final CollisionComponent collidedWith;
public CollisionEvent(CollisionComponent sender, CollisionComponent collidedWith) {
super(sender);
this.collidedWith = collidedWith;
}
@Override
protected String getEventTypeID() {
return "Collision";
}
public CollisionComponent getCollidedWith() {
return collidedWith;
}
}
public static class CollisionStartedEvent extends CollisionEvent{
public CollisionStartedEvent(CollisionComponent sender, CollisionComponent collidedWith) {
super(sender, collidedWith);
}
@Override
protected String getEventTypeID() {
return "CollisionStarted";
}
}
public static class CollisionStoppedEvent extends CollisionEvent{
public CollisionStoppedEvent(CollisionComponent sender, CollisionComponent finishedCollidingWith) {
super(sender, finishedCollidingWith);
}
@Override
protected String getEventTypeID() {
return "CollisionStopped";
}
}
@Override
public void onSiblingAdded(Component sibling) {
super.onSiblingAdded(sibling);
if(sibling.isType(OrientationComponent.class)){
mask.rotate(((OrientationComponent) sibling).getValue());
}
}
public boolean isIgnoringCollision() {
return ignoreCollisions;
}
public void setIgnoreCollisions(boolean ignoreCollision) {
this.ignoreCollisions = ignoreCollision;
}
@Override
public void draw(Graphics g, int x, int y) {
g.setColor(Color.white);
if(getMask() != null){
getMask().renderForDebug(g, new Vector2f(x, y));
}
}
@Override
public int getRenderDepth() {
return RenderableComponent.ALWAYS_ON_TOP;
}
@Override
public void setOffsetFromOrigin(Vector2f offsetFromOrigin) {
super.setOffsetFromOrigin(offsetFromOrigin);
if(mask != null){
mask.setRelativePositionOfOrigin(offsetFromOrigin);
}
}
}