/*
 * The contents of this file are subject to the terms
 * of the Common Development and Distribution License
 * (the License).  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the license at
 * https://glassfish.dev.java.net/public/CDDLv1.0.html or
 * glassfish/bootstrap/legal/CDDLv1.0.txt.
 * See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL
 * Header Notice in each file and include the License file
 * at glassfish/bootstrap/legal/CDDLv1.0.txt.
 * If applicable, add the following below the CDDL Header,
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 *
 * Copyright (c) Ericsson AB, 2004-2007. All rights reserved.
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 */
package org.jvnet.glassfish.comms.admin.mbeans.extensions;

import com.sun.enterprise.admin.mbeans.ConfigsMBean;
import com.sun.enterprise.admin.server.core.AdminService;
import com.sun.enterprise.admin.target.ConfigTarget;
import com.sun.enterprise.admin.target.Target;
import com.sun.enterprise.admin.target.TargetBuilder;
import com.sun.enterprise.admin.target.TargetType;
import com.sun.enterprise.config.ConfigContext;
import com.sun.enterprise.config.ConfigException;
import com.sun.enterprise.config.serverbeans.ApplicationHelper;
import com.sun.enterprise.config.serverbeans.ApplicationRef;
import com.sun.enterprise.config.serverbeans.AvailabilityService;
import com.sun.enterprise.config.serverbeans.Cluster;
import com.sun.enterprise.config.serverbeans.ClusterHelper;
import com.sun.enterprise.config.serverbeans.Config;
import com.sun.enterprise.config.serverbeans.ConfigAPIHelper;
import com.sun.enterprise.config.serverbeans.Configs;
import com.sun.enterprise.config.serverbeans.ConvergedLbClusterRef;
import com.sun.enterprise.config.serverbeans.ConvergedLbConfig;
import com.sun.enterprise.config.serverbeans.ConvergedLbConfigs;
import com.sun.enterprise.config.serverbeans.ConvergedLbPolicy;
import com.sun.enterprise.config.serverbeans.ConvergedLoadBalancer;
import com.sun.enterprise.config.serverbeans.Domain;
import com.sun.enterprise.config.serverbeans.ElementProperty;
import com.sun.enterprise.config.serverbeans.Proxy;
import com.sun.enterprise.config.serverbeans.Server;
import com.sun.enterprise.config.serverbeans.ServerHelper;
import com.sun.enterprise.config.serverbeans.ServerRef;
import com.sun.enterprise.util.SystemPropertyConstants;
import com.sun.enterprise.util.i18n.StringManager;
import com.sun.enterprise.util.i18n.StringManagerBase;

import com.sun.logging.ee.EELogDomains;

import java.lang.Boolean;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.management.*;


public class ConvergedLbConfigsMBean extends ConfigsMBean {
    private static final StringManager _strMgr = StringManager.getManager(ConvergedLbConfigsMBean.class);
    private static Logger _logger = Logger.getLogger(EELogDomains.EE_ADMIN_LOGGER);
    private static final StringManagerBase _sMgr = StringManagerBase.getStringManager(_logger.getResourceBundleName());
    private ObjectName on;

    public ConvergedLbConfigsMBean() {
        super();
    }

    private ElementProperty[] convertPropertiesToElementProperties(
        Properties props) {
        ArrayList list = new ArrayList();
        Enumeration keys = props.keys();

        while (keys.hasMoreElements()) {
            final String key = (String) keys.nextElement();
            ElementProperty property = new ElementProperty();
            property.setName(key);
            property.setValue((String) props.get(key));
            list.add(property);
        }

        return (ElementProperty[]) list.toArray(new ElementProperty[list.size()]);
    }

    protected ConvergedLbConfigs getConvergedLbConfigs() {
        try {
            ConfigContext ctx = AdminService.getAdminService().getAdminContext()
                                            .getAdminConfigContext();
            Domain domain = (Domain) ctx.getRootConfigBean();
            ConvergedLbConfigs clbConfigs = domain.getConvergedLbConfigs();

            if (clbConfigs == null) {
                _logger.log(Level.INFO, "clbconfigs is null");
                clbConfigs = new ConvergedLbConfigs();
                domain.setConvergedLbConfigs(clbConfigs);
            } else {
                _logger.log(Level.INFO, "clbconfigs is not null");
            }

            return clbConfigs;
        } catch (ConfigException ce) {
            return null;
        }
    }

    protected ConvergedLbConfig getConvergedLbConfig(String configName)
        throws MBeanException {
        ConvergedLbConfigs clbConfigs = getConvergedLbConfigs();

        if (clbConfigs == null) {
            _logger.log(Level.INFO, "clbconfigs is null");
            _logger.log(Level.FINE, _sMgr.getString("http_lb_admin.NoLbConfigs"));

            String msg = _strMgr.getString("NoLbConfigsElement");
            throw new MBeanException(new ConfigException(msg));
        }

        ConvergedLbConfig clbConfig = clbConfigs.getConvergedLbConfigByName(configName);

        if (clbConfig == null) {
            _logger.log(Level.FINE, _sMgr.getString("http_lb_admin.NoLbConfigs"));

            String msg = _strMgr.getString("InvalidLbConfigName", configName);
            throw new MBeanException(new ConfigException(msg));
        }

        return clbConfig;
    }

    public String[] getConvergedLbConfigsForServer(String serverName)
        throws MBeanException {
        ArrayList sList = new ArrayList();
        ConvergedLbConfigs clbConfigs = getConvergedLbConfigs();

        if (clbConfigs == null) {
            // return, no lb configs
            return null;
        }

        ConvergedLbConfig[] clbConfigArray = clbConfigs.getConvergedLbConfig();

        for (int i = 0; i < clbConfigArray.length; i++) {
            ServerRef[] sRefs = clbConfigArray[i].getServerRef();

            for (int j = 0; j < sRefs.length; j++) {
                if (sRefs[j].getRef().equals(serverName)) {
                    sList.add(clbConfigArray[i].getName());
                }
            }
        }

        String[] strList = new String[sList.size()];

        return (String[]) sList.toArray(strList);
    }

    public String[] getConvergedLbConfigsForCluster(String clusterName)
        throws MBeanException {
        ArrayList sList = new ArrayList();
        ConvergedLbConfigs clbConfigs = getConvergedLbConfigs();

        if (clbConfigs == null) {
            // return, no lb configs
            return null;
        }

        ConvergedLbConfig[] clbConfigArray = clbConfigs.getConvergedLbConfig();

        for (int i = 0; i < clbConfigArray.length; i++) {
            ConvergedLbClusterRef[] cRefs = clbConfigArray[i].getConvergedLbClusterRef();

            for (int j = 0; j < cRefs.length; j++) {
                if (cRefs[j].getRef().equals(clusterName)) {
                    sList.add(clbConfigArray[i].getName());
                }
            }
        }

        String[] strList = new String[sList.size()];

        return (String[]) sList.toArray(strList);
    }

    public void createConvergedLbConfig(String reloadInterval,
        String dcrFileName, String httpLbPolicy, String sipLbPolicy,
        Properties props, String clbConfigName) throws MBeanException {
        try {
            ConvergedLbConfigs clbConfigs = null;
            clbConfigs = getConvergedLbConfigs();

            ConvergedLbConfig clbConfig = new ConvergedLbConfig();
            clbConfig.setName(clbConfigName);

            ConvergedLbPolicy clbPolicy = new ConvergedLbPolicy();
            clbPolicy.setHttp(httpLbPolicy);
            clbPolicy.setSip(sipLbPolicy);
            clbPolicy.setDcrFile(dcrFileName);
            clbConfig.setConvergedLbPolicy(clbPolicy);

            clbConfigs.addConvergedLbConfig(clbConfig);
        } catch (ConfigException ce) {
            System.out.println("exce");
        }
    }

    public boolean deleteConvergedLbConfig(String configName)
        throws MBeanException {
        ConvergedLbConfigs clbConfigs = getConvergedLbConfigs();

        if (clbConfigs == null) {
            String msg = _strMgr.getString("NoLbConfigsElement");
            throw new MBeanException(new ConfigException(msg));
        }

        ConvergedLbConfig clbConfig = clbConfigs.getConvergedLbConfigByName(configName);

        if (clbConfig == null) {
            // Nothing to be deleted
            String msg = _strMgr.getString("InvalidLbConfigName", configName);
            throw new MBeanException(new ConfigException(msg));
        }

        if (((clbConfig.getServerRef() == null) ||
                (clbConfig.getServerRef().length == 0)) &&
                ((clbConfig.getConvergedLbClusterRef() == null) ||
                (clbConfig.getConvergedLbClusterRef().length == 0))) {
            clbConfigs.removeConvergedLbConfig(clbConfig);
            _logger.log(Level.INFO,
                _sMgr.getString("http_lb_admin.LbConfigDeleted", configName));
        } else {
            String msg = _strMgr.getString("LbConfigNotEmpty", configName);
            throw new MBeanException(new ConfigException(msg));
        }

        return true;
    }

    public String[] listConvergedLbConfigs(String target)
        throws MBeanException {
        _logger.log(Level.FINE, "[LBAdminMBean] listLBConfigs called");

        ConvergedLbConfigs clbConfigs = getConvergedLbConfigs();

        if (clbConfigs == null) {
            _logger.log(Level.INFO, _sMgr.getString("http_lb_admin.NoLbConfigs"));

            return null;
        }

        String[] names = null;

        if (target == null) {
            ConvergedLbConfig[] clbConfigArray = clbConfigs.getConvergedLbConfig();

            if (clbConfigArray.length == 0) {
                _logger.log(Level.INFO,
                    _sMgr.getString("http_lb_admin.NoLbConfigs"));

                return null;
            }

            names = new String[clbConfigArray.length];

            for (int i = 0; i < clbConfigArray.length; i++) {
                names[i] = clbConfigArray[i].getName();
            }
        } else {
            ConfigContext ctx = AdminService.getAdminService().getAdminContext()
                                            .getAdminConfigContext();

            try {
                // target is a cluster
                if (ClusterHelper.isACluster(ctx, target)) {
                    names = getConvergedLbConfigsForCluster(target);

                    // target is a server
                } else if (ServerHelper.isAServer(ctx, target)) {
                    names = getConvergedLbConfigsForServer(target);
                }
            } catch (ConfigException ce) {
                throw new MBeanException(ce);
            }
        }

        return names;
    }

    public void createConvergedLoadBalancer(String clbConfigFile, // location of converged-load-balancer.xml
        String clbConfigName, // converged-lb-config
        Boolean autocommit, // if true, apply changes immediately
        String targetName, // target name (cluster or server)
        Properties props, // proxy's properties
        String clbName) throws MBeanException {
        try {
            final Target target = getTarget(targetName);
            final ConfigTarget configTarget = target.getConfigTarget();

            Config config = ConfigAPIHelper.getConfigByName(getConfigContext(),
                    configTarget.getName());
            AvailabilityService availabilityService = config.getAvailabilityService();

            if (availabilityService == null) {
                availabilityService = new AvailabilityService();
                config.setAvailabilityService(availabilityService);
            }

            ConvergedLoadBalancer clb = availabilityService.getConvergedLoadBalancer();
            boolean bClbJustCreated = false;

            if (clb == null) {
                // we have to create it
                clb = new ConvergedLoadBalancer();
                clb.setName(clbName);
                clb.setConfigFile(clbConfigFile);
                clb.setConvergedLbConfigName(clbConfigName);
                clb.setAutoCommit(autocommit);
                bClbJustCreated = true;
            }

            Proxy proxy = new Proxy();

            //properties
            if (null != props) {
                proxy.setElementProperty(convertPropertiesToElementProperties(
                        props));
            }

            clb.setProxy(proxy);

            if (bClbJustCreated) {
                availabilityService.setConvergedLoadBalancer(clb);
            }
        } catch (Exception ce) {
            System.out.println("exce");
        }
    }

    public boolean deleteConvergedLoadBalancer(String targetName)
        throws MBeanException {
        final Target target = getTarget(targetName);

        //        check1ToN(target);
        final ObjectName availabilityService = getAvailabilityServiceMBean(target);
        invoke0(availabilityService, "removeConvergedLoadBalancer");
        _logger.log(Level.INFO, "getscalled");

        return true;
    }

    public String[] listConvergedLoadBalancers() throws MBeanException {
        try {
            ConfigContext ctx = AdminService.getAdminService().getAdminContext()
                                            .getAdminConfigContext();
            Domain domain = (Domain) ctx.getRootConfigBean();
            Configs configs = domain.getConfigs();
            String[] names = null;

            if (configs != null) {
                _logger.log(Level.INFO, "configs is not null");

                Config[] configArray = configs.getConfig();
                names = new String[configArray.length];

                for (int i = 0, j = 0; i < configArray.length; i++) {
                    ConvergedLoadBalancer clb = configArray[i].getAvailabilityService()
                                                              .getConvergedLoadBalancer();

                    if (clb != null) {
                        names[j++] = clb.getName();
                    }
                }
            }

            return names;
        } catch (ConfigException ce) {
            return null;
        }
    }

    private void addServerToConvergedLbConfig(String configName,
        String serverName) throws MBeanException {
        ConvergedLbConfig clbConfig = getConvergedLbConfig(configName);

        ServerRef sRef = clbConfig.getServerRefByRef(serverName);

        if (sRef != null) {
            // already exists
            return;
        }

        try {
            ConfigContext ctx = AdminService.getAdminService().getAdminContext()
                                            .getAdminConfigContext();

            if (!ServerHelper.isServerStandAlone(ctx, serverName)) {
                String msg = _strMgr.getString("NotStandAloneInstance",
                        serverName);
                throw new MBeanException(new ConfigException(msg));
            }

            sRef = new ServerRef();
            sRef.setRef(serverName);
            clbConfig.addServerRef(sRef);
        } catch (ConfigException ce) {
            throw new MBeanException(ce);
        }
    }

    private void addClusterToConvergedLbConfig(String configName,
        String clusterName, boolean selfloadbalance) throws MBeanException {
        ConvergedLbConfig clbConfig = getConvergedLbConfig(configName);

        ConvergedLbClusterRef clbRef = clbConfig.getConvergedLbClusterRefByRef(clusterName);

        if (clbRef != null) {
            // already exists
            return;
        }

        clbRef = new ConvergedLbClusterRef();
        clbRef.setRef(clusterName);
        clbRef.setSelfLoadbalance(selfloadbalance);

        try {
            clbConfig.addConvergedLbClusterRef(clbRef);
        } catch (ConfigException ce) {
            throw new MBeanException(ce);
        }
    }

    public void createConvergedLbRef(String clbConfigName,
        Boolean selfloadbalance, Boolean lbEnableApplications,
        Boolean lbEnableInstances, String target) throws MBeanException {
        _logger.log(Level.FINE,
            "[LBAdminMbean] createLBRef called for target " + target);

        ConfigContext ctx = AdminService.getAdminService().getAdminContext()
                                        .getAdminConfigContext();

        try {
            // target is a cluster
            if (ClusterHelper.isACluster(ctx, target)) {
                addClusterToConvergedLbConfig(clbConfigName, target,
                    selfloadbalance);
                _logger.log(Level.INFO,
                    _sMgr.getString("http_lb_admin.AddClusterToConfig", target,
                        clbConfigName));

                // target is a server
            } else if (ServerHelper.isAServer(ctx, target)) {
                addServerToConvergedLbConfig(clbConfigName, target);
                _logger.log(Level.INFO,
                    _sMgr.getString("http_lb_admin.AddServerToConfig", target,
                        clbConfigName));
            } else {
                String msg = _strMgr.getString("InvalidTarget", target);
                throw new MBeanException(new RuntimeException(msg));
            }

            ApplicationRef[] appRefs = null;
            ConvergedLbConfig clbConfig = getConvergedLbConfig(clbConfigName);

            if (ClusterHelper.isACluster(ctx, target)) {
                Cluster c = ClusterHelper.getClusterByName(ctx, target);

                if (c == null) {
                    String msg = _strMgr.getString("ClusterNotDefined", target);
                    throw new MBeanException(new ConfigException(msg));
                }

                appRefs = c.getApplicationRef();

                ServerRef[] sRefs = c.getServerRef();

                for (int i = 0; i < sRefs.length; i++)
                    sRefs[i].setLbEnabled(lbEnableInstances);
            } else {
                Server s = ServerHelper.getServerByName(ctx, target);

                if (s == null) {
                    String msg = _strMgr.getString("InstanceNotFound", target);
                    throw new MBeanException(new ConfigException(msg));
                }

                appRefs = s.getApplicationRef();

                ServerRef sRef = clbConfig.getServerRefByRef(target);
                sRef.setLbEnabled(lbEnableInstances);
            }

            if (appRefs != null) {
                for (ApplicationRef ref : appRefs) {
                    //enable only user applications
                    if (!ApplicationHelper.isSystemApp(ctx, ref.getRef())) {
                        ref.setLbEnabled(lbEnableApplications);
                    }
                }
            }
        } catch (ConfigException ce) {
            throw new MBeanException(ce);
        }
    }

    public void deleteConvergedLbRef(String clbConfigName, String target)
        throws MBeanException {
        _logger.log(Level.FINE,
            "[LBAdminMBean] deleteLBRef called for target " + target);

        ConfigContext ctx = AdminService.getAdminService().getAdminContext()
                                        .getAdminConfigContext();

        try {
            // target is a cluster
            if (ClusterHelper.isACluster(ctx, target)) {
                deleteClusterFromConvergedLbConfig(clbConfigName, target);
                _logger.log(Level.INFO,
                    _sMgr.getString("http_lb_admin.DeleteClusterFromConfig",
                        target, clbConfigName));

                // target is a server
            } else if (ServerHelper.isAServer(ctx, target)) {
                deleteServerFromConvergedLbConfig(clbConfigName, target);
                _logger.log(Level.INFO,
                    _sMgr.getString("http_lb_admin.DeleteServerFromConfig",
                        target, clbConfigName));
            } else {
                String msg = _strMgr.getString("InvalidTarget", target);
                throw new MBeanException(new RuntimeException(msg));
            }
        } catch (ConfigException ce) {
            throw new MBeanException(ce);
        }
    }

    private void deleteServerFromConvergedLbConfig(String configName,
        String serverName) throws MBeanException {
        ConvergedLbConfig clbConfig = getConvergedLbConfig(configName);

        ServerRef sRef = clbConfig.getServerRefByRef(serverName);

        if (sRef == null) {
            // does not exist, just return from here
            _logger.log(Level.FINEST,
                " server " + serverName +
                " does not exist in any cluster in the domain");

            String msg = _strMgr.getString("ServerNotDefined", serverName);
            throw new MBeanException(new RuntimeException(msg));
        }

        if (sRef.isLbEnabled()) {
            String msg = _strMgr.getString("ServerNeedsToBeDisabled", serverName);
            throw new MBeanException(new ConfigException(msg));
        }

        // check if its applications are LB disabled.
        Server s = null;

        try {
            ConfigContext ctx = AdminService.getAdminService().getAdminContext()
                                            .getAdminConfigContext();
            s = ServerHelper.getServerByName(ctx, sRef.getRef());
        } catch (ConfigException ce) {
            throw new MBeanException(ce);
        }

        if (s == null) {
            String msg = _strMgr.getString("ServerNotDefined", serverName);
            throw new MBeanException(new ConfigException(msg));
        }

        ApplicationRef[] appRef = s.getApplicationRef();

        if (appRef == null) {
            String msg = _strMgr.getString("AppRefsNotDefined", serverName);
            throw new MBeanException(new ConfigException(msg));
        }

        int i = 0;

        for (i = 0; i < appRef.length; i++) {
            if (appRef[i].isLbEnabled()) {
                break;
            }
        }

        if (i < appRef.length) {
            String msg = _strMgr.getString("AppsNotDisabled");
            throw new MBeanException(new ConfigException(msg));
        }

        clbConfig.removeServerRef(sRef);
    }

    private void deleteClusterFromConvergedLbConfig(String configName,
        String clusterName) throws MBeanException {
        ConvergedLbConfig clbConfig = getConvergedLbConfig(configName);

        ConvergedLbClusterRef clbcRef = clbConfig.getConvergedLbClusterRefByRef(clusterName);

        if (clbcRef == null) {
            // does not exist, just return from here
            return;
        }

        Cluster c = null;

        try {
            ConfigContext ctx = AdminService.getAdminService().getAdminContext()
                                            .getAdminConfigContext();
            c = ClusterHelper.getClusterByName(ctx, clusterName);
        } catch (ConfigException ce) {
            throw new MBeanException(ce);
        }

        if (c == null) {
            String msg = _strMgr.getString("ClusterNotDefined", clusterName);
            throw new MBeanException(new ConfigException(msg));
        }

        ServerRef[] sRefs = c.getServerRef();

        for (int i = 0; i < sRefs.length; i++) {
            if (sRefs[i].isLbEnabled()) {
                String msg = _strMgr.getString("ServerNeedsToBeDisabled",
                        clusterName);
                throw new MBeanException(new ConfigException(msg));
            }
        }

        clbConfig.removeConvergedLbClusterRef(clbcRef);
    }

    public ObjectName getAvailabilityService(String targetName)
        throws MBeanException {
        return getChild("availability-service", null, targetName);
    }

    protected ObjectName getAvailabilityServiceMBean(Target target)
        throws MBeanException {
        final ObjectName configMBean = getConfigMBean(target);
        ObjectName ret = (ObjectName) invoke0(configMBean,
                "getAvailabilityService");

        return ret;
    }
}
