/**
 * Copyright 2011-2015 John Ericksen
 *
 * 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.androidtransfuse.experiment.generators;

import com.sun.codemodel.JBlock;
import com.sun.codemodel.JClassAlreadyExistsException;
import com.sun.codemodel.JExpression;
import org.androidtransfuse.TransfuseAnalysisException;
import org.androidtransfuse.adapter.ASTMethod;
import org.androidtransfuse.adapter.ASTType;
import org.androidtransfuse.adapter.element.ASTElementFactory;
import org.androidtransfuse.adapter.element.ASTTypeBuilderVisitor;
import org.androidtransfuse.analysis.AnalysisContext;
import org.androidtransfuse.analysis.InjectionPointFactory;
import org.androidtransfuse.annotations.LayoutHandler;
import org.androidtransfuse.experiment.*;
import org.androidtransfuse.gen.InjectionFragmentGenerator;
import org.androidtransfuse.gen.InstantiationStrategyFactory;
import org.androidtransfuse.layout.LayoutHandlerDelegate;
import org.androidtransfuse.model.InjectionNode;
import org.androidtransfuse.model.MethodDescriptor;
import org.androidtransfuse.model.TypedExpression;
import org.androidtransfuse.util.AndroidLiterals;

import javax.inject.Inject;
import javax.inject.Provider;
import javax.lang.model.type.TypeMirror;
import java.util.Map;

import static org.androidtransfuse.util.TypeMirrorUtil.getTypeMirror;

/**
 * @author John Ericksen
 */
public class LayoutHandlerGenerator implements Generation {

    private final Provider<ASTTypeBuilderVisitor> astTypeBuilderVisitorProvider;
    private final InjectionPointFactory injectionPointFactory;
    private final InjectionFragmentGenerator injectionFragmentGenerator;
    private final InstantiationStrategyFactory instantiationStrategyFactory;
    private final ASTElementFactory astElementFactory;

    @Inject
    public LayoutHandlerGenerator(Provider<ASTTypeBuilderVisitor> astTypeBuilderVisitorProvider,
                                  InjectionPointFactory injectionPointFactory,
                                  InjectionFragmentGenerator injectionFragmentGenerator,
                                  InstantiationStrategyFactory instantiationStrategyFactory,
                                  ASTElementFactory astElementFactory) {
        this.astTypeBuilderVisitorProvider = astTypeBuilderVisitorProvider;
        this.injectionPointFactory = injectionPointFactory;
        this.injectionFragmentGenerator = injectionFragmentGenerator;
        this.instantiationStrategyFactory = instantiationStrategyFactory;
        this.astElementFactory = astElementFactory;
    }

    @Override
    public String getName() {
        return "LayoutHandler Generator";
    }

    @Override
    public void schedule(final ComponentBuilder builder, final ComponentDescriptor descriptor) {

        ASTType target = descriptor.getTarget();

        if(target.isAnnotated(LayoutHandler.class)) {
            LayoutHandler layoutHandlerAnnotation = target.getAnnotation(LayoutHandler.class);

            final InjectionNode layoutHandlerInjectionNode = buildLayoutHandlerInjectionNode(layoutHandlerAnnotation, builder.getAnalysisContext());

            ASTMethod onCreateMethod = astElementFactory.findMethod(AndroidLiterals.ACTIVITY, "onCreate", AndroidLiterals.BUNDLE);
            builder.add(onCreateMethod, GenerationPhase.LAYOUT, new ComponentMethodGenerator() {
                @Override
                public void generate(MethodDescriptor methodDescriptor, JBlock block) {
                    try {

                        Map<InjectionNode, TypedExpression> expressionMap = injectionFragmentGenerator.buildFragment(block,
                                instantiationStrategyFactory.buildMethodStrategy(block, builder.getScopes()),
                                builder.getDefinedClass(),
                                layoutHandlerInjectionNode,
                                builder.getScopes());

                        //LayoutHandlerDelegate.invokeLayout()
                        JExpression layoutHandlerDelegate = expressionMap.get(layoutHandlerInjectionNode).getExpression();

                        block.add(layoutHandlerDelegate.invoke(LayoutHandlerDelegate.INVOKE_LAYOUT_METHOD));

                    } catch (JClassAlreadyExistsException e) {
                        throw new TransfuseAnalysisException("Class Already Exists ", e);
                    }
                }
            });
        }


    }

    private InjectionNode buildLayoutHandlerInjectionNode(final LayoutHandler layoutHandlerAnnotation, AnalysisContext context) {
        if (layoutHandlerAnnotation != null) {
            TypeMirror layoutHandlerType = getTypeMirror(layoutHandlerAnnotation, "value");

            if (layoutHandlerType != null) {
                ASTType layoutHandlerASTType = layoutHandlerType.accept(astTypeBuilderVisitorProvider.get(), null);
                return injectionPointFactory.buildInjectionNode(layoutHandlerASTType, context);
            }
        }
        return null;
    }
}
