/* See LICENSE for licensing and NOTICE for copyright. */
package org.ldaptive.beans.reflect;

import java.lang.reflect.Field;
import java.util.LinkedHashMap;
import java.util.Map;
import org.ldaptive.beans.AbstractClassDescriptor;
import org.ldaptive.beans.Attribute;
import org.ldaptive.beans.AttributeValueMutator;
import org.ldaptive.beans.DnValueMutator;
import org.ldaptive.beans.Entry;
import org.ldaptive.transcode.ValueTranscoder;

/**
 * Creates DN and attribute mutators for the {@link Field}s on a type.
 *
 * @author  Middleware Services
 */
public class FieldClassDescriptor extends AbstractClassDescriptor
{


  @Override
  public void initialize(final Class<?> type)
  {
    final Map<String, Field> fields = getDeclaredFields(type);
    final Entry entryAnnotation = type.getAnnotation(Entry.class);
    if (fields.containsKey(entryAnnotation.dn())) {
      setDnValueMutator(createDnValueMutator(fields.get(entryAnnotation.dn())));
    }
    for (Attribute attr : entryAnnotation.attributes()) {
      if (attr.values().length == 0) {
        final String property = attr.property().length() > 0 ? attr.property() : attr.name();
        if (fields.containsKey(property)) {
          addAttributeValueMutator(createAttributeValueMutator(fields.get(property), attr));
        }
      }
    }
  }


  /**
   * Returns a map of all the field names to the field.
   *
   * @param  type  to read declared fields from
   *
   * @return  map of field names to field
   */
  protected Map<String, Field> getDeclaredFields(final Class<?> type)
  {
    final Map<String, Field> fields = new LinkedHashMap<>();
    Class<?> c = type;
    while (c != null) {
      for (Field field : c.getDeclaredFields()) {
        if (!fields.containsKey(field.getName())) {
          fields.put(field.getName(), field);
        }
      }
      c = c.getSuperclass();
    }
    return fields;
  }


  /**
   * Returns a dn value mutator for the supplied field.
   *
   * @param  field  to create dn value mutator for
   *
   * @return  dn value mutator
   */
  protected DnValueMutator createDnValueMutator(final Field field)
  {
    return
      new DefaultDnValueMutator(
        new FieldAttributeValueMutator(new DefaultReflectionTranscoder(field.getGenericType()), field));
  }


  /**
   * Returns an attribute value mutator for the supplied field.
   *
   * @param  field  to create attribute value mutator for
   * @param  attribute  attribute containing metadata
   *
   * @return  attribute value mutator
   */
  protected AttributeValueMutator createAttributeValueMutator(final Field field, final Attribute attribute)
  {
    final String name = "".equals(attribute.name()) ? field.getName() : attribute.name();
    final ValueTranscoder<?> transcoder = TranscoderFactory.getInstance(attribute.transcoder());
    return
      new FieldAttributeValueMutator(
        name,
        attribute.binary(),
        new DefaultReflectionTranscoder(field.getGenericType(), transcoder),
        field);
  }
}
