package de.gurkenlabs.utiliti.renderers;

import de.gurkenlabs.litiengine.Game;
import de.gurkenlabs.litiengine.environment.tilemap.IMap;
import de.gurkenlabs.litiengine.graphics.ICamera;
import de.gurkenlabs.litiengine.graphics.ImageRenderer;
import de.gurkenlabs.litiengine.graphics.ShapeRenderer;
import de.gurkenlabs.litiengine.util.Imaging;
import de.gurkenlabs.utiliti.components.Editor;
import java.awt.BasicStroke;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class GridRenderer implements IEditorRenderer {
  private final Map<String, GridImages> gridCache = new ConcurrentHashMap<>();

  @Override
  public String getName() {
    return "GRID";
  }

  @Override
  public void render(Graphics2D g) {
    // render the grid
    final ICamera camera = Game.world().camera();
    if (Editor.preferences().showGrid()
        && camera.getRenderScale() >= 1
        && Game.world().environment() != null) {
      final IMap map = Game.world().environment().getMap();

      if (map == null || map.getName() == null) {
        return;
      }

      if (!gridCache.containsKey(map.getName())) {
        gridCache.put(map.getName(), new GridImages(map));
      }

      Point2D viewPortLocation = Game.world().camera().getViewportLocation(0, 0);

      final float scale =
          camera.getRenderScale() > GridImages.RENDERSCALE_LARGE
              ? GridImages.LARGE_GRID_SCALE
              : camera.getRenderScale() > GridImages.RENDERSCALE_MID
                  ? GridImages.MID_GRID_SCALE
                  : GridImages.SMALL_GRID_SCALE;
      final GridImages images = gridCache.get(map.getName());
      final BufferedImage image =
          camera.getRenderScale() > GridImages.RENDERSCALE_LARGE
              ? images.getLargeImage()
              : camera.getRenderScale() > GridImages.RENDERSCALE_MID
                  ? images.getMidImage()
                  : images.getSmallImage();
      ImageRenderer.renderScaled(
          g,
          image,
          viewPortLocation.getX() * camera.getRenderScale(),
          viewPortLocation.getY() * Game.world().camera().getRenderScale(),
          camera.getRenderScale() / scale);
    }
  }

  public void clearCache() {
    gridCache.clear();
  }

  private static class GridImages {
    private static final float RENDERSCALE_LARGE = 6;
    private static final float RENDERSCALE_MID = 1.5f;

    private static final float SMALL_GRID_SCALE = 1f;
    private static final float MID_GRID_SCALE = 2;
    private static final float LARGE_GRID_SCALE = 6;
    private final BufferedImage smallImage;
    private final BufferedImage midImage;
    private final BufferedImage largeImage;

    private GridImages(IMap map) {
      this.smallImage = createImage(map, SMALL_GRID_SCALE);
      this.midImage = createImage(map, MID_GRID_SCALE);
      this.largeImage = createImage(map, LARGE_GRID_SCALE);
    }

    public BufferedImage getSmallImage() {
      return smallImage;
    }

    public BufferedImage getMidImage() {
      return midImage;
    }

    public BufferedImage getLargeImage() {
      return largeImage;
    }

    private static BufferedImage createImage(IMap map, float scale) {
      BufferedImage image =
          Imaging.getCompatibleImage(
              (int) (map.getSizeInPixels().width * scale) + 1,
              (int) (map.getSizeInPixels().height * scale) + 1);
      Graphics2D graphics = (Graphics2D) image.getGraphics();

      final float lineSize = Editor.preferences().getGridLineWidth() / scale;
      final Stroke stroke = new BasicStroke(lineSize);
      graphics.setColor(Editor.preferences().getGridColor());
      for (int x = 0; x < map.getWidth(); x++) {
        for (int y = 0; y < map.getHeight(); y++) {
          Shape tile = map.getOrientation().getShape(x, y, map);
          ShapeRenderer.renderOutlineTransformed(
              graphics, tile, AffineTransform.getScaleInstance(scale, scale), stroke);
        }
      }

      graphics.dispose();

      return image;
    }
  }
}

@@ -721,16 +721,17 @@
 derer %7B%0A
+%0A
   privat
@@ -2107,23 +2107,129 @@
 Image();
+%0A%0A      for (int x = 0; x %3C map.getWidth(); x++) %7B%0A        for (int y = 0; y %3C map.getHeight(); y++) %7B
 %0A      
+    
 ImageRen
@@ -2262,11 +2262,19 @@
     
-g,%0A
+    g,%0A    
     
@@ -2288,32 +2288,37 @@
 mage,%0A          
+    (
 viewPortLocation
@@ -2324,16 +2324,44 @@
 n.getX()
+ + (x * map.getTileWidth()))
  * camer
@@ -2390,16 +2390,21 @@
         
+    (
 viewPort
@@ -2418,16 +2418,45 @@
 n.getY()
+ + (y * map.getTileHeight()))
  * Game.
@@ -2492,32 +2492,36 @@
 le(),%0A          
+    
 camera.getRender
@@ -2534,24 +2534,43 @@
 ) / scale);%0A
+        %7D%0A      %7D%0A%0A
     %7D%0A  %7D%0A%0A 
@@ -2661,16 +2661,17 @@
 mages %7B%0A
+%0A
     priv
@@ -3668,28 +3668,19 @@
 .get
-SizeInPixels().w
+TileW
 idth
+()
  * s
@@ -3722,29 +3722,20 @@
 .get
-SizeInPixels().h
+TileH
 eight
+()
  * s
@@ -4008,113 +4008,8 @@
 ));%0A
-      for (int x = 0; x %3C map.getWidth(); x++) %7B%0A        for (int y = 0; y %3C map.getHeight(); y++) %7B%0A    
     
@@ -4057,12 +4057,12 @@
 ape(
-x, y
+0, 0
 , ma
@@ -4071,20 +4071,16 @@
 ;%0A      
-    
 ShapeRen
@@ -4110,31 +4110,16 @@
 sformed(
-%0A              
 graphics
@@ -4182,34 +4182,16 @@
 stroke);
-%0A        %7D%0A      %7D
 %0A%0A      

Improve memory usage of the GridRenderer