/*
 * Copyright 2002-2008 the original author or authors.
 *
 * 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.
 */
package org.springframework.config.java.internal.parsing.asm;

import static org.springframework.config.java.internal.parsing.asm.MutableAnnotationUtils.createMutableAnnotation;
import static org.springframework.util.ClassUtils.getDefaultClassLoader;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;

import org.springframework.asm.AnnotationVisitor;
import org.springframework.asm.Type;


/** TODO: JAVADOC */
class MutableAnnotationVisitor implements AnnotationVisitor {

    protected final MutableAnnotation mutableAnno;

    public MutableAnnotationVisitor(MutableAnnotation mutableAnno) {
        this.mutableAnno = mutableAnno;
    }

    public AnnotationVisitor visitArray(final String attribName) {
        return new MutableAnnotationArrayVisitor(mutableAnno, attribName);
    }

    public void visit(String attribName, Object attribValue) {
        //System.out.println(format("MutableAnnotationVisitor.visit(%s, %s)", attribName, attribValue));
        Class<?> attribReturnType = mutableAnno.getAttributeType(attribName);

        if (attribReturnType.equals(Class.class)) {
            // the attribute type is Class -> load it and set it.
            // TODO: Spring IDE issues here. Class may not be a user-defined type.
            try {
                Class<?> classVal = getDefaultClassLoader().loadClass(((Type) attribValue).getClassName());
                mutableAnno.setAttributeValue(attribName, classVal);
                return;
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        }

        // otherwise, assume the value can be set literally
        mutableAnno.setAttributeValue(attribName, attribValue);
    }

    @SuppressWarnings("unchecked")
    public void visitEnum(String attribName, String enumTypeDescriptor, String strEnumValue) {
        String enumClassName = AsmUtils.convertTypeDescriptorToClassName(enumTypeDescriptor);

        Class<Enum> enumClass;
        try {
            enumClass = (Class<Enum>) getDefaultClassLoader().loadClass(enumClassName);
        } catch (ClassNotFoundException ex) {
            throw new RuntimeException(ex);
        }

        Object enumValue = Enum.valueOf(enumClass, strEnumValue);
        mutableAnno.setAttributeValue(attribName, enumValue);
    }

    public AnnotationVisitor visitAnnotation(String attribName, String attribAnnoTypeDescriptor) {
        try {
            String annoTypeName = AsmUtils.convertTypeDescriptorToClassName(attribAnnoTypeDescriptor);
            @SuppressWarnings("unchecked")
            Class<? extends Annotation> annoType = (Class<? extends Annotation>) getDefaultClassLoader().loadClass(annoTypeName);
            Annotation annoValue = createMutableAnnotation(annoType);
            Field annoAttrib = mutableAnno.getClass().getField(attribName);
            annoAttrib.set(mutableAnno, annoValue);
            return new MutableAnnotationVisitor((MutableAnnotation) annoValue);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    public void visitEnd() { }

}
