/*
 * 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.plugin.tx;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor;
import org.springframework.transaction.interceptor.TransactionInterceptor;


/** TODO: JAVADOC */
public class GeneralizedAnnotationDrivenBeanDefinitionParser {

    public static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";

    /**
     * The bean name of the internally managed transaction advisor (mode="proxy").
     */
    public static final String TRANSACTION_ADVISOR_BEAN_NAME =
            "org.springframework.transaction.config.internalTransactionAdvisor";

    /**
     * The bean name of the internally managed transaction aspect (mode="aspectj").
     */
    public static final String TRANSACTION_ASPECT_BEAN_NAME =
            "org.springframework.transaction.config.internalTransactionAspect";

    private static final String TRANSACTION_ASPECT_CLASS_NAME =
            "org.springframework.transaction.aspectj.AnnotationTransactionAspect";


    public BeanDefinition parseDeclaration(TxAnnotationDrivenDeclaration txDec, BeanDefinitionRegistry registry) {

        if (txDec.mode == AopMode.ASPECTJ) {
            // mode="aspectj"
            registerTransactionAspect(txDec, registry);
        }
        else {
            // mode="proxy"
            AopAutoProxyConfigurer.configureAutoProxyCreator(txDec, registry);
        }

        return null;
    }

    private void registerTransactionAspect(TxAnnotationDrivenDeclaration txDec, BeanDefinitionRegistry registry) {
        if (!registry.containsBeanDefinition(TRANSACTION_ASPECT_BEAN_NAME)) {
            RootBeanDefinition def = new RootBeanDefinition();
            def.setBeanClassName(TRANSACTION_ASPECT_CLASS_NAME);
            def.setFactoryMethodName("aspectOf");
            registerTransactionManager(txDec, def);
            //parserContext.registerBeanComponent(new BeanComponentDefinition(def, TRANSACTION_ASPECT_BEAN_NAME));
            registry.registerBeanDefinition(TRANSACTION_ASPECT_BEAN_NAME, def);
        }
    }

    private static void registerTransactionManager(TxAnnotationDrivenDeclaration txDec, BeanDefinition def) {
        def.getPropertyValues().addPropertyValue(
                TxNamespaceUtils.TRANSACTION_MANAGER_PROPERTY, new RuntimeBeanReference(txDec.transactionManagerName));
    }

    /**
     * Inner class to just introduce an AOP framework dependency when actually in proxy mode.
     */
    private static class AopAutoProxyConfigurer {

        public static void configureAutoProxyCreator(TxAnnotationDrivenDeclaration txDec, BeanDefinitionRegistry registry) {
            GeneralizedAopNamespaceUtils.registerAutoProxyCreatorIfNecessary(registry, txDec);

            if (!registry.containsBeanDefinition(TRANSACTION_ADVISOR_BEAN_NAME)) {
                // Create the TransactionInterceptor definition.
                RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
                //interceptorDef.setSource(registry.extractSource(txDec.getSource()));
                interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
                registerTransactionManager(txDec, interceptorDef);

                Class<?> sourceClass = TxNamespaceUtils.getAnnotationTransactionAttributeSourceClass();
                interceptorDef.getPropertyValues().addPropertyValue(
                        TxNamespaceUtils.TRANSACTION_ATTRIBUTE_SOURCE, new RootBeanDefinition(sourceClass));

                // Create the TransactionAttributeSourceAdvisor definition.
                RootBeanDefinition advisorDef = new RootBeanDefinition(TransactionAttributeSourceAdvisor.class);
                //advisorDef.setSource(registry.extractSource(txDec.getSource()));
                advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
                advisorDef.getPropertyValues().addPropertyValue("transactionInterceptor", interceptorDef);
                if (txDec.isExplicitlyOrdered()) {
                    advisorDef.getPropertyValues().addPropertyValue("order", txDec.getOrder());
                }

                //parserContext.registerBeanComponent(new BeanComponentDefinition(advisorDef, TRANSACTION_ADVISOR_BEAN_NAME));
                registry.registerBeanDefinition(TRANSACTION_ADVISOR_BEAN_NAME, advisorDef);
            }
        }
    }

}
