/*
 * Copyright 2016 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.util.concurrent.TimeUnit;

import io.fabric8.utils.PasswordEncoder;
import io.fabric8.zookeeper.curator.DynamicEnsembleProvider;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.state.ConnectionState;
import org.apache.curator.framework.state.ConnectionStateListener;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.jbosson.plugins.fuse.JBossFuseProfileGroupManager;

/**
 * Implements {@link FabricRegistry} using Apache Curator library.
 * @author dbokde
 */
public class CuratorFabricRegistryImpl implements FabricRegistry {

    private static final Log LOG = LogFactory.getLog(CuratorFabricRegistryImpl.class);
    private static final int BASE_SLEEP_TIME_MS = 5000;
    private static final int MAX_RETRIES = 3;
    private static final String UTF_8_CHARSET_NAME = "UTF-8";

    private final String url;
    private final String password;
    private final long timeout;
    private final String containerName;

    private boolean connected;
    private CuratorFramework framework;

    public static boolean isSupported() {
        try {
            Class.forName("org.apache.curator.framework.CuratorFramework");
            return true;
        } catch (ClassNotFoundException e) {
            return false;
        }
    }

    public CuratorFabricRegistryImpl(String zookeeperUrl, String zookeeperPassword, long timeoutSeconds, String containerName) {
        this.url = zookeeperUrl;
        if (zookeeperPassword.startsWith(PasswordEncoder.PREFIX)) {
            this.password = PasswordEncoder.decode(zookeeperPassword);
        } else {
            this.password = zookeeperPassword;
        }
        this.timeout = timeoutSeconds;
        this.containerName = containerName;
    }

    public void connect() throws Exception {
        final DynamicEnsembleProvider ensembleProvider = new DynamicEnsembleProvider();
        ensembleProvider.update(url);

        CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder()
                .ensembleProvider(ensembleProvider)
                .retryPolicy(new ExponentialBackoffRetry(BASE_SLEEP_TIME_MS, MAX_RETRIES))
                .connectionTimeoutMs((int) TimeUnit.SECONDS.toMillis(timeout));
        if (password != null) {
            builder = builder.authorization("digest", ("fabric:" + password).getBytes(UTF_8_CHARSET_NAME));
        }

        framework = builder.build();
        framework.getConnectionStateListenable().addListener(new ConnectionStateListener() {
            public void stateChanged(CuratorFramework curatorFramework, ConnectionState connectionState) {
                // just log the events for now
                if (connected && (connectionState == ConnectionState.SUSPENDED || connectionState == ConnectionState.LOST)) {
                    LOG.warn("Connection state changed to " + connectionState);
                }
            }
        });
        framework.start();

        connected = true;
    }

    public void disconnect() throws Exception {
        connected = false;
        if (framework != null) {
            framework.close();
        }
    }

    public String getContainerVersion() throws Exception {
        final String path = String.format(JBossFuseProfileGroupManager.CONTAINER_VERSION_PATH_FORMAT, containerName);
        return new String(framework.getData().forPath(path), UTF_8_CHARSET_NAME);
    }

    public String[] getContainerProfiles(String containerVersion) throws Exception {
        final String path = String.format(JBossFuseProfileGroupManager.CONTAINER_PROFILES_PATH_FORMAT,
                containerVersion, containerName);
        return (new String(framework.getData().forPath(path), UTF_8_CHARSET_NAME)).split(" ");
    }

    public void writeTextNode(String key, String value) throws Exception {
        framework.create().forPath(key, value.getBytes(UTF_8_CHARSET_NAME));
    }

    public String getTextNode(String key) throws Exception {
        if( framework.checkExists().forPath(key) == null )
            return null;
        else
            return framework.getData().forPath(key).toString();
    }
}
