package de.frajul.endlessroll.main.game;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import de.frajul.endlessroll.data.Vector;
import de.frajul.endlessroll.entities.AnimatedEntity;
import de.frajul.endlessroll.entities.Background;
import de.frajul.endlessroll.entities.Entity;
import de.frajul.endlessroll.entities.Obstacle;
import de.frajul.endlessroll.entities.Player;
import de.frajul.endlessroll.entities.collectables.Collectables;
import de.frajul.endlessroll.entities.particles.ParticleSystem;
import de.frajul.endlessroll.entities.textures.TexturePack;
import de.frajul.endlessroll.entities.tileLists.Ceiling;
import de.frajul.endlessroll.entities.tileLists.Terrain;
import de.frajul.endlessroll.entities.tools.Tool;
import de.frajul.endlessroll.levels.worlds.World;

/**
 * Created by Julian on 20.07.2016.
 */
public abstract class Scene {

    //Not sure if needed, this why set on very high value
    private final float MAX_Y_MOVEMENT = -1f;

    protected Camera camera;
    private Vector screenSize;
    private Entity playerArrow;

    protected ParticleSystem particleSystem;
    protected TexturePack textures;
    protected Background background;
    protected Terrain terrain;
    protected Ceiling ceiling;
    protected Player player;

    protected List<Entity> uncategorizedEntities = Collections
            .synchronizedList(new ArrayList<Entity>());
    protected List<Obstacle> obstacles = Collections.synchronizedList(new ArrayList<Obstacle>());
    protected List<Tool> tools = Collections.synchronizedList(new ArrayList<Tool>());
    protected Collectables collectables = new Collectables();

    public Scene(TexturePack texturePack, ParticleSystem particleSystem) {
        this.particleSystem = particleSystem;
        setTexturePack(texturePack);
        camera = new Camera();
        playerArrow = new Entity(textures.playerArrow, new Vector(0, 0.9f), .2f, .2f);
        background = new Background(World.GRASSLANDS.getBackgroundTexture());
        terrain = new Terrain(World.GRASSLANDS.getTerrainTexture());
        ceiling = new Ceiling(World.GRASSLANDS.getTerrainTexture());
        player = new Player();
    }

    public void setTexturePack(TexturePack texturePack) {
        this.textures = texturePack;
    }

    public void reset() {
        uncategorizedEntities.clear();
        obstacles.clear();
        tools.clear();
        collectables.reset();
        camera.reset();
        background.resetPosition();
    }

    public void update(Timer timer) {
        updateEntityList(uncategorizedEntities, timer);
        updateEntityList(obstacles, timer);
        updateEntityList(tools, timer);
        updateEntityList(collectables, timer);

        if (player.getPosition().y >= player.RADIUS + 1 + camera.getY()) {
            playerArrow.getPosition().x = player.getPosition().x;
            playerArrow.getPosition().y = camera.getY() + 0.9f;
            if (!uncategorizedEntities.contains(playerArrow)) {
                uncategorizedEntities.add(playerArrow);
            }
        } else
            uncategorizedEntities.remove(playerArrow);
    }

    private void updateEntityList(List<? extends Entity> list, Timer timer) {
        synchronized (list) {
            Iterator<? extends Entity> iterator = list.iterator();
            while (iterator.hasNext()) {
                Entity entity = iterator.next();
                if (entity instanceof Obstacle) {
                    Obstacle obstacle = (Obstacle) entity;
                    if (obstacle.isMoving() && obstacle.isPlayerOverTrigger(player))
                        obstacle.moveWithMoveComponent(timer.getFrameTimeSeconds(), player);
                }
                boolean remove = updateEntity(entity, timer);
                if (remove)
                    iterator.remove();
            }
        }
    }

    private boolean updateEntity(Entity entity, Timer timer) {
        if (entity instanceof AnimatedEntity)
            ((AnimatedEntity) entity).update(timer);
        if (!(entity instanceof Obstacle)) {
            Vector movement = entity.getMovement();
            Vector finalMovement = new Vector(movement).mul(timer.getFrameTimeSeconds());
            if (finalMovement.y < MAX_Y_MOVEMENT)
                finalMovement.y = MAX_Y_MOVEMENT;
            entity.move(finalMovement);
            if (entity.equals(player))
                moveEnviroment(finalMovement.x);
        }

        if (entity.isDestroyed() && entity.getDestroyEffect() != null)
            entity.getDestroyEffect().createEffect(particleSystem, new Vector(entity.getPosition()),
                    new Vector(entity.getWidth(), entity.getHeight())).start();
        if (entity.getRightEdge() - camera.getX() < -3f || entity.isDestroyed()) {
            return true;
        }
        return false;
    }

    private void moveEnviroment(float x) {
        camera.moveX(x);
        background.move(x * 0.95f, camera.getX());
        terrain.update(camera.getX());
        ceiling.update(camera.getX());
    }

    protected Vector calcWorldFromScreenCoords(float screenX, float screenY) throws Exception {
        if (screenSize == null)
            throw new Exception("ScreenSize not set");
        float glCoordWidth = (2f * screenSize.x / screenSize.y);
        float x = ((screenX / screenSize.x) * 2f - 1f) * glCoordWidth / 2;
        x += camera.getX();
        float y = -((screenY / screenSize.y) * 2f - 1f);
        y += camera.getY();
        return new Vector(x, y);
    }

    public void setScreenSize(Vector screenSize) {
        this.screenSize = screenSize;
    }

    public synchronized Background getBackground() {
        return background;
    }

    public synchronized Terrain getTerrain() {
        return terrain;
    }

    public synchronized Ceiling getCeiling() {
        return ceiling;
    }

    public synchronized List<Entity> getUncategorizedEntities() {
        return uncategorizedEntities;
    }

    public synchronized List<Obstacle> getObstacles() {
        return obstacles;
    }

    public synchronized List<Tool> getTools() {
        return tools;
    }

    public synchronized Collectables getCollectables() {
        return collectables;
    }

    public Player getPlayer() {
        return player;
    }

    public TexturePack getTextures() {
        return textures;
    }

    public ParticleSystem getParticleSystem() {
        return particleSystem;
    }

    public Camera getCamera() {
        return camera;
    }
}
