package io.tetrapod.core.rpc;

import io.tetrapod.core.serialize.DataSource;
import io.tetrapod.protocol.core.StructDescription;

import java.io.IOException;
import java.lang.reflect.*;
import java.util.List;

abstract public class Structure {

   public static enum Security {
      PUBLIC, // open to services and unauthorized users
      PROTECTED, // open to services and authorized users
      INTERNAL, // open to services
      PRIVATE, // open to exact same service only
      ADMIN // open to admin user only
   }

   abstract public void write(DataSource data) throws IOException;

   abstract public void read(DataSource data) throws IOException;

   abstract public int getStructId();

   abstract public int getContractId();

   public Structure make() {
      try {
         return getClass().newInstance();
      } catch (InstantiationException | IllegalAccessException e) {
         return null;
      }
   }

   public StructDescription makeDescription() {
      return null;
   }

   @Override
   public String toString() {
      return getClass().getSimpleName();
   }

   public String dump() {
      return dump(true, true);
   }

   public String dump(boolean expandSubTypes, boolean includeClassname) {
      StringBuilder sb = new StringBuilder();
      Field f[] = getClass().getDeclaredFields();

      for (int i = 0; i < f.length; i++) {
         try {
            int mod = f[i].getModifiers();
            if (Modifier.isPublic(mod) && !Modifier.isStatic(mod)) {
               String name = f[i].getName();
               Object val = isSensitive(name) ? "~" : dumpValue(f[i].get(this), expandSubTypes);
               sb.append(f[i].getName() + ":" + val + ", ");
            }
         } catch (Exception e) {
            e.printStackTrace();
         }
      }
      String s = sb.length() > 0 ? ("{ " + sb.substring(0, sb.length() - 2) + " } ") : "{}";
      return (includeClassname ? this.getClass().getSimpleName() + " " : "") + s;
   }

   public Security getSecurity() {
      return Security.INTERNAL;
   }

   public String[] tagWebNames() {
      return new String[] {};
   }

   @SuppressWarnings("rawtypes")
   protected Object dumpValue(Object val, boolean expandSubTypes) {
      if (val != null && val instanceof List) {
         val = "[len=" + ((List) val).size() + "]";
      }
      if (val != null && val.getClass().isArray()) {
         val = "[len=" + Array.getLength(val) + "]";
      }
      if (expandSubTypes && val != null && val instanceof Structure) {
         val = ((Structure) val).dump(false, false);
      }
      if (expandSubTypes && val instanceof String) {         
         if (((String) val).length() > 2048) {
            return ((String) val).substring(0, 2048) + "...";
         }
      }
      return val;
   }
   
   public Object toRawForm(DataSource ds) {
      try {
         write(ds);
         return ds.getUnderlyingObject();
      } catch (IOException e) {
         return null;
      }
   }
   
   protected boolean isSensitive(String fieldName) {
      return false;
   }

}

@@ -1185,32 +1185,111 @@
 true);%0D%0A   %7D%0D%0A%0D%0A
+   public String unsafeDump() %7B%0D%0A      return unsafeDump(true, true);%0D%0A   %7D%0D%0A%0D%0A
    public String
@@ -2106,32 +2106,815 @@
 %22%22) + s;%0D%0A   %7D%0D%0A
+   %0D%0A   public String unsafeDump(boolean expandSubTypes, boolean includeClassname) %7B%0D%0A      StringBuilder sb = new StringBuilder();%0D%0A      Field f%5B%5D = getClass().getDeclaredFields();%0D%0A%0D%0A      for (int i = 0; i %3C f.length; i++) %7B%0D%0A         try %7B%0D%0A            int mod = f%5Bi%5D.getModifiers();%0D%0A            if (Modifier.isPublic(mod) && !Modifier.isStatic(mod)) %7B%0D%0A               Object val = dumpValue(f%5Bi%5D.get(this), expandSubTypes);%0D%0A               sb.append(f%5Bi%5D.getName() + %22:%22 + val + %22, %22);%0D%0A            %7D%0D%0A         %7D catch (Exception e) %7B%0D%0A            e.printStackTrace();%0D%0A         %7D%0D%0A      %7D%0D%0A      String s = sb.length() %3E 0 ? (%22%7B %22 + sb.substring(0, sb.length() - 2) + %22 %7D %22) : %22%7B%7D%22;%0D%0A      return (includeClassname ? this.getClass().getSimpleName() + %22 %22 : %22%22) + s;%0D%0A   %7D%0D%0A%0D%0A
 %0D%0A   public Secu

add in an unsafeDump that can be called for encrypted logging