/*
 * Copyright (c) 2014, Miguel Angel Astor Romero
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer. 
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * Read the LICENSE file for more details.
 */
package com.gamejolt.mikykr5.ceidecpong.ecs.systems;

import com.badlogic.ashley.core.ComponentType;
import com.badlogic.ashley.core.Engine;
import com.badlogic.ashley.core.Entity;
import com.badlogic.ashley.core.Family;
import com.badlogic.ashley.systems.IteratingSystem;
import com.badlogic.ashley.utils.ImmutableArray;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.gamejolt.mikykr5.ceidecpong.ProjectConstants;
import com.gamejolt.mikykr5.ceidecpong.ecs.components.BoundingBoxComponent;
import com.gamejolt.mikykr5.ceidecpong.ecs.components.Mappers;
import com.gamejolt.mikykr5.ceidecpong.ecs.components.PlayerComponent;
import com.gamejolt.mikykr5.ceidecpong.ecs.components.PositionComponent;
import com.gamejolt.mikykr5.ceidecpong.ecs.components.SoundComponent;
import com.gamejolt.mikykr5.ceidecpong.ecs.components.SpriteComponent;
import com.gamejolt.mikykr5.ceidecpong.ecs.components.VelocityComponent;
import com.gamejolt.mikykr5.ceidecpong.ecs.systems.messaging.InterSystemMessage;
import com.gamejolt.mikykr5.ceidecpong.ecs.systems.messaging.InterSystemMessagingQueue;

public class CollisionDetectionSystem extends IteratingSystem {
	private ImmutableArray<Entity> collidables;
	private final float            screenLeftBorder;
	private final float            screenRightBorder;
	private final float            screenTopBorder;
	private final float            screenBottomBorder;
	private final Vector2          randomVector = new Vector2();

	@SuppressWarnings("unchecked")
	public CollisionDetectionSystem(Engine engine){
		super(Family.getFor(ComponentType.getBitsFor(PositionComponent.class, BoundingBoxComponent.class, VelocityComponent.class), ComponentType.getBitsFor(), ComponentType.getBitsFor(PlayerComponent.class)));

		collidables = engine.getEntitiesFor(Family.getFor(BoundingBoxComponent.class));
		screenLeftBorder = -((float)ProjectConstants.FB_WIDTH / 2.0f);
		screenRightBorder = ((float)ProjectConstants.FB_WIDTH / 2.0f) - 1.0f;
		screenTopBorder = ((float)ProjectConstants.FB_HEIGHT / 2.0f) - 1.0f;
		screenBottomBorder = -((float)ProjectConstants.FB_HEIGHT / 2.0f);
	}

	@Override
	public void processEntity(Entity entity, float deltaTime){
		InterSystemMessage   message;
		PositionComponent    position = Mappers.positionMapper.get(entity);
		BoundingBoxComponent bounds   = Mappers.bboxMapper.get(entity);
		VelocityComponent    velocity = Mappers.velocityMapper.get(entity);
		SoundComponent       sound    = Mappers.soundMapper.get(entity);

		// Check if this entity is within the screen.
		// If the entity collides with any of the borders then bounce or score as needed.
		if(position.x < screenLeftBorder){
			resetEntity(entity);

			message = new InterSystemMessage(ScoringSystem.class.getCanonicalName());
			message.data.put("SCORE", 1);
			InterSystemMessagingQueue.pushMessage(message);

			message = new InterSystemMessage(SoundSystem.class.getCanonicalName());
			message.data.put("PLAY", "data/sfx/atari_boom.ogg");
			InterSystemMessagingQueue.pushMessage(message);
		}

		if(position.x + bounds.bbox.getWidth() >= screenRightBorder){
			resetEntity(entity);

			message = new InterSystemMessage(ScoringSystem.class.getCanonicalName());
			message.data.put("SCORE", 0);
			InterSystemMessagingQueue.pushMessage(message);

			message = new InterSystemMessage(SoundSystem.class.getCanonicalName());
			message.data.put("PLAY", "data/sfx/oh_yeah_wav_cut.ogg");
			InterSystemMessagingQueue.pushMessage(message);
		}

		if(position.y < screenBottomBorder){
			position.y = screenBottomBorder;
			velocity.vy = velocity.vy < 0.0f ? -velocity.vy : velocity.vy;
			accelerate(velocity);
			if(sound != null){
				message = new InterSystemMessage(SoundSystem.class.getCanonicalName());
				message.data.put("PLAY", sound.path);
				InterSystemMessagingQueue.pushMessage(message);
			}
		}

		if(position.y + bounds.bbox.getHeight() >= screenTopBorder){
			position.y = screenTopBorder - bounds.bbox.getHeight();
			velocity.vy = velocity.vy > 0.0f ? -velocity.vy : velocity.vy;
			accelerate(velocity);
			if(sound != null){
				message = new InterSystemMessage(SoundSystem.class.getCanonicalName());
				message.data.put("PLAY", sound.path);
				InterSystemMessagingQueue.pushMessage(message);
			}
		}

		for(int i = 0; i < collidables.size(); i++){
			BoundingBoxComponent collidable;
			PositionComponent    colPosition;

			if(collidables.get(i).getIndex() == entity.getIndex()){
				continue;
			}else{
				collidable = Mappers.bboxMapper.get(collidables.get(i));
				colPosition = Mappers.positionMapper.get(collidables.get(i));
				if(colPosition == null)
					continue;

				bounds.bbox.setPosition(position.x, position.y);
				collidable.bbox.setPosition(colPosition.x, colPosition.y);

				if(collidesLeft(bounds.bbox, collidable.bbox)){
					velocity.vx = velocity.vx < 0.0f ? -velocity.vx : velocity.vx;
					accelerate(velocity);

					if(sound != null){
						message = new InterSystemMessage(SoundSystem.class.getCanonicalName());
						message.data.put("PLAY", sound.path);
						InterSystemMessagingQueue.pushMessage(message);
					}
				}else if(collidesRight(bounds.bbox, collidable.bbox)){
					velocity.vx = velocity.vx > 0.0f ? -velocity.vx : velocity.vx;
					accelerate(velocity);

					if(sound != null){
						message = new InterSystemMessage(SoundSystem.class.getCanonicalName());
						message.data.put("PLAY", sound.path);
						InterSystemMessagingQueue.pushMessage(message);
					}
				}
			}
		}

		message = new InterSystemMessage(ComputerPlayerPositioningSystem.class.getCanonicalName());
		message.data.put("BALL_Y", position.y);
		InterSystemMessagingQueue.pushMessage(message);
	}

	private void accelerate(VelocityComponent velocity){
		velocity.vx *= 1.03f;
		velocity.vy *= 1.03f;
	}

	private boolean collidesLeft(Rectangle a, Rectangle b){
		float leftBottomCornerY, leftTopCornerY, leftCenterY;

		leftBottomCornerY = a.y;
		leftTopCornerY = a.y + a.height;
		leftCenterY = a.y + (a.height / 2);

		return b.contains(a.x, leftBottomCornerY) || b.contains(a.x, leftTopCornerY) || b.contains(a.x, leftCenterY);
	}

	private boolean collidesRight(Rectangle a, Rectangle b){
		float x, rightBottomCornerY, rightTopCornerY, rightCenterY;

		x = a.x + a.width;
		rightBottomCornerY = a.y;
		rightTopCornerY = a.y + a.height;
		rightCenterY = a.y + (a.height / 2);

		return b.contains(x, rightBottomCornerY) || b.contains(x, rightTopCornerY) || b.contains(x, rightCenterY);
	}

	private void resetEntity(Entity entity){
		PositionComponent position     = Mappers.positionMapper.get(entity);
		SpriteComponent   sprite       = Mappers.spriteMapper.get(entity);
		VelocityComponent velocity     = Mappers.velocityMapper.get(entity);
		int               randomSign   = MathUtils.random(-1, 1) >= 0 ? 1 : -1;

		randomVector.set(Vector2.X).setAngle(MathUtils.random(-60, 60));
		velocity.setXY(randomVector.x * -475 * randomSign, randomVector.y * 475 * randomSign);

		if(position != null){
			if(sprite != null){
				position.setXY(-(sprite.sprite.getWidth() / 2), -(sprite.sprite.getHeight() / 2));
			}else{
				position.setXY(0, 0);
			}
		}
	}
}
