package jgrasp_viewers;


import java.util.Collection;
import java.util.EnumSet;
import java.util.HashSet;
import jgrasp.PluginOptOut;
import jgrasp.viewer.ViewerCreateData;
import jgrasp.viewer.ViewerException;
import jgrasp.viewer.ViewerInfo;
import jgrasp.viewer.ViewerPriorityData;
import jgrasp.viewer.ViewerValueData;
import jgrasp.viewer.gui.BarGraphView;
import jgrasp.viewer.jgrdi.DebugContext;
import jgrasp.viewer.jgrdi.Method;
import jgrasp.viewer.jgrdi.Type;
import jgrasp.viewer.jgrdi.Value;


/** A viewer that displays a bar graph for arrays and lists of integral or floating point type. **/
public class java__lang__Object_BarGraphView extends BarGraphView {

   /** Supported element types. **/
   private static final Collection<String> javaSupportedTypes = new HashSet<>();

   /** Supported float types. **/
   private static final Collection<String> javaFloatTypes = new HashSet<>();
   
   static {
      javaSupportedTypes.add("long");
      javaSupportedTypes.add("int");
      javaSupportedTypes.add("short");
      javaSupportedTypes.add("char");
      javaSupportedTypes.add("byte");
      javaSupportedTypes.add("java.lang.Long");
      javaSupportedTypes.add("java.lang.Integer");
      javaSupportedTypes.add("java.lang.Short");
      javaSupportedTypes.add("java.lang.Char");
      javaSupportedTypes.add("java.lang.Byte");
      javaSupportedTypes.add("java.math.BigDecimal");
      javaSupportedTypes.add("java.math.BigInteger");
      javaSupportedTypes.add("float");
      javaSupportedTypes.add("double");
      javaSupportedTypes.add("java.lang.Float");
      javaSupportedTypes.add("java.lang.Double");
   }


   /** Creates a new bar graph viewer.
    *
    * @param vcd viewer creation data. **/
   public java__lang__Object_BarGraphView(final ViewerCreateData vcd) {
      this(vcd, EnumSet.noneOf(Flags.class));
   }
   
   
   /** Creates a new bar graph viewer.
    *
    * @param vcd viewer creation data.
    *
    * @param flags creation flags. **/
   public java__lang__Object_BarGraphView(final ViewerCreateData vcd, final EnumSet<Flags> flags) {
      super(vcd, javaSupportedTypes, flags);
      Value v = vcd.getValue();
      DebugContext dc = vcd.getDebugContext();
      Type t = v.getType(dc);
      try {
         if (!t.isArray(dc)) {
            if (v.isInstanceOf(dc, "java.util.List")) {
               return;
            }
         }
         else {
            Type elementType = t.getArrayElementType(dc);
            String tName = elementType.getName(dc);
            if (javaSupportedTypes.contains(tName) || "java.lang.Object".equals(tName)) {
               return;
            }
         }
      }
      catch (ViewerException ignored) {
      }
      throw new PluginOptOut();
   }


   /** {@inheritDoc} **/
   @Override
   public void getInfo(final ViewerInfo vi) {
      vi.setShortDescription("Viewer for integral and floating point arrays and lists");
      vi.setLongDescription("This viewer displays integral and floating point arrays and java.util.List "
            + "implementations as a bar graph.");
   }


   /** {@inheritDoc} **/
   @Override
   public int getPriority(final ViewerPriorityData vpd) {
      Value v = vpd.getValue();
      DebugContext context = vpd.getDebugContext();
      try {
         if (v == null) {
            return Integer.MIN_VALUE;
         }
         Type type = v.getType(context);
         if (!type.isArray(context)) {
            if (v.isInstanceOf(context, "java.util.List")) {
               return -10;
            }
            return Integer.MIN_VALUE;
         }
         Type elementType = type.getArrayElementType(context);
         String elementTypeName = elementType.getName(context);
         if (!javaSupportedTypes.contains(elementTypeName) && !"java.lang.Object".equals(elementTypeName)) {
            return Integer.MIN_VALUE;
         }
      }
      catch (ViewerException e) {
         return Integer.MIN_VALUE;
      }
      return -10;
   }


   /** {@inheritDoc} **/
   @Override
   public boolean modifyArrayData(final ArrayData ad, final DebugContext context) throws ViewerException {
      if (!ad.type.isArray(context)) {
         if (ad.value.isInstanceOf(context, "java.util.List")) {
            Method toArrayMethod = ad.type.getMethod(context, "toArray", "java.lang.Object[]", null);
            ad.value = ad.value.invokeMethod(context, toArrayMethod, null);
            ad.type = ad.value.getType(context);
         }
         else {
            return false;
         }
      }
   
      Type elementType = ad.type.getArrayElementType(context);
      if (!elementType.isObject(context)) {
         ad.fixedElementType = elementType;
      }
      return true;
   }
   
   
   /** {@inheritDoc} **/
   @Override
   public String getExpr(final ViewerValueData valueData, final int index, final DebugContext context) {
      if (index < 0) {
         return null;
      }
      String expr = getVIData().getExpression();
      if (expr == null) {
         return null;
      }
      
      String end;
      boolean needCast;
      Value val = valueData.value();
      Type dt = valueData.declaredType();
      if (val.isArray(context)) {
         end = "[" + index + "]";
         needCast = !dt.isArray(context);
      }
      else if (val.isInstanceOf(context, "java.util.List")) {
         end = ".get(" + index + ")";
         needCast = !dt.isInstanceOf(context, "java.util.List");
      }
      else {
         return null;
      }
      if (!needCast) {
         if (getVIData().getNeedParen()) {
            return "(" + expr + ")" + end;
         }
         return expr + end;
      }
      Value value = valueData.value();
      String cast = value.getType(context).getName(context);
      return "((" + (cast.indexOf('.') >= 0 ? "`cast`" : "") + cast + ") " + expr + ")" + end;
   }
}
