

package gov.va.med.cds.filter;


import gov.va.med.cds.clinicaldata.FilterMetaDataInterface;
import gov.va.med.cds.util.LRUHashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.IOException;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import javax.xml.validation.Schema;


/**
 * This class creates an in memory cache of Filter Schemas for the Filter package. It keeps these schemas in a map. This
 * class is setup to be reentrant code.
 */
public class FilterMemoryCache
    implements
        FilterMemoryCacheInterface
{
    private static final Log LOGGER = LogFactory.getLog( FilterMemoryCache.class );
    private final ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    private final Lock readLock = reentrantReadWriteLock.readLock();
    private final Lock writeLock = reentrantReadWriteLock.writeLock();
    private LRUHashMap<String, FilterMetaDataInterface> filterMemoryCacheMap = null;


    /**
     * A constructor that takes the map which is keyed by filterId stores it into a local map. The map contains the
     * associated Filter Schema Metadata.
     * 
     * @param aMap
     */
    public FilterMemoryCache( LRUHashMap<String, FilterMetaDataInterface> aMap )
    {
        filterMemoryCacheMap = aMap;
    }


    /**
     * Loads the map with the given pair of filterId and filterMetadata.
     * 
     * @param filterId
     * @param filterMetaData
     * @throws IOException
     */
    public void loadFilterMetaDataIntoMemoryCache( String filterId, FilterMetaDataInterface filterMetaData )
    {

        if ( LOGGER.isDebugEnabled() )
        {
            LOGGER.debug( "loading filterId " + filterId );
        }

        if ( !filterMemoryCacheMap.containsKey( filterId ) )
        {
        	writeLock.lock();
            try
            {
            	if ( !filterMemoryCacheMap.containsKey( filterId ) )
            	{
            		filterMemoryCacheMap.put( filterId, filterMetaData );
            	}
            }
            finally
            {
                writeLock.unlock();
            }
        }
        else
        {
            if ( LOGGER.isWarnEnabled() )
            {
                LOGGER.warn( "The filterId='" + filterId + "' already exists. The filter registry will not add another one." );
            }
        }
    }


    /**
     * Gets the schema string for the filter from the filterMetaData.
     * 
     */
    public String getFilterString( String filterId )
    {
        FilterMetaDataInterface filerMetaData = filterMemoryCacheMap.get( filterId );
        if ( null != filerMetaData )
        {
            return filerMetaData.getFilterSchemaXml();
        }
        return null;
    }


    /**
     * Gets the schema object for the filter from the filterMetaData.
     * 
     */
    public Schema getFilterSchema( String filterId )
    {
        FilterMetaDataInterface filerMetaData = filterMemoryCacheMap.get( filterId );
        if ( null != filerMetaData )
        {
            return filerMetaData.getFilterSchema();
        }
        return null;
    }


    /**
     * @return true if the Filter Memory Cache map is empty.
     */
    public boolean isEmpty( )
    {
        boolean isEmpty = true;

        try
        {
            readLock.lock();
            isEmpty = filterMemoryCacheMap.isEmpty();
        }
        finally
        {
            readLock.unlock();
        }

        return isEmpty;
    }


    /**
     * Clears the Filter Memory Cache map.
     */
    public void clear( )
    {
        try
        {
            writeLock.lock();
            filterMemoryCacheMap.clear();
        }
        finally
        {
            writeLock.unlock();
        }
    }


    /**
     * Gets the no. of schemas contained in Filter Memory Cache map.
     */
    public int getNumberOfFilterSchemasLoadedIntoFilterCache( )
    {
        return filterMemoryCacheMap.size();
    }


    /**
     * Gets the size of Filter Memory Cache threshold.
     */
    public int getFilterCacheThreshold( )
    {
        return filterMemoryCacheMap.getMapThreshold();
    }


    /**
     * Resizes the Filter Memory Cache map to the given size.
     */
    public void reSize( int newSize )
    {
        try
        {
            writeLock.lock();
            filterMemoryCacheMap.reSize( newSize );
        }
        finally
        {
            writeLock.unlock();
        }
    }


    /**
     * gets available list of filterIds in filter cache
     */
    public Set<String> getFilterCacheFilterIds( )
    {
        return filterMemoryCacheMap.keySet();
    }
}
