/*
 * Copyright 2013 Red Hat, Inc.
 *
 * Red Hat 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.jbosson.plugins.fuse.utils;

import java.io.IOException;
import java.io.StringReader;
import java.util.Properties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.zookeeper.KeeperException;
import org.jbosson.plugins.fuse.JBossFuseContainerTraits;
import org.jbosson.plugins.fuse.JBossFuseProfileGroupManager;
import org.linkedin.zookeeper.client.IZKClient;

/**
 * Implements {@link DataStore} using LinkedIn Zookeeper library.
 *
 * @author dbokde
 */
public class ZooKeeperDataStoreImpl implements DataStore {

    private static final Log LOG = LogFactory.getLog(ZooKeeperDataStoreImpl.class);

    private final IZKClient client;
    private final String containerVersion;

    public ZooKeeperDataStoreImpl(Object client, String containerVersion) {
        this.client = (IZKClient)client;
        this.containerVersion = containerVersion;
    }

    public void getFabricMetadata(String profileName, JBossFuseContainerTraits traits) throws Exception {

        // look for MQ cluster info in non ensemble profiles
        if (!JBossFuseProfileGroupManager.ENSEMBLE_PROFILE_PATTERN.matcher(profileName).matches()) {
            // MQ broker cluster name for MQ Cluster Groups
            String brokerClusterName = null;

            try {


                String brokerPropertiesPath = "/fabric/configs/versions/" + containerVersion +
                    "/profiles/" + profileName + "/org.fusesource.mq.fabric.server-" +
                    profileName + ".properties";
                boolean brokerFound;
                if (null == client.exists(brokerPropertiesPath)) {
                    // try the default properties name
                    brokerPropertiesPath = "/fabric/configs/versions/" + containerVersion +
                        "/profiles/" + profileName + "/org.fusesource.mq.fabric.server-broker.properties";
                    brokerFound = (null != client.exists(brokerPropertiesPath));
                } else {
                    brokerFound = true;
                }

                if (brokerFound) {
                    LOG.debug("Broker properties found in profile " + profileName);
                    Properties brokerProperties = new Properties();
                    String propertiesData = client.getStringData(brokerPropertiesPath);
                    brokerProperties.load(new StringReader(propertiesData));
                    brokerClusterName = brokerProperties.getProperty(JBossFuseProfileGroupManager.MQ_GROUP_PROPERTY);
                    LOG.debug("Found Broker Cluster " + brokerClusterName);
                } else {
                    LOG.debug("Broker properties NOT found in profile " + profileName);
                }

            } catch (InterruptedException e) {
                throw new IllegalArgumentException(
                    "Error looking up MQ broker properties: " + e.getMessage(), e);
            } catch (KeeperException e) {
                throw new IllegalArgumentException(
                    "Error looking up MQ broker properties: " + e.getMessage(), e);
            } catch (IOException e) {
                throw new IllegalArgumentException(
                    "Error looking up MQ broker properties: " + e.getMessage(), e);
            }

            // add the MQ Cluster to traits
            if (brokerClusterName != null) {
                traits.addCluster(brokerClusterName);
            }
        }

        // now do this all over for parent profiles
        // get parent profiles from ZK
        try {

            String parentList = getParents(profileName);
            if (parentList != null) {
                String[] parents = parentList.split(" ");
                // add parents to trait, returns true if this is a new child
                if (traits.setParentProfiles(profileName, parents)) {
                    // recursively process parents of new child
                    for (String parent : parents) {
                        // make sure the parent has not been processed already
                        if (!traits.hasChildProfile(parent)) {
                            getFabricMetadata(parent, traits);
                        }
                    }
                }
            }

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } catch (KeeperException e) {
            throw new IllegalArgumentException(
                "Error getting Parent profiles for " + profileName + ": " + e.getMessage(), e);
        }
    }

    public void close() throws Exception {
        // do nothing
    }

    // handle old and new style parent lists
    // TODO refactor this to use the Fabric Profile API instead of raw ZKClient
    private String getParents(String profileName) throws InterruptedException, KeeperException {
        String value = null;
        if (JBossFuseProfileGroupManager.ENSEMBLE_PROFILE_PATTERN.matcher(profileName).matches()) {
            // check whether this is an un-versioned ensemble profile
            final String ensemblePath = "/fabric/configs/ensemble/profiles/" + profileName;
            if (null != client.exists(ensemblePath)) {
                value = client.getStringData(ensemblePath);
            }
        } else {
            final String profilePath = "/fabric/configs/versions/" + containerVersion + "/profiles/" + profileName;
            if (null != client.exists(profilePath)) {
                value = client.getStringData(profilePath);
            }
        }

        final Properties props = new Properties();
        if (value != null) {
            try {
                props.load(new StringReader(value));
            } catch (IOException ignore) {}
        }

        // handle old style parent list
        // For compatibility, check if we have instead the list of parents
        if (props.size() == 1) {
            String key = props.stringPropertyNames().iterator().next();
            if (!key.equals(JBossFuseProfileGroupManager.PARENTS_PROPERTY)) {
                String val = props.getProperty(key);
                props.remove(key);
                props.setProperty(JBossFuseProfileGroupManager.PARENTS_PROPERTY, val.isEmpty() ? key : key + " " + val);
            }
        }

        return props.getProperty(JBossFuseProfileGroupManager.PARENTS_PROPERTY);
    }

}
