/**
 *  Copyright 2005-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 io.fabric8.core.jmx;

import static io.fabric8.internal.Objects.notNull;
import static io.fabric8.zookeeper.utils.ZooKeeperUtils.exists;
import static io.fabric8.zookeeper.utils.ZooKeeperUtils.getChildren;
import static io.fabric8.zookeeper.utils.ZooKeeperUtils.getStringData;
import io.fabric8.api.jmx.ZkContents;
import io.fabric8.api.jmx.ZooKeeperFacadeMXBean;
import io.fabric8.common.util.ShutdownTracker;
import io.fabric8.service.FabricServiceImpl;
import io.fabric8.zookeeper.utils.ZooKeeperUtils;

import java.util.List;

import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.StandardMBean;

import org.apache.curator.framework.CuratorFramework;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 */
public class ZooKeeperFacade implements ZooKeeperFacadeMXBean {
    private static final transient Logger LOG = LoggerFactory.getLogger(ZooKeeperFacade.class);

    private final FabricServiceImpl fabricService;
    private ObjectName objectName;

    public ZooKeeperFacade(FabricServiceImpl fabricService) {
        this.fabricService = fabricService;
    }

    public ObjectName getObjectName() throws MalformedObjectNameException {
        if (objectName == null) {
            // TODO to avoid mbean clashes if ever a JVM had multiple FabricService instances, we may
            // want to add a parameter of the fabric ID here...
            objectName = new ObjectName("io.fabric8:type=ZooKeeper");
        }
        return objectName;
    }

    public void setObjectName(ObjectName objectName) {
        this.objectName = objectName;
    }

    public void registerMBeanServer(ShutdownTracker shutdownTracker, MBeanServer mbeanServer) {
        try {
            ObjectName name = getObjectName();
			if (!mbeanServer.isRegistered(name)) {
	            StandardMBean mbean = new StandardMBean(this, ZooKeeperFacadeMXBean.class);
				mbeanServer.registerMBean(mbean, name);
			}
		} catch (Exception e) {
            LOG.warn("An error occurred during mbean server registration: " + e, e);
        }
    }

    public void unregisterMBeanServer(MBeanServer mbeanServer) {
        if (mbeanServer != null) {
            try {
                ObjectName name = getObjectName();
				if (mbeanServer.isRegistered(name)) {
					mbeanServer.unregisterMBean(name);
				}
			} catch (Exception e) {
                LOG.warn("An error occurred during mbean server registration: " + e, e);
            }
        }
    }

    protected FabricServiceImpl getFabricService() {
        return fabricService;
    }

    /**
     * Returns the ZooKeeper client or throwns an exception if its not configured properly
     */
    protected CuratorFramework getCurator() {
        CuratorFramework curator = fabricService.adapt(CuratorFramework.class);
        notNull(curator, "curator");
        return curator;
    }

    // Facade API
    //-------------------------------------------------------------------------


    public ZkContents read(String path) throws Exception {
        return read(path, true);
    }

    public ZkContents read(String path, boolean escape) throws Exception {
        CuratorFramework curator = getCurator();
        Stat exists = exists(curator, path);
        if (exists == null) {
            return null;
        }
        int numChildren = exists.getNumChildren();
        int dataLength = exists.getDataLength();
        List<String> children = null;
        String data = null;
        if (numChildren > 0) {
            children = getChildren(curator, path);
        } else {
            data = getContents(path, escape);
        }
        return new ZkContents(dataLength, children, data);
    }

    @Override
    public String getContents(String path) throws Exception {
        return getContents(path, true);
    }

    public String getContents(String path, boolean escape) throws Exception {
        CuratorFramework curator = getCurator();
        String answer = getStringData(curator, path);
        if (answer != null) {
            return ZooKeeperUtils.getSubstitutedData(curator, answer);
        }
        return answer;
    }
}
