package fi.vincit.jmobster.processor.defaults.validator;

/*
 * Copyright 2012 Juha Siponen
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import fi.vincit.jmobster.annotation.InitMethod;
import fi.vincit.jmobster.processor.model.Validator;
import fi.vincit.jmobster.util.collection.AnnotationBag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * Base class for implementing validators. By default sets
 * the validator type with {@link Object#getClass()}. This can
 * be overridden with {@link BaseValidator#setType(Class)}
 */
public abstract class BaseValidator implements Validator {
    private static final Logger LOG = LoggerFactory.getLogger(BaseValidator.class);
    private Class type;

    protected BaseValidator() {
        this.type = this.getClass();
    }

    public void init(AnnotationBag annotations) {
        List<Method> methods = findInitMethods();

        // TODO: BeforeInit
        // TODO: Init via reflection
        for( Method m : methods ) {
            Class[] paramTypes = m.getParameterTypes();
            Annotation[] params = new Annotation[paramTypes.length];

            int numberFound = getParams(annotations, paramTypes, params);
            if( numberFound == paramTypes.length ) {
                try {
                    m.invoke(this, params);
                } catch (Exception e) {
                    LOG.error(e.getMessage(), e);
                }
            }
        }
        // TODO: AfterInit
    }

    private int getParams(AnnotationBag annotations, Class[] paramTypes, Annotation[] params) {
        int numberFound = 0;
        for( int i = 0; i < paramTypes.length; ++i ) {
            Class<?> paramType = paramTypes[i];
            if( Annotation.class.isAssignableFrom(paramType) ) {
                // Now we know that the paramType is an Annotation
                Class<? extends Annotation> annotationType = paramType.asSubclass(Annotation.class);
                Annotation annotation = annotations.getAnnotation(annotationType);
                if( annotation != null ) {
                    params[i] = annotation;
                    ++numberFound;
                }
            }
        }
        return numberFound;
    }

    private List<Method> findInitMethods() {
        List<Method> initMethods = new ArrayList<Method>();
        for( Method m : type.getMethods() ) {
            if( m.isAnnotationPresent(InitMethod.class) ) {
                initMethods.add(m);
            }
        }
        return initMethods;
    }

    @Override
    public Class getType() {
        return type;
    }

    protected void setType(Class type) {
        this.type = type;
    }
}

@@ -1175,16 +1175,367 @@
 Class)%7D%0A
+ *%0A * To initialize the subclass with correct validation data, use%0A * %7B@link InitMethod%7D annotation and create an init method. The name%0A * of the method doesn't matter. The only thing that matters is that%0A * the method parameters exist. The init method is only called when%0A * every required method parameter is found from the given annotation%0A * bag.%0A
  */%0Apubl
@@ -1836,35 +1836,91 @@
     
-List%3CMethod%3E methods = find
+// TODO: BeforeInit%0A        callInitMethods(annotations, findMethodsWithAnnotation(
 Init
@@ -1929,14 +1929,18 @@
 thod
-s(
+.class)
 );%0A
-%0A
     
@@ -1944,38 +1944,37 @@
        // TODO: 
-Before
+After
 Init%0A        // 
@@ -1970,40 +1970,271 @@
     
+%7D%0A%0A
     
-// TODO: Init via reflection
+private void callMethods(List%3CMethod%3E methods, Object... params) throws Exception %7B%0A        for( Method m : methods ) %7B%0A            m.invoke(this, params);%0A        %7D%0A    %7D%0A%0A    private void callInitMethods(AnnotationBag annotations, List%3CMethod%3E methods) %7B
 %0A   
@@ -2422,18 +2422,22 @@
 Found = 
-ge
+collec
 tParams(
@@ -2725,35 +2725,8 @@
   %7D%0A
-        // TODO: AfterInit%0A
     
@@ -2744,18 +2744,22 @@
 ate int 
-ge
+collec
 tParams(
@@ -3092,16 +3092,80 @@
 otation%0A
+                // We can cast it to correct type of class type%0A
         
@@ -3252,24 +3252,185 @@
 tion.class);
+%0A%0A                // Only add as found, if the annotation is actually found.%0A                // Ensures that init methods are always called with non null values.
 %0A           
@@ -3729,28 +3729,82 @@
 od%3E find
-Init
 Methods
-(
+WithAnnotation(Class%3C? extends Annotation%3E methodAnnotation
 ) %7B%0A    
@@ -3943,32 +3943,32 @@
 Present(
-InitMethod.class
+methodAnnotation
 ) ) %7B%0A  

Add documentation for init methods. Clean code.