package pathgame.rendering;

import me.hammerle.snuviengine.api.Renderer;
import me.hammerle.snuviengine.api.Texture;
import me.hammerle.snuviengine.api.TextureRenderer;
import pathgame.tilemap.TileMap;

/** A renderer for tile maps.
 *
 * @author kajetan
 */
public class TileMapRenderer
{
    // prevents rendering artifacts especially on different zoom levels
    private final static float ERROR = 1.0f / 512.0F;
    private final static float T_ERROR = 1.0f / 4096.0F;
    
    private final Texture tileTexture = new Texture("resources/tiles.png");
    private final TextureRenderer textureRenderer = new TextureRenderer(20 * 20 * 2); // default to 20x20 map
    private float scale = 1.0f;
    
    /** Creates a new tile map renderer.
     *
     */
    public TileMapRenderer()
    {
    }

    /** Sets the scale of the map.
     *
     * @param scale the scale of the map
     */
    public void setScale(float scale)
    {
        this.scale = scale;
    }

    /** Returns the scale of the map.
     *
     * @return the scale of the map
     */
    public float getScale()
    {
        return scale;
    }
    
    /** Returns the scaled render width of a map.
     *
     * @param map a map
     * @return the scaled render width of a map
     */
    public float getWidth(TileMap map)
    {
        return map.getWidth() * TileRenderer.TILE_SIZE * scale;
    }
    
    /** Returns the scaled render height of a map.
     *
     * @param map a map
     * @return the scaled render height of a map
     */
    public float getHeight(TileMap map)
    {
        return map.getHeight() * TileRenderer.TILE_SIZE * scale;
    }
    
    private void updateData(TileMap map)
    {
        textureRenderer.clear();
        for(int x = 0; x < map.getWidth(); x++)
        {
            for(int y = 0; y < map.getHeight(); y++)
            {
                TileTexture tt = TileRenderer.getTileTexture(map, map.getTile(x, y), x, y);
                if(tt == null)
                {
                    continue;
                }
                textureRenderer.addRectangle(
                        x * TileRenderer.TILE_SIZE - ERROR, y * TileRenderer.TILE_SIZE - ERROR,
                        (x + 1) * TileRenderer.TILE_SIZE + ERROR, (y + 1) * TileRenderer.TILE_SIZE + ERROR,
                        tt.getMinX() + T_ERROR, tt.getMinY() + T_ERROR, 
                        tt.getMaxX() - T_ERROR, tt.getMaxY() - T_ERROR);
            }
        }
        textureRenderer.build();
    }
    
    /** Ticks the renderer. Used for animated tiles.
     *
     */
    public void tick()
    {
        // tick tile animations here
    }
    
    /** Draws the given map at the given offset.
     *
     * @param map a map
     * @param r the renderer given by the engine
     * @param forceUpdate whether an update of the render data should be forced
     * @param offX the x coordinate of the offset
     * @param offY the y coordinate of the offset
     */
    public void renderTick(TileMap map, Renderer r, boolean forceUpdate, float offX, float offY)
    {
        r.setTextureEnabled(true);
        r.setColorEnabled(false);
        r.setMixColorEnabled(false);
        
        tileTexture.bind();
        
        if(forceUpdate || map.isDirty())
        {
            updateData(map);
            map.clean();
        }
        
        float viewScale = r.getViewScale();
        offX = (int) (offX * viewScale) / viewScale;
        offY = (int) (offY * viewScale) / viewScale;
        
        r.translateTo(offX, offY);
        r.scale(scale, scale);
        r.updateMatrix();
        textureRenderer.draw();
    }
}
