//
//  ========================================================================
//  Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//  ------------------------------------------------------------------------
//  All rights reserved. This program and the accompanying materials
//  are made available under the terms of the Eclipse Public License v1.0
//  and Apache License v2.0 which accompanies this distribution.
//
//      The Eclipse Public License is available at
//      http://www.eclipse.org/legal/epl-v10.html
//
//      The Apache License v2.0 is available at
//      http://www.opensource.org/licenses/apache2.0.php
//
//  You may elect to redistribute this code under either of these licenses.
//  ========================================================================
//

package org.eclipse.jetty.webapp;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.annotation.Name;


/* ------------------------------------------------------------------------------- */
/** Base Class for WebApplicationContext Configuration.
 * This class can be extended to customize or extend the configuration
 * of the WebApplicationContext. 
 */
public interface Configuration 
{
    public final static String ATTR="org.eclipse.jetty.webapp.configuration";
    
    /* ------------------------------------------------------------------------------- */
    /** Set up for configuration.
     * <p>
     * Typically this step discovers configuration resources
     * @param context The context to configure
     * @throws Exception
     */
    public void preConfigure (WebAppContext context) throws Exception;
    
    
    /* ------------------------------------------------------------------------------- */
    /** Configure WebApp.
     * <p>
     * Typically this step applies the discovered configuration resources to
     * either the {@link WebAppContext} or the associated {@link MetaData}.
     * @param context The context to configure
     * @throws Exception
     */
    public void configure (WebAppContext context) throws Exception;
    
    
    /* ------------------------------------------------------------------------------- */
    /** Clear down after configuration.
     * @param context The context to configure
     * @throws Exception
     */
    public void postConfigure (WebAppContext context) throws Exception;
    
    /* ------------------------------------------------------------------------------- */
    /** DeConfigure WebApp.
     * This method is called to undo all configuration done. This is
     * called to allow the context to work correctly over a stop/start cycle
     * @param context The context to configure
     * @throws Exception
     */
    public void deconfigure (WebAppContext context) throws Exception;

    /* ------------------------------------------------------------------------------- */
    /** Destroy WebApp.
     * This method is called to destroy a webappcontext. It is typically called when a context 
     * is removed from a server handler hierarchy by the deployer.
     * @param context The context to configure
     * @throws Exception
     */
    public void destroy (WebAppContext context) throws Exception;
    

    /* ------------------------------------------------------------------------------- */
    /** Clone configuration instance.
     * <p>
     * Configure an instance of a WebAppContext, based on a template WebAppContext that 
     * has previously been configured by this Configuration.
     * @param template The template context
     * @param context The context to configure
     * @throws Exception
     */
    public void cloneConfigure (WebAppContext template, WebAppContext context) throws Exception;
    
    
    public class ClassList extends ArrayList<String>
    {        
        /* ------------------------------------------------------------ */
        /** Get/Set/Create the server default Configuration ClassList.
         * <p>Get the class list from: a Server bean; or the attribute (which can
         * either be a ClassList instance or an String[] of class names); or a new instance
         * with default configuration classes.</p>
         * <p>This method also adds the obtained ClassList instance as a dependent bean
         * on the server and clears the attribute</p>
         * @param server The server the default is for
         * @return the server default ClassList instance of the configuration classes for this server. Changes to this list will change the server default instance.
         */
        public static ClassList setServerDefault(Server server)
        {
            ClassList cl=server.getBean(ClassList.class);
            if (cl!=null)
                return cl;
            cl=serverDefault(server);
            server.addBean(cl);
            server.setAttribute(ATTR,null);
            return cl;
        }

        /* ------------------------------------------------------------ */
        /** Get/Create the server default Configuration ClassList.
         * <p>Get the class list from: a Server bean; or the attribute (which can
         * either be a ClassList instance or an String[] of class names); or a new instance
         * with default configuration classes.
         * @param server The server the default is for
         * @return A copy of the server default ClassList instance of the configuration classes for this server. Changes to the returned list will not change the server default.
         */
        public static ClassList serverDefault(Server server)
        {
            ClassList cl=server.getBean(ClassList.class);
            if (cl!=null)
                return new ClassList(cl);
            Object attr = server.getAttribute(ATTR);
            if (attr instanceof ClassList)
                return new ClassList((ClassList)attr);
            if (attr instanceof String[])
                return new ClassList((String[])attr);
            return new ClassList();
        }
        
        public ClassList()
        {
            this(WebAppContext.DEFAULT_CONFIGURATION_CLASSES);
        }
        
        public ClassList(String[] classes)
        {
            addAll(Arrays.asList(classes));
        }

        public ClassList(List<String> classes)
        {
            addAll(classes);
        }
        
        public void addAfter(@Name("afterClass") String afterClass,@Name("configClass")String... configClass)
        {
            if (configClass!=null && afterClass!=null)
            {
                ListIterator<String> iter = listIterator();
                while (iter.hasNext())
                {
                    String cc=iter.next();
                    if (afterClass.equals(cc))
                    {
                        for (int i=0;i<configClass.length;i++)
                            iter.add(configClass[i]);
                        return;
                    }
                }
            }
            throw new IllegalArgumentException("afterClass '"+afterClass+"' not found in "+this);
        }

        public void addBefore(@Name("beforeClass") String beforeClass,@Name("configClass")String... configClass)
        {
            if (configClass!=null && beforeClass!=null)
            {
                ListIterator<String> iter = listIterator();
                while (iter.hasNext())
                {
                    String cc=iter.next();
                    if (beforeClass.equals(cc))
                    {
                        iter.previous();
                        for (int i=0;i<configClass.length;i++)
                            iter.add(configClass[i]);
                        return;
                    }
                }
            }
            throw new IllegalArgumentException("beforeClass '"+beforeClass+"' not found in "+this);
        }
        
    }
}
