/*******************************************************************************
 * Copyright (c) 2010 Robert "Unlogic" Olofsson (unlogic@unlogic.se).
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v3
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl-3.0-standalone.html
 ******************************************************************************/
package se.unlogic.standardutils.string;

import se.unlogic.standardutils.reflection.ReflectionUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;


public class BeanTagSourceFactory<T> implements TagSourceFactory<T>{

   private Class<T> beanClass;
   private HashMap<String,Method> tagMethodMap = new HashMap<String, Method>();
   private HashMap<String,Field> tagFieldMap = new HashMap<String, Field>();
   private HashSet<String> tagsSet = new HashSet<String>();

   public BeanTagSourceFactory(Class<T> beanClass) {

      this.beanClass = beanClass;
   }

   public void addMethodMapping(String tag, String methodName) throws NoSuchMethodException{

      Method method = ReflectionUtils.getMethod(beanClass, methodName, Object.class);

      if(method == null){

         throw new NoSuchMethodException("Method " + methodName + " with no input parameters not found in " + beanClass);
      }

      addMethodMapping(tag, method);
   }

   protected void addMethodMapping(String tag, Method method){

      if(!method.isAccessible()){

         ReflectionUtils.fixMethodAccess(method);
      }

      tagMethodMap.put(tag, method);
      this.tagsSet.add(tag);
   }

   public void addFieldMapping(String tag, String fieldName) throws NoSuchFieldException{

      Field field = ReflectionUtils.getField(beanClass, fieldName);

      if(field == null){

         throw new NoSuchFieldException("Field " + fieldName + " not found in " + beanClass);
      }

      addFieldMapping(tag, field);
   }

   protected void addFieldMapping(String tag, Field field){

      if(!field.isAccessible()){

         ReflectionUtils.fixFieldAccess(field);
      }

      tagFieldMap.put(tag, field);
      this.tagsSet.add(tag);
   }

   public void addAllFields(String fieldPrefix, String... excludedFields){

      List<String> exclusionList = null;

      if(excludedFields != null){

         exclusionList = Arrays.asList(excludedFields);
      }


      List<Field> fields = ReflectionUtils.getFields(beanClass);

      for(Field field : fields){

         if(exclusionList == null || !exclusionList.contains(field.getName())){

            if(!field.isAccessible()){

               ReflectionUtils.fixFieldAccess(field);
            }

            this.tagFieldMap.put(fieldPrefix + field.getName(), field);
            this.tagsSet.add(fieldPrefix + field.getName());
         }
      }
   }

   public <X extends T> BeanTagSource<T> getTagSource(X bean){

      return new BeanTagSource<T>(bean, tagMethodMap, tagFieldMap, tagsSet);
   }


   public HashSet<String> getTagsSet() {
      return new HashSet<String>(tagsSet);
   }

   public String getAvailableTags(){

      return StringUtils.toCommaSeparatedString(this.tagsSet);
   }
}
