/*
 * Licensed to Jasig under one or more contributor license
 * agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Jasig 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 the following location:
 *
 *   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.jasig.cas.monitor;

import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.validation.constraints.NotNull;

import net.spy.memcached.MemcachedClient;

/**
 * Monitors the memcached hosts known to an instance of {@link net.spy.memcached.MemcachedClient}.
 *
 * @author Marvin S. Addison
 * @since 3.5.1
 */
public class MemcachedMonitor extends AbstractCacheMonitor {

    @NotNull
    private final MemcachedClient memcachedClient;


    /**
     * Creates a new monitor that observes the given memcached client.
     *
     * @param client Memcached client.
     */
    public MemcachedMonitor(final MemcachedClient client) {
        this.memcachedClient = client;
    }


    /**
     * Supersede the default cache status algorithm by considering unavailable memcached nodes above cache statistics.
     * If all nodes are unavailable, raise an error; if one or more nodes are unavailable, raise a warning; otherwise
     * delegate to examination of cache statistics.
     *
     * @return Cache status descriptor.
     */
    public CacheStatus observe() {
        if (memcachedClient.getAvailableServers().size() == 0) {
            return new CacheStatus(StatusCode.ERROR, "No memcached servers available.");
        }
        final Collection<SocketAddress> unavailableList = memcachedClient.getUnavailableServers();
        final CacheStatus status;
        if (unavailableList.size() > 0) {
            final String description = "One or more memcached servers is unavailable: " + unavailableList;
            status = new CacheStatus(StatusCode.WARN, description, getStatistics());
        } else {
            status = super.observe();
        }
        return status;
    }


    /**
     * Get cache statistics for all memcached hosts known to {@link MemcachedClient}.
     *
     * @return Statistics for all available hosts.
     */
    protected CacheStatistics[] getStatistics() {
        long evictions;
        long size;
        long capacity;
        String name;
        Map<String, String> statsMap;
        final Map<SocketAddress, Map<String, String>> allStats = memcachedClient.getStats();
        final List<CacheStatistics> statsList = new ArrayList<CacheStatistics>();
        for (final SocketAddress address : allStats.keySet()) {
            statsMap = allStats.get(address);
            if (statsMap.size() > 0) {
                size = Long.parseLong(statsMap.get("bytes"));
                capacity = Long.parseLong(statsMap.get("limit_maxbytes"));
                evictions = Long.parseLong(statsMap.get("evictions"));
                if (address instanceof InetSocketAddress) {
                    name = ((InetSocketAddress) address).getHostName();
                } else {
                    name = address.toString();
                }
                statsList.add(new SimpleCacheStatistics(size, capacity, evictions, name));
            }
        }
        return statsList.toArray(new CacheStatistics[statsList.size()]);
    }
}
