package pathgame.rendering;

import me.hammerle.snuviengine.api.Renderer;
import pathgame.gameplay.Camera;
import pathgame.gameplay.Gamestate;
import pathgame.gameplay.Gamestates;
import pathgame.gameplay.Level;
import pathgame.gameplay.Player;
import pathgame.tilemap.TileMap;

/**
 * A container for holding everything about the renderer for the player
 *
 * @author julia
 */
public class LevelRenderer
{
    private final PlayerRenderer playerRenderer = new PlayerRenderer();
    private final TileMapRenderer mapRenderer = new TileMapRenderer();
    private final HUDRenderer hudRenderer = new HUDRenderer();
    private final Camera cam = new Camera();
    private final ScoreMenuRenderer scoreRenderer = new ScoreMenuRenderer();

    /**
     * Calls the camera tick and the mapRenderer tick and resets the camera if
     * the player is moving
     *
     * @param level a level containing the current map and the current player
     * @param gamestate the gamestate
     */
    public void tick(Level level, Gamestate gamestate)
    {
        cam.tick(level, gamestate);
        mapRenderer.tick();
        if(level.getPlayer().isMoving())
        {
            cam.reset();
        }
    }

    /**
     * Recalculates the rendering position and settings of everything in the
     * level every rendertick based on the gamelogic in the gametick
     *
     * @param r the renderer
     * @param lag the current lag
     * @param level the current level containing the map and the player
     * @param gamestate the gamestate
     */
    public void renderTick(Renderer r, float lag, Level level, Gamestate gamestate)
    {
        if(level.isShowingAfterScore() || level.isShowingScoreMenu())
        {
            lag = 0.0f;
        }
        TileMap map = level.getMap();
        Player player = level.getPlayer();

        float zoomRestrictionX = r.getViewWidth() / (map.getWidth() * TileRenderer.TILE_SIZE);
        float zoomRestrictionY = (r.getViewHeight() - HUDRenderer.OFFSET_Y) / (map.getHeight() * TileRenderer.TILE_SIZE);

        cam.limitScale(Math.max(zoomRestrictionX, zoomRestrictionY));
        float interScale = cam.getInterpolatedScale(lag);

        mapRenderer.setScale(interScale);

        float offX = getMapOffsetX(map, player, r, lag, interScale);
        float offY = getMapOffsetY(map, player, r, lag, interScale) + HUDRenderer.OFFSET_Y;

        mapRenderer.renderTick(map, r, player, false, offX, offY);

        if(gamestate.is(Gamestates.GAMEPLAY))
        {
            playerRenderer.renderTick(map, mapRenderer, r, player, lag, offX, offY);
            if(!level.isShowingScoreMenu())
            {
                hudRenderer.renderTick(r, player, lag);
            }
            else if(level.isShowingScoreMenu())
            {
                scoreRenderer.renderTick(r, lag, level);
            }
        }

    }

    private float getMapOffsetX(TileMap map, Player player, Renderer r, float lag, float interScale)
    {
        float ix = (player.getLastX() + (player.getX() - player.getLastX()) * lag) * interScale * TileRenderer.TILE_SIZE;
        float offX = (-ix + r.getViewWidth() * 0.5f) - TileRenderer.TILE_SIZE * 0.5f * interScale;

        float minOffX = -mapRenderer.getWidth(map) + r.getViewWidth();
        if(offX < minOffX)
        {
            return cam.getCamOffsetX(minOffX, minOffX, lag, interScale);
        }
        else if(offX > 0.0f)
        {
            return cam.getCamOffsetX(0.0f, minOffX, lag, interScale);
        }
        return cam.getCamOffsetX(offX, minOffX, lag, interScale);
    }

    private float getMapOffsetY(TileMap map, Player player, Renderer r, float lag, float interScale)
    {
        float viewHeight = r.getViewHeight() - HUDRenderer.OFFSET_Y;
        float iy = (player.getLastY() + (player.getY() - player.getLastY()) * lag) * interScale * TileRenderer.TILE_SIZE;
        float offY = (-iy + viewHeight * 0.5f) - TileRenderer.TILE_SIZE * 0.5f * interScale;

        float minOffY = -mapRenderer.getHeight(map) + viewHeight;
        if(offY < minOffY)
        {
            return cam.getCamOffsetY(minOffY, minOffY, lag, interScale);
        }
        else if(offY > 0.0f)
        {
            return cam.getCamOffsetY(0.0f, minOffY, lag, interScale);
        }
        return cam.getCamOffsetY(offY, minOffY, lag, interScale);
    }
}
