package com.jan.scene;

import java.util.ArrayList;

import com.badlogic.gdx.maps.MapLayer;
import com.badlogic.gdx.maps.MapObject;
import com.badlogic.gdx.maps.tiled.TiledMapImageLayer;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.TiledMapTileLayer;
import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;
import com.jan.entity.Actor;
import com.jan.entity.Animator;
import com.jan.entity.Renderable;
import com.jan.entity.Trigger;
import com.jan.game.Display;

public class World {
    private TiledMap tiledMap;
    private WorldRenderer worldRenderer;

    private ArrayList<Layer> layers;

    private ArrayList<Renderable> renderables;
    private ArrayList<Entity> nonRenderables;

    public World(TiledMap tiledMap){
        this.tiledMap = tiledMap;
        WorldLoader loader = new WorldLoader(tiledMap);
        layers = loader.getLayers();
        renderables = loader.getRenderables();
        nonRenderables = loader.getNonRenderables();
        worldRenderer = new WorldRenderer(tiledMap);
    }

    public void render(){
        worldRenderer.render(layers);
    }

    public Renderable getRenderable(String name){
        for(int i = 0; i < renderables.size(); i++){
            if(renderables.get(i).getEntityName() == name)
                return renderables.get(i);
        }
        return null;
    }

    public ArrayList<Renderable> getRenderables(){ return renderables; }
    public ArrayList<Entity> getNonRenderables(){ return nonRenderables; }
    public ArrayList<Entity> getEntities(){
        ArrayList<Entity> entities = new ArrayList<>();
        for(int i = 0; i < renderables.size(); i++)
            entities.add(renderables.get(i));
        for(int i = 0; i < nonRenderables.size(); i++)
            entities.add(nonRenderables.get(i));
        return entities;
    }

    public TiledMap getMap(){ return tiledMap; }

}

enum LAYER_TYPES{STATIC_IMAGE, TILES, OBJECTS}

class WorldRenderer{
	private OrthogonalTiledMapRenderer  tiledMapRenderer;

    public WorldRenderer(TiledMap tiledMap){
        tiledMapRenderer = new OrthogonalTiledMapRenderer(tiledMap, 1/32f);
    }

    public void render(ArrayList<Layer> layers){ 
        tiledMapRenderer.setView(Display.getCamera());
        tiledMapRenderer.getBatch().begin();
        for(int i = 0; i < layers.size(); i++)
            renderLayer(layers.get(i));
        
        tiledMapRenderer.getBatch().end();
    }

    private void renderLayer(Layer curr){
        switch(curr.getType()){
            case OBJECTS: renderObjectLayer(curr.getObjectLayer());break;
            case STATIC_IMAGE: tiledMapRenderer.renderImageLayer(curr.getImageLayer()); break;
            case TILES: tiledMapRenderer.renderTileLayer(curr.getTileLayer()); break;
        }
    }

    private void renderObjectLayer(ArrayList<Renderable> objLayer){
        for(int i = 0; i < objLayer.size(); i++){
            Renderable obj = objLayer.get(i);
            obj.update();
            tiledMapRenderer.getBatch().draw(obj.getTexture(), obj.getX() /32, obj.getY() /32 , obj.getWidth() /32, obj.getHeight() /32);
        }
    }
}

class WorldLoader{
    private TiledMap worldMap;
    private ArrayList<Layer> worldLayers;

    public WorldLoader(TiledMap worldMap){
        this.worldMap = worldMap;
        worldLayers = new ArrayList<>();
        populate();
    }
    private void populate(){
        int count = worldMap.getLayers().getCount();
        for(int i = 0; i < count; i++)
            determenType(worldMap.getLayers().get(i));
    }

    private void determenType(MapLayer curr){
        if(curr instanceof TiledMapImageLayer){ worldLayers.add(new Layer((TiledMapImageLayer)curr)); return; }
        if(curr instanceof TiledMapTileLayer){ worldLayers.add(new Layer((TiledMapTileLayer)curr)); return; }
        if(curr instanceof MapLayer){ worldLayers.add(new Layer(curr)); return; }
    }

    public ArrayList<Renderable> getRenderables(){
        ArrayList<Renderable> renederables = new ArrayList<>();
        for(int i = 0; i < worldLayers.size(); i++){
            if(worldLayers.get(i).getType() == LAYER_TYPES.OBJECTS){
                ArrayList<Renderable> objLayer = worldLayers.get(i).getObjectLayer();
                for(int j = 0; j < objLayer.size(); j++)
                    renederables.add(objLayer.get(j));
            }
        }
        return renederables;
    }

    public ArrayList<Entity> getNonRenderables(){
        ArrayList<Entity> nonRenderables = new ArrayList<>();
        for(int i = 0; i < worldLayers.size(); i++){
            if(worldLayers.get(i).getType() == LAYER_TYPES.OBJECTS){
                ArrayList<Entity> objLayer = worldLayers.get(i).getNonRenderables();
                for(int j = 0; j < objLayer.size(); j++)
                    nonRenderables.add(objLayer.get(j));
            }
        }
        return nonRenderables;
    }

    public ArrayList<Layer> getLayers(){
        return worldLayers;
    }
}

class Layer{
    private TiledMapTileLayer tileLayer;
    private TiledMapImageLayer imageLayer;

    private ArrayList<Renderable> objectLayer;
    private ArrayList<Entity> nonRenderables;

    private LAYER_TYPES type;

    public Layer(TiledMapTileLayer tileLayer){
        this.type = LAYER_TYPES.TILES;
        this.tileLayer = tileLayer;
    }

    public Layer(TiledMapImageLayer imageLayer){
        this.type = LAYER_TYPES.STATIC_IMAGE;
        this.imageLayer = imageLayer;
    }

    public Layer(MapLayer objLayer){
        this.type = LAYER_TYPES.OBJECTS;
        objectLayer = new ArrayList<>();
        nonRenderables = new ArrayList<>();
        for(int i = 0; i < objLayer.getObjects().getCount(); i++){
            MapObject currObj = objLayer.getObjects().get(i);
            String type = (String)objLayer.getObjects().get(i).getProperties().get("type");
            if(type == null){continue;};
            switch(type){
                case "Renderable": objectLayer.add(new Renderable(currObj)); break;
                case "Animator": objectLayer.add(new Animator(currObj)); break;
                case "Actor": objectLayer.add(new Actor(currObj)); break;
                case "Trigger": nonRenderables.add(new Trigger(currObj));
            }
        }
        
    }

    public TiledMapTileLayer getTileLayer(){ return tileLayer; }
    public TiledMapImageLayer getImageLayer(){ return imageLayer; }

    public ArrayList<Renderable> getObjectLayer(){ return objectLayer; }
    public ArrayList<Entity> getNonRenderables() {return nonRenderables; }

    public LAYER_TYPES getType(){
        return type;
    }
}