/*
 * Copyright (c) 2005-2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. 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.synapse.transport.dynamicconfigurations;

import org.apache.axiom.om.OMElement;
import org.apache.axis2.description.Parameter;
import org.apache.axis2.description.ParameterInclude;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.xml.namespace.QName;

/**
 * Abstract class to use as Profile Reloader Subscribers. FileUpdateNotificationHandler will notify
 * this once the event is triggered.
 */
public abstract class DynamicProfileReloader {

    private static final Log log = LogFactory.getLog(DynamicProfileReloader.class);

    /* XML parameter name for dynamic profiles in Axis2 config */
    private final String PROFILE_CONFIG_NAME = "dynamicSSLProfilesConfig";

    /* XML element name for dynamic profiles configuration path in Axis2 config */
    private final String PATH_CONFIG_NAME = "filePath";

    /* XML element name for dynamic profiles file read interval in Axis2 config */
    private final String INTERVAL_CONFIG_NAME = "fileReadInterval";

    private boolean invokedFromSchedule = true;

    private long lastUpdatedtime;

    private String filePath;

    public abstract void notifyFileUpdate(boolean isScheduled);

    protected FileUpdateNotificationHandler fileUpdateNotificationHandler;

    /**
     * Returns Last Updated Time of the File
     * @return Long time in milliseconds
     */
    public long getLastUpdatedtime() {
        return this.lastUpdatedtime;
    }

    /**
     * Returns File Path of the dynamic SSL profiles
     *
     * @return String file path
     */
    public String getFilePath() {
        return this.filePath;
    }

    /**
     * Set file path
     *
     * @param filePath String file path
     */
    public void setFilePath(String filePath) {
        this.filePath = filePath;
    }

    /**
     * Set Last Updated time of the file
     *
     * @param lastUpdatedtime Long time in milliseconds
     */
    public void setLastUpdatedtime(long lastUpdatedtime) {
        this.lastUpdatedtime = lastUpdatedtime;
    }

    /**
     * Check whether the file is invoked from scheduled task
     *
     * @return true if invoked from schedule, false otherwise
     */
    public boolean isInvokedFromSchedule() {
        return invokedFromSchedule;
    }

    /**
     * Set whether the file is invoked from schedule
     *
     * @param invokedFromSchedule true if invoked from schedule, false otherwise
     */
    public void setInvokedFromSchedule(boolean invokedFromSchedule) {
        this.invokedFromSchedule = invokedFromSchedule;
    }

    /**
     * Set the configuration file path from custom SSL config
     *
     * @param transportOut TransportOutDescription of the configuration
     * @return File Path String
     */
    protected String extractConfigurationFilePath(ParameterInclude transportOut) {
        String path = null;
        Parameter profileParam = transportOut.getParameter(PROFILE_CONFIG_NAME);
        //No Separate configuration file configured. Therefore using Axis2 Configuration
        if (profileParam != null) {
            OMElement profileParamElem = profileParam.getParameterElement();
            path = profileParamElem.getFirstChildWithName(new QName(PATH_CONFIG_NAME)).getText();

        }
        return path;
    }

    /**
     * Set SSL Profile configuration loading interval from Axis2 config
     *
     * @param transportOut TransportOutDescription of the configuration
     * @return Long value of the interval in milliseconds
     */
    protected long extractSleepInterval(ParameterInclude transportOut) {
        long fileReadInterval = -1;
        Parameter profileParam = transportOut.getParameter(PROFILE_CONFIG_NAME);
        //No Separate configuration file configured. Therefore using Axis2 Configuration
        if (profileParam != null) {
            OMElement profileParamElem = profileParam.getParameterElement();
            String interval = profileParamElem.getFirstChildWithName(new QName(INTERVAL_CONFIG_NAME)).getText();
            if (interval != null) {
                fileReadInterval = Long.parseLong(interval);
            }
        }
        return fileReadInterval;
    }

    /**
     * Register this Profile Loader in FileUpdateNotificationHandler for notifications
     *
     * @param transportDescription Transport In/Out Description of the configuration
     */
    protected boolean registerListener(ParameterInclude transportDescription) {
        boolean notificationHandlerStarted = false;
        long configurationLoadingInterval = extractSleepInterval(transportDescription);
        String filePath = extractConfigurationFilePath(transportDescription);
        //Create File Update Notification Handler only if file path is configured
        if (filePath != null) {
            fileUpdateNotificationHandler = new FileUpdateNotificationHandler(configurationLoadingInterval);
            setFilePath(filePath);
            setLastUpdatedtime(System.currentTimeMillis());

            fileUpdateNotificationHandler.registerListener(this);
            DynamicProfileReloaderHolder.getInstance().addNotificationHandler(fileUpdateNotificationHandler);
            notificationHandlerStarted = true;
        } else {
            if (log.isDebugEnabled()) {
                log.debug("Configuration File path is not configured and SSL Profiles will not be loaded " +
                          "dynamically in " + this.getClass().getName());
            }
        }
        return notificationHandlerStarted;
    }

    /**
     * Get actual class name from comprehensive class name
     *
     * @param completeClassName complete class name String
     * @return instance name String
     */
    public String getClassName(String completeClassName) {
        String absoluteClassName = null;
        if (completeClassName != null) {
            absoluteClassName = completeClassName.substring(completeClassName.lastIndexOf(".") + 1);
        }
        return absoluteClassName;
    }
}
