/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.apache.myfaces.orchestra.viewController;

import org.apache.commons.lang.StringUtils;
import org.apache.myfaces.orchestra.annotation.AnnotationInfo;
import org.apache.myfaces.orchestra.annotation.AnnotationInfoManager;
import org.apache.myfaces.orchestra.conversation.ConversationManager;
import org.apache.myfaces.orchestra.conversation.annotations.ConversationRequire;
import org.apache.myfaces.orchestra.frameworkAdapter.FrameworkAdapter;
import org.apache.myfaces.orchestra.lib.OrchestraException;

import java.io.IOException;

/**
 * A {@link org.apache.myfaces.orchestra.viewController.ViewControllerManager} implementation which uses
 * annotations on backing beans to determine the beans responsible for a given view and execute
 * the appropriate annotated methods.
 *
 * <p>When using Spring, every bean declaration in the spring config files is checked to see if the
 * referenced class has annotations, and if so that information is cached. Here, that information is
 * then used to locate a bean which has a ViewController annotation that references the current view.</p>
 *
 * <p>See also org.apache.myfaces.orchestra.viewController.annotations.*.</p>
 *
 * @see org.apache.myfaces.orchestra.viewController.ViewControllerManager
 */
public abstract class AbstractAnnotationsViewControllerManager extends AbstractViewControllerManager
{
    private AnnotationInfoManager annotationInfoManager;

    public AbstractAnnotationsViewControllerManager()
    {
    }

    public abstract void initManager();

    public void setAnnotationInfoManager(AnnotationInfoManager annotationInfoManager)
    {
        this.annotationInfoManager = annotationInfoManager;
    }

    public AnnotationInfoManager getAnnotationInfoManager()
    {
        return annotationInfoManager;
    }

    public void assertConversationState(String viewId)
    {
        String beanName = getViewControllerNameMapper().mapViewId(viewId);
        if (beanName == null)
        {
            return;
        }

        AnnotationInfo annotationInfo = annotationInfoManager.getAnnotationInfoByBeanName(beanName);
        if (annotationInfo == null)
        {
            return;
        }

        ConversationRequire conversationRequire = annotationInfo.getConversationRequire();
        if (conversationRequire == null)
        {
            return;
        }

        // check if we are on an ignored view
        String[] entryPoints = conversationRequire.entryPointViewIds();
        if (entryPoints != null)
        {
            for(String ignoredView: entryPoints)
            {
                if (!StringUtils.isEmpty(ignoredView) && ignoredView.equals(viewId))
                {
                    return;
                }
            }
        }

        // check that all the required conversations already exist
        ConversationManager manager = ConversationManager.getInstance();
        String[] conversationNames = conversationRequire.conversationNames();
        if (conversationNames != null) 
        {
            for(String conversationName: conversationNames)
            {
                if (!manager.hasConversation(conversationName))
                {
                    reportInactive(manager, conversationName, conversationRequire);
                    return;
                }
            }
        }
    }
    
    private void reportInactive(ConversationManager manager, String conversationName,
            ConversationRequire conversationRequire)
    {
        // oops..handle failure
        if (manager.getMessager() != null)
        {
            manager.getMessager().setConversationNotActive(conversationName);
        }

        if (!StringUtils.isEmpty(conversationRequire.redirect()))
        {
            try
            {
                FrameworkAdapter.getCurrentInstance().redirect(conversationRequire.redirect());
            }
            catch (IOException e)
            {
                throw new OrchestraException(e);
            }
        }
        else if (!StringUtils.isEmpty(conversationRequire.navigationAction()))
        {
            try
            {
                String dst = conversationRequire.navigationAction();
                FrameworkAdapter.getCurrentInstance().invokeNavigation(dst);
            }
            catch (IOException e)
            {
                throw new OrchestraException(e);
            }
        }
    }
}
