package util.sqlparse.visitor.es.dense;

import org.apache.commons.jexl2.Expression;
import org.apache.commons.jexl2.JexlContext;
import org.apache.commons.jexl2.JexlEngine;
import org.apache.commons.jexl2.JexlInfo;
import org.apache.commons.jexl2.MapContext;
import org.apache.commons.jexl2.introspection.JexlMethod;
import org.apache.commons.jexl2.introspection.JexlPropertyGet;
import org.apache.commons.jexl2.introspection.JexlPropertySet;
import org.apache.commons.jexl2.introspection.Uberspect;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class ExlActor {
   public static Object replace(String expression, Object value) {
      expression = expression.replace("${value}", "_value");
      Object code = convertToCode(expression, value);
      return code;
   }

public static Object convertToCode(String jexlExp, Object value) {
   System.out.println(jexlExp);
   Map<String, Object> map = new HashMap<>(16);
   map.put("_value", value);
   map.put("functions", new ExlFunctionProvider());
   JexlEngine jexl = new JexlEngine(new UberspectImpl(), null, map, null);
   jexl.setDebug(false);
   Expression e = jexl.createExpression(jexlExp);
   JexlContext jc = new MapContext();

   for(String key : map.keySet()) {
      jc.set(key, map.get(key));
   }

   return e.evaluate(jc);
}

   public static void main(String... args) throws Exception {
      try {
         String[] expressions = new String[]{"'abcd' + 'aaaa'", "substr('张三分',1,1) + '***'", "substr('abcd',-9,2)", "substr('abcd',1,2)", "substr('abcde',1,3)", "substr('abcde',1,4)", "substr('abcde',1,5)", "substr('abcde',4,1)", "substr(substring('123',0,2),0,2)", "to_char(now(),'yyyy-MM-dd')", "now()", "floor(20 + (random()*71))", "(substr('192.168.2.111',1,length('192.168.2.111')-3)+'***')", "floor(random()*900+100)", "left('50512345678@qq.com',3)+ '********' + substr('50512345678@qq.com',position('50512345678@qq.com','@'))", "date(timestamp(date('1970-01-01')) + floor(random() * ( timestamp(now()) - timestamp(date('1970-01-01')) + 1 )))", "to_char(date(timestamp(date('1970-01-01')) + floor(random() * ( timestamp(now()) - timestamp(date('1970-01-01')) + 1 ))),'yyyy-MM-dd HH:mm:ss')", "to_char(date(timestamp(date('1970-01-01')) + floor(random() * ( timestamp(now()) - timestamp(date('1970-01-01')) + 1 ))),'yyyy-MM-dd')"};

         for(String expression : expressions) {
            Object code = replace(expression, null);
            System.out.println(code);
         }

         System.out.println("1：" + replace("to_char(date(timestamp(date(${value})) + floor(random() * ( timestamp(now()) - timestamp(date(${value})) + 1 ))),'yyyy-MM-dd')", "1922-01-01"));
      } catch (Exception e) {
         e.printStackTrace();
      }

   }

   static class JexlMethodImpl implements JexlMethod {
      private final Method m;

      JexlMethodImpl(Method m) {
         this.m = m;
      }

      public Object invoke(Object obj, Object[] params) throws Exception {
         return this.m.invoke(obj, params);
      }

      public Object tryInvoke(String name, Object obj, Object[] params) {
         try {
            return this.m.invoke(obj, params);
         } catch (IllegalAccessException e) {
            e.printStackTrace();
            return null;
         } catch (InvocationTargetException e) {
            e.printStackTrace();
            return null;
         }
      }

      public boolean tryFailed(Object rval) {
         return false;
      }

      public boolean isCacheable() {
         return false;
      }

      public Class<?> getReturnType() {
         return this.m.getReturnType();
      }
   }

   static class UberspectImpl implements Uberspect {
      ClassLoader classLoader;

      public void setClassLoader(ClassLoader loader) {
         this.classLoader = loader;
      }

      public Constructor<?> getConstructor(Object ctorHandle, Object[] args, JexlInfo info) {
         return null;
      }

      public JexlMethod getConstructorMethod(Object o, Object[] objects, JexlInfo jexlInfo) {
         return null;
      }

      public JexlMethod getMethod(Object obj, String method, Object[] args, JexlInfo info) {
         Class[] types = new Class[args.length];

         for(int i = 0; i < args.length; ++i) {
            types[i] = args[i].getClass();
         }

         try {
            Method methodInfo = obj.getClass().getMethod(method, types);
            return new JexlMethodImpl(methodInfo);
         } catch (NoSuchMethodException e) {
            e.printStackTrace();
            return null;
         }
      }

      public JexlPropertyGet getPropertyGet(Object obj, Object identifier, JexlInfo info) {
         return null;
      }

      public JexlPropertySet getPropertySet(Object obj, Object identifier, Object arg, JexlInfo info) {
         return null;
      }

      public Iterator<?> getIterator(Object obj, JexlInfo info) {
         return null;
      }
   }

   public static class ExlFunctionProvider {
      public String substr(String str, Integer begin) {
         return this.substr(str, begin, str.length());
      }

      public String substr(String str, Integer begin, Integer length) {
         if (str != null && str.length() != 0) {
            int len = str.length();
            begin = begin - 1;
            if (begin < 0) {
               begin = 0;
            }

            if (begin >= len) {
               begin = len - 1;
            }

            if (length < 0) {
               length = 0;
            }

            int endIndex = begin + length;
            if (endIndex > len) {
               endIndex = len;
            }

            return str.substring(begin, endIndex);
         } else {
            return str;
         }
      }

      public String substring(String str, Integer begin, Integer end) {
         if (str != null && str.length() != 0) {
            int len = str.length();
            begin = begin - 1;
            if (begin < 0) {
               begin = 0;
            }

            if (begin >= len) {
               begin = len - 1;
            }

            if (end > len) {
               end = len - begin + 1;
            }

            return str.substring(begin, end);
         } else {
            return str;
         }
      }

      public int length(String str) {
         return str == null ? 0 : str.length();
      }

      public String left(String str, Byte len) {
         return this.left(str, Integer.valueOf(len));
      }

      public String left(String str, Integer len) {
         if (str != null && str.length() != 0) {
            len = Math.min(len, str.length());
            return str.substring(0, len);
         } else {
            return str;
         }
      }

      public Date now() {
         return new Date();
      }

      public String to_char(Date date, String format) {
         if (format != null && format.length() != 0) {
            SimpleDateFormat sdf = new SimpleDateFormat(format);
            return sdf.format(date);
         } else {
            return date.toString();
         }
      }

      public long timestamp(Date date) {
         return date.getTime();
      }

      public Date date(Double d) {
          return new Date((long) d.doubleValue());
      }

      public Date date(String str) {
         return this.date(str, "yyyy-MM-dd");
      }

      public Date date(String str, String format) {
         if (str != null && str.length() != 0) {
            SimpleDateFormat sdf = new SimpleDateFormat(format);
            return sdf.parse(str, new ParsePosition(0));
         } else {
            return null;
         }
      }

      public double random() {
         return Math.random();
      }

      public double ceiling(Double v) {
         return Math.ceil(v);
      }

      public double floor(Double v) {
         return Math.floor(v);
      }

      public int position(String str, String pattern) {
         if (str != null && str.length() != 0) {
            if (pattern != null && pattern.length() != 0) {
               int index = str.indexOf(pattern);
               return index + 1;
            } else {
               return str.length();
            }
         } else {
            return 0;
         }
      }
   }
}
