package jgrasp_viewers;


import java.util.List;
import jgrasp.viewer.ViewerCreateData;
import jgrasp.viewer.ViewerException;
import jgrasp.viewer.ViewerValueData;
import jgrasp.viewer.ViewerUpdateData;
import jgrasp.viewer.jgrdi.Constructor;
import jgrasp.viewer.jgrdi.DebugContext;
import jgrasp.viewer.jgrdi.Field;
import jgrasp.viewer.jgrdi.JgrdiIsFinalException;
import jgrasp.viewer.jgrdi.Method;
import jgrasp.viewer.jgrdi.Type;
import jgrasp.viewer.jgrdi.Value;


/** Image viewer for java Images. **/
public abstract class ImageImageView extends ImageView {


   /** Creates a new color viewer.
    *
    * @param vcd viewer creation data. **/
   public ImageImageView(final ViewerCreateData vcd) {
      super(vcd);
   }


    /** {@inheritDoc} **/
   @Override
   public Data getData(final ViewerValueData valueData, final ViewerUpdateData data, final DebugContext context)
         throws ViewerException {
      String[] clientTextOut = new String[1];
      Value value = getImage(valueData.value(), context, clientTextOut);
      if (value.isNull(context)) {
         return new Data("Image is null.", clientTextOut[0], null, 0, 0, false);
      }
      Method getWidthMethod = value.getMethod(context, "getWidth", "int", null);
      int width = value.invokeMethod(context, getWidthMethod, null).toInt(context);
      Method getHeightMethod = value.getMethod(context, "getHeight", "int", null);
      int height = value.invokeMethod(context, getHeightMethod, null).toInt(context);
   
      String dsText = width + " x " + height;
   
      if (width <= 0 || height <= 0) {
         return new Data(dsText, clientTextOut[0], null, 0, 0, false);
      }
      Value scaledWidthV = context.createPrimitiveValue("int", String.valueOf(width));
      Value scaledHeightV = context.createPrimitiveValue("int", String.valueOf(height));
      Value zeroV = context.createPrimitiveValue("int", "0");
      Value nullV = context.createNullValue();
   
      Value bufferedImage = null;
      boolean complete = true;
      boolean createdImage = false;
      if (value.isInstanceOf(context, "java.awt.image.BufferedImage")) {
         bufferedImage = value;
      }
      Type bufferedImageType = context.getType("java.awt.image.BufferedImage");
      bufferedImageType.validate(context);
      if (bufferedImage == null) {
         createdImage = true;
         Constructor biCons = bufferedImageType.getConstructor(context, new String[] { "int", "int", "int" });
         bufferedImage = bufferedImageType.createInstance(context, biCons, new Value[] { scaledWidthV, scaledHeightV,
               context.createPrimitiveValue("int", "2" /* TYPE_INT_ARGB */) });
         Method getGraphicsMethod = bufferedImageType.getMethod(context, "getGraphics", "java.awt.Graphics", null);
         Value graphics = bufferedImage.invokeMethod(context, getGraphicsMethod, null);
         Method drawImage = graphics.getMethod(context, "drawImage", "boolean", new String[] { "java.awt.Image",
               "int", "int", "int", "int", "java.awt.image.ImageObserver" });
         Value result = graphics.invokeMethod(context, drawImage, new Value[] { value, zeroV, zeroV, scaledWidthV,
               scaledHeightV, nullV });
         complete = result.toBoolean(context);
         Method dispose = graphics.getMethod(context, "dispose", "void", null);
         graphics.invokeMethod(context, dispose, null);
         List<Field> gFields = graphics.getType(context).getFields(Type.INSTANCE);
         for (Field field : gFields) {
            Type t = field.getType(context);
            if (t != null && t.isObject(context)) {
               graphics.setFieldValue(context, field, nullV);
            }
         }
      }
      
      Method getRGBMethod = bufferedImageType.getMethod(context, "getRGB", "int[]",
            new String[] { "int", "int", "int", "int", "int[]", "int", "int"});
      Value rgbData = bufferedImage.invokeMethod(context, getRGBMethod, new Value[] { zeroV, zeroV, scaledWidthV,
            scaledHeightV, nullV, zeroV, scaledWidthV });
      List<Value> pixels = rgbData.getArrayElements(context);
      
      if (pixels == null || pixels.size() < width * height) {
         return new Data(dsText, clientTextOut[0], null, 0, 0, false);
      }
      int[] pixelData = new int[pixels.size()];
      for (int i = 0; i < pixels.size(); i++) {
         pixelData[i] = pixels.get(i).toInt(context);
      }
   
      if (createdImage) {
         Method flush = bufferedImage.getMethod(context, "flush", "void", null);
         bufferedImage.invokeMethod(context, flush, null);
         try {
            bufferedImage.setFieldValue(context, "raster", nullV);
         }
         catch (JgrdiIsFinalException ignored) {
         }
         try {
            bufferedImage.setFieldValue(context, "surfaceManager", nullV);
         }
         catch (JgrdiIsFinalException ignored) {
         }
      }
      return new Data(dsText, clientTextOut[0], pixelData, width, height, complete);
   }


   /** Gets the image to be displayed.
    *
    * @param value the value being viewed.
    *
    * @param context the current debugger context.
    *
    * @param clientTextOut the first element of this array is used to return display text, if any.
    *
    * @throws ViewerException if an error occurs while retrieving the image.
    *
    * @return the image value. **/
   public abstract Value getImage(Value value, DebugContext context, String[] clientTextOut) throws ViewerException;
}