package basics.utl;

import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import basics.testing.Test;

//sample usage
//@PK("id,num" )
public class AnnotationUtl
{
// @LEN(10)
// public String id = "d";
// @FK()
// public String fKey;
// //sample usage
// public AnnotationHelper() throws Exception
// {
// String r =
// (String)AnnotationHelper.getClassAnnotation(AnnotationHelper.class,
// PK.class, "value");
// System.out.println(r);
// Integer i = (Integer)
// AnnotationHelper.getFieldAnnotation(AnnotationHelper.class,
// "id", LEN.class, "value");
// System.out.println(i);
// boolean b = AnnotationHelper.hasFieldAnnotation(AnnotationHelper.class,
// "fKey",
// FK.class);
// System.out.println(b);
// }
// public static void main(String[] args) throws Exception
// {
// new AnnotationHelper();
// }
@SuppressWarnings("unchecked")
public static Object getClassAnnotation(Class classInQuestion, Class annotationInterface,
   String annotationKeyName) throws Exception
{
   Annotation annotation = classInQuestion.getAnnotation(annotationInterface);
   if(annotation == null) return null;
   Method method = annotation.getClass().getDeclaredMethod(annotationKeyName, (Class [])null);
   return method.invoke(annotation, new Object [] { });
}

@SuppressWarnings("unchecked")
public static long getMethodAnnotationValue(long dv, Method method, Class annotationInterface,
   String annotationKeyName) throws Exception
{
   Annotation annotation = method.getAnnotation(annotationInterface);
   if(annotation != null)
   {
      Method amethod = annotation.getClass().getDeclaredMethod(annotationKeyName, (Class [])null);
      dv = (Long)amethod.invoke(annotation, new Object [] { });
   }
   return dv;
}

@SuppressWarnings("unchecked")
public static Object getFieldAnnotation(Class classInQuestion, String annotatedField,
   Class annotationInterface, String annotationKeyName) throws Exception
{
   Field field = classInQuestion.getField(annotatedField);
   if(field == null) return null;
   Annotation annotation = field.getAnnotation(annotationInterface);
   if(annotation == null) return null;
   Method method = annotation.getClass().getDeclaredMethod(annotationKeyName, (Class [])null);
   if(method == null) return null;
   return method.invoke(annotation, new Object [] { });
}

@SuppressWarnings("unchecked")
public static boolean hasFieldAnnotation(Class classInQuestion, String annotatedField,
   Class annotationInterface) throws Exception
{
   Field field = classInQuestion.getField(annotatedField);
   return field.isAnnotationPresent(annotationInterface);
}

@SuppressWarnings("unchecked")
public static Method getAnnotatedMethod(Class classInQuestion, Class annotationInterface,
   String annotationKeyName, Object annotationValue)
{
   for(Method m : classInQuestion.getMethods())
   {
      Annotation annotation = m.getAnnotation(annotationInterface);
      if(annotation != null)
      {
         try
         {
            Method method = annotation.getClass().getDeclaredMethod(annotationKeyName,
               (Class [])null);
            if(method != null)
            {
               Object value = method.invoke(annotation, new Object [] { });
               if(value.equals(annotationValue)) return m;
            }
         }
         catch(Throwable ex)
         {
            SysUtl.trace("Test %s failed: %s %n", m, ex.getCause());
         }
      }
   }
   return null;
}

// ------------------------------ testing ---------------------------//
public static void unittest()
{
   try
   {
      String r = (String)AnnotationUtl.getClassAnnotation(AnnoTest.class, TestPK.class, "value");
      Test.assertEqualsTrue(r, "ok", "TestPK must contain ok");
      Integer i = (Integer)AnnotationUtl.getFieldAnnotation(AnnoTest.class, "id", TestLEN.class,
         "value");
      Test.assertTrue(i == 5, "TestLEN must contain 5");
      boolean b = AnnotationUtl.hasFieldAnnotation(AnnoTest.class, "ndx", TestFK.class);
      Test.assertTrue(b, "TestFK should be set");
   }
   catch(Exception e)
   {
      Test.handleAsFailure(e);
   }
}

@Retention(RetentionPolicy.RUNTIME)
@interface TestPK
{
   String value();
} // holds a string

@Retention(RetentionPolicy.RUNTIME)
@interface TestFK
{
} // marker 

@Retention(RetentionPolicy.RUNTIME)
@interface TestLEN
{
   int value();
} // holds an integer

@TestPK("ok")
private static class AnnoTest
{
@TestLEN(5)
public String id;
@TestFK
public int    ndx;
}
}
