/*
 * Copyright 2009 Mikhail Kryshen
 *
 * 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 kryshen.catalina.userconfig;

import java.io.File;
import org.apache.catalina.startup.HostConfig;
import org.apache.juli.logging.LogFactory;

/**
 * Event listener for Host that deploys and updates applications
 * provided by users. In multi-app mode (appsPerUser property > 1)
 * each user's application is mapped to "/~username/application" path,
 * ROOT is mapped to "/~username". In single-app mode (appsPerUser = 1)
 * each user could have single webapp mapped to "/~username".
 *
 * @author Mikhail Kryshen
 */
public abstract class UserConfig extends HostConfig {

    /**
     * The directory name to be searched for within each user home directory.
     */
    private String directoryName = "public_webapps";

    /**
     * Maximum number of application each user is allowed to deploy.
     */
    private int appsPerUser = -1;


    protected UserConfig() {
        log = LogFactory.getLog(UserConfig.class);
    }

    /**
     *  Returns the directory name to be searched for webapps for each user
     *  (relative to the user's home direcotry).
     */
    public String getDirectoryName() {
        return directoryName;
    }

    /**
     *  Set the directory name to be searched for webapps for each user
     *  (relative to the user's home direcotry).
     * 
     * @param directoryName The new directory name.
     */
    public void setDirectoryName(String directoryName) {
        this.directoryName = directoryName;
    }

    /**
     * Returns the maximum number of application each user is allowed to
     * deploy.
     */
    public int getAppsPerUser() {
        return appsPerUser;
    }

    /**
     * Set the maximum number of application each user is allowed to deploy.
     */
    public void setAppsPerUser(int appsPerUser) {
        if (appsPerUser < 1 && appsPerUser != -1) {
            throw new IllegalArgumentException("Invalid appsPerUser value.");
        }

        this.appsPerUser = appsPerUser;
    }

    @Override
    protected void deployApps() {       
        deployUserApps();
    }

    /**
     * Deploy applications for all available users.
     */
    protected abstract void deployUserApps();

    /**
     * Deploy applications (if any) for specific user.
     *
     * @param user Username.
     * @param home User home directory.
     */
    protected void deployUserApps(String user, File home) {
        File base = new File(home, directoryName);

        if (!base.isDirectory() || !base.canRead()) {
            return;
        }

        String[] files = base.list();

        if (files == null) {
            log.warn("Error reading base directory: " + base.getPath() + ".");
            return;
        }

        // TODO: deployWars

        if (appsPerUser == 1) {
            // Single application mode.
            String contextPath = getContextPath(user, "ROOT");

            if (isServiced(contextPath)) {
                return;
            }

            deployDirectory(contextPath, base, base.getAbsolutePath());
        } else {
            // Multiple applications mode.
            deployUserDirectories(user, base, files);
        }
    }

    /**
     * Deploy user application directories.
     *
     * @param user Username.
     * @param base Application base directory.
     * @param files Array of directories to deploy.
     */
    protected void deployUserDirectories(String user, File base, 
            String[] files) {

        int appCount = 0;

        if (appsPerUser > 0) {
            // Count number of already deployed user applications.
            for (int i = 0; i < files.length; i++) {
                String contextPath = getContextPath(user, files[i]);

                if (isDeployed(contextPath)) {
                    appCount++;
                }
            }
        }

        // Deploy new applications.
        for (int i = 0; i < files.length; i++) {
            if (appsPerUser > 0 && appCount >= appsPerUser) {
                break;
            }

            if (files[i].equalsIgnoreCase("META-INF") ||
                    files[i].equalsIgnoreCase("WEB-INF")) {
                continue;
            }

            File dir = new File(base, files[i]);

            if (!dir.isDirectory() || !dir.canRead()) {
                continue;
            }

            String contextPath = getContextPath(user, files[i]);

            if (isDeployed(contextPath) || isServiced(contextPath)) {
                continue;
            }

            deployDirectory(contextPath, dir, dir.getAbsolutePath());
            appCount++;
        }
    }

    /**
     * Constructs context path for the specifed user name and application.
     *
     * @param user User name.
     * @param app Application name.
     * @return Context path.
     */
    protected String getContextPath(String user, String app) {
        if (app.equals("ROOT")) {
            return "/~" + user;
        }

        return "/~" + user + '/' + app.replace('#', '/');
    }

    /**
     * Process a start event.
     */
    @Override
    public void start() {
        if (log.isDebugEnabled()) {
            log.debug("Starging UserConfig.");
        }

        if (host.getDeployOnStartup()) {
            deployApps();
        }
    }

    /**
     * Process a stop event.
     */
    @Override
    public void stop() {
        if (log.isDebugEnabled()) {
            log.debug("Stopping UserConfig");
        }

        undeployApps();
    }
}