/*
 * JBoss, Home of Professional Open Source
 * Copyright 2007, Red Hat Middleware LLC, and individual contributors
 * as indicated by the @author tags.
 * See the copyright.txt in the distribution for a full listing
 * of individual contributors.
 * This copyrighted material is made available to anyone wishing to use,
 * modify, copy, or redistribute it subject to the terms and conditions
 * of the GNU General Public License, v. 2.0.
 * This program is distributed in the hope that it will be useful, but WITHOUT A
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License,
 * v. 2.0 along with this distribution; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA  02110-1301, USA.
 *
 * (C) 2007,
 * @author JBoss Inc.
 */
package org.jboss.txbridge.ba.service;

import org.apache.log4j.Logger;
import org.jboss.txbridge.ba.id.IdentifierFactory;
import org.jboss.txbridge.ba.id.UidIdentifierFactory;

import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentHashMap;

/**
 * This class is responsible for caching information about service descriptions. It associates
 * its description with a unique identifier of a service.
 *
 * @author Maciej P. Machulak (mmachulak@redhat.com)
 * @version 0.1
 */
public class ServiceInformationManager
{
    // Logger
    private static Logger log = Logger.getLogger(ServiceInformationManager.class);

    // Service information manager instance
    private volatile static ServiceInformationManager cim = null;

    // Identifier factory
    private IdentifierFactory idf = null;

    // Mappings
    // - (class name + method name, service id)
    private ConcurrentMap<String,String> methodServiceIdMapping;
    // - (service id, service_description)
    private ConcurrentMap<String,ServiceDescription> serviceDescriptions;

    // Constructor
    private ServiceInformationManager()
    {
        log.info("constructor()");
        idf = new UidIdentifierFactory();
        methodServiceIdMapping = new ConcurrentHashMap<String,String>();
        serviceDescriptions = new ConcurrentHashMap<String,ServiceDescription>();
    }

    // Get instance of the class information manager
    public static ServiceInformationManager getSingletonInstance()
    {
        log.info("getSingletonInstance()");
        if (cim == null)
        {
            synchronized(ServiceInformationManager.class)
            {
                if (cim == null)
                {
                    log.info("Creating new instance");
                    cim = new ServiceInformationManager();
                }
            }
        }
        return cim;
    }

    /**
     * This method stores a service description. It returns a unique associates service ID.
     *
     * @param serviceDescription is the service description that needs to be stored.
     * @return is the unique service ID.
     */
    public String storeServiceDescription(ServiceDescription serviceDescription)
    {
        log.info("storeServiceDescription()");
        String serviceId = idf.getIdentifier();
        log.info("service id: " + serviceId);
        String className = serviceDescription.getOriginalClass().getName();
        String methodName = serviceDescription.getOriginalMethodName();
        storeServiceDescription(serviceId,className+methodName,serviceDescription);
        return serviceId;
    }

    /**
     * This method stores a service description for a service with a given complete name and
     * returns a unique associated service ID.
     *
     * @param serviceId is the unique service Id.
     * @param completeMethodName is the complete method's name.
     * @param serviceDescription is the service description.
     */
    private void storeServiceDescription(String serviceId, String completeMethodName,ServiceDescription serviceDescription)
    {
        log.info("storeServiceDescription()");
        methodServiceIdMapping.putIfAbsent(completeMethodName,serviceId);
        serviceDescriptions.putIfAbsent(serviceId,serviceDescription);
    }

    /**
     * This method returns the service description for a service with a given identifier.
     *
     * @param completeMethodName is the complete method name (classname+methodname)
     * @return service description.
     */
    public ServiceDescription getServiceByName(String completeMethodName)
    {
        log.info("getServiceByName()");
        String serviceId = methodServiceIdMapping.get(completeMethodName);
        if (serviceId == null)
        {
            return null;
        }
        return serviceDescriptions.get(serviceId);
    }

    /**
     * This method returns the service description for a service with a given identifier.
     *
     * @param serviceId is the ID of the service.
     * @return service description.
     */
    public ServiceDescription getServiceById(String serviceId)
    {
        log.info("getServiceById");
        return serviceDescriptions.get(serviceId);
    }

    /**
     * This method returns the ID for the service with a given class name and given method name.
     *
     * @param completeMethodName is the complete method name (classname+methodname)
     * @return the service ID.
     */
    public String getServiceId(String completeMethodName)
    {
        log.info("getServiceId()");
        return methodServiceIdMapping.get(completeMethodName);
    }

    /**
     * This method checks if the service information manager already knows of a service
     * with a given identifier.
     *
     * @param serviceId is the service identifier.
     * @return true if the service is know, false if not.
     */
    public boolean knowsAboutById(String serviceId)
    {
        log.info("knowsAboutById()");
        return serviceDescriptions.containsKey(serviceId);
    }

    /**
     * This method checks if the service information manager already knows of a service
     * with a given identifier.
     *
     * @param serviceName is the full name of the service (class name + method name)
     * @return true if the service is know, false if not.
     */
    public boolean knowsAboutByName(String serviceName)
    {
        log.info("knowsAboutByName()");
        return methodServiceIdMapping.containsKey(serviceName);
    }

}
