

package gov.va.med.cds.template;


import gov.va.med.cds.clinicaldata.TemplateMetaDataInterface;
import gov.va.med.cds.exception.ErrorCodeEnum;
import gov.va.med.cds.exception.InitializationException;
import gov.va.med.cds.junit.runners.Suite;
import gov.va.med.cds.junit.runners.SuiteAwareRunner;
import gov.va.med.cds.util.LRUHashMap;
import gov.va.med.cds.xml.schema.SchemaException;
import gov.va.med.cds.xml.schema.SchemaHelper;
import gov.va.med.cds.xml.schema.SchemaHelperInterface;

import java.lang.reflect.Field;
import java.sql.Blob;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.concurrent.locks.Lock;

import org.easymock.EasyMock;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;


@RunWith( SuiteAwareRunner.class )
public class TemplateCacheTest
{
    private TemplateCache templateCache = null;
    private LRUHashMap<String, TemplateCacheModelInterface> templateCacheModelMapMock = EasyMock.createMock( LRUHashMap.class );;
    private Lock readLockMock = EasyMock.createMock( Lock.class );
    private Lock writeLockMock = EasyMock.createMock( Lock.class );


    @Before
    @Suite( groups = { "checkintest" } )
    public void beforeTemplateCacheTestMethodSetUp( )
        throws Exception
    {
        EasyMock.reset( templateCacheModelMapMock, readLockMock, writeLockMock );
        templateCache = new TemplateCache( templateCacheModelMapMock );
        setTemplateCacheLocksToMocks( readLockMock, writeLockMock );
    }


    @Test
    @Suite( groups = { "checkintest" } )
    public void testClear( )
    {
        writeLockMock.lock();
        templateCacheModelMapMock.clear();
        writeLockMock.unlock();
        EasyMock.replay( templateCacheModelMapMock, readLockMock, writeLockMock );
        templateCache.clear();
        EasyMock.verify( templateCacheModelMapMock, readLockMock, writeLockMock );
    }


    @Test
    @Suite( groups = { "checkintest" } )
    public void testGetAllTemplateCacheModels( )
    {
        readLockMock.lock();
        EasyMock.expect( templateCacheModelMapMock.values() ).andReturn( new ArrayList<TemplateCacheModelInterface>() );
        readLockMock.unlock();
        EasyMock.replay( templateCacheModelMapMock, readLockMock, writeLockMock );
        Assert.assertNotNull( templateCache.getAllTemplateCacheModels() );
        EasyMock.verify( templateCacheModelMapMock, readLockMock, writeLockMock );
    }


    @Test
    @Suite( groups = { "checkintest" } )
    public void testGetNumberOfTemplatesLoadedIntoTemplateCache( )
    {
        EasyMock.expect( templateCacheModelMapMock.size() ).andReturn( 5 );
        EasyMock.replay( templateCacheModelMapMock, readLockMock, writeLockMock );
        Assert.assertEquals( 5, templateCache.getNumberOfTemplatesLoadedIntoTemplateCache() );
        EasyMock.verify( templateCacheModelMapMock, readLockMock, writeLockMock );
    }


    @Test
    @Suite( groups = { "checkintest" } )
    public void testGetSchemaHelper( )
    {

        TemplateCacheModelInterface templateCacheModelDataMock = EasyMock.createMock( TemplateCacheModelInterface.class );
        SchemaHelperInterface schemaHelperMock = EasyMock.createMock( SchemaHelperInterface.class );

        readLockMock.lock();
        EasyMock.expect( templateCacheModelMapMock.get( "Template Id" ) ).andReturn( templateCacheModelDataMock );
        readLockMock.unlock();
        EasyMock.expect( templateCacheModelDataMock.getSchemaHelper() ).andReturn( schemaHelperMock );

        EasyMock.replay( templateCacheModelMapMock, readLockMock, writeLockMock, templateCacheModelDataMock );
        Assert.assertEquals( schemaHelperMock, templateCache.getSchemaHelper( "Template Id" ) );
        EasyMock.verify( templateCacheModelMapMock, readLockMock, writeLockMock, templateCacheModelDataMock );

    }


    @Test
    @Suite( groups = { "checkintest" } )
    public void testGetTemplateMetaData( )
    {
        TemplateCacheModelInterface templateCacheModelDataMock = EasyMock.createMock( TemplateCacheModelInterface.class );
        TemplateMetaDataInterface templateMetaData = EasyMock.createMock( TemplateMetaDataInterface.class );

        readLockMock.lock();
        EasyMock.expect( templateCacheModelMapMock.get( "Template Id" ) ).andReturn( templateCacheModelDataMock );
        readLockMock.unlock();
        EasyMock.expect( templateCacheModelDataMock.getTemplateMetaData() ).andReturn( templateMetaData );

        EasyMock.replay( templateCacheModelMapMock, readLockMock, writeLockMock, templateCacheModelDataMock );
        Assert.assertEquals( templateMetaData, templateCache.getTemplateMetaData( "Template Id" ) );
        EasyMock.verify( templateCacheModelMapMock, readLockMock, writeLockMock, templateCacheModelDataMock );
    }


    @Test
    @Suite( groups = { "checkintest" } )
    public void testIsEmpty( )
    {

        readLockMock.lock();
        EasyMock.expect( templateCacheModelMapMock.isEmpty() ).andReturn( true );
        readLockMock.unlock();

        EasyMock.replay( templateCacheModelMapMock, readLockMock, writeLockMock );
        Assert.assertTrue( templateCache.isEmpty() );
        EasyMock.verify( templateCacheModelMapMock, readLockMock, writeLockMock );
    }


    @Test
    @Suite( groups = { "checkintest" } )
    public void testLoadSchemaIntoCache( )
        throws Exception
    {
        TemplateMetaDataInterface templateMetaDataMock = EasyMock.createMock( TemplateMetaDataInterface.class );
        final SchemaHelper schemaHelperMock = EasyMock.createMock( SchemaHelper.class );

        templateCache = new TemplateCache( templateCacheModelMapMock )
        {
            @Override
            SchemaHelper getSchemaHelper( TemplateMetaDataInterface templateMetaData, Blob schemaJar )
            {
                return schemaHelperMock;
            }
        };

        EasyMock.expect( templateMetaDataMock.getTemplateJar() ).andReturn( new byte[1] );
        EasyMock.expect( templateMetaDataMock.getTemplateId() ).andReturn( "12345" );
        EasyMock.expect( templateCacheModelMapMock.containsKey( "12345" ) ).andReturn( false ).times( 2 );
        EasyMock.expect( templateCacheModelMapMock.put( EasyMock.eq( "12345" ), EasyMock.isA( TemplateCacheModelData.class ) ) ).andReturn( null );

        EasyMock.replay( templateCacheModelMapMock, templateMetaDataMock );
        templateCache.loadSchemaIntoCache( templateMetaDataMock );
        EasyMock.verify( templateCacheModelMapMock, templateMetaDataMock );
    }


    @Test( expected = InitializationException.class )
    @Suite( groups = { "checkintest" } )
    public void testLoadSchemaIntoCache_ThrowsExceptioin( )
        throws Exception
    {
        TemplateMetaDataInterface templateMetaDataMock = EasyMock.createMock( TemplateMetaDataInterface.class );
        templateCache = new TemplateCache( templateCacheModelMapMock )
        {
            @Override
            SchemaHelper getSchemaHelper( TemplateMetaDataInterface templateMetaData, Blob schemaJar )
            {
                throw new SchemaException( ErrorCodeEnum.SCHEMA_XML_NULL );
            }
        };

        EasyMock.expect( templateMetaDataMock.getTemplateJar() ).andReturn( new byte[1] );
        EasyMock.expect( templateMetaDataMock.getTemplateName() ).andReturn( "Template Name" );
        EasyMock.replay( templateCacheModelMapMock, templateMetaDataMock );
        templateCache.loadSchemaIntoCache( templateMetaDataMock );
        EasyMock.verify( templateCacheModelMapMock, templateMetaDataMock );
    }


    @Test
    @Suite( groups = { "checkintest" } )
    public void testReSize( )
    {
        writeLockMock.lock();
        templateCacheModelMapMock.reSize( 5 );
        writeLockMock.unlock();
        EasyMock.replay( templateCacheModelMapMock, readLockMock, writeLockMock );
        templateCache.reSize( 5 );
        EasyMock.verify( templateCacheModelMapMock, readLockMock, writeLockMock );

    }


    @Test
    @Suite( groups = { "checkintest" } )
    public void testGetTemplateCacheThreshold( )
    {
        EasyMock.expect( templateCacheModelMapMock.getMapThreshold() ).andReturn( 5 );
        EasyMock.replay( templateCacheModelMapMock, readLockMock, writeLockMock );
        Assert.assertEquals( 5, templateCache.getTemplateCacheThreshold() );
        EasyMock.verify( templateCacheModelMapMock, readLockMock, writeLockMock );

    }


    @Test
    @Suite( groups = { "checkintest" } )
    public void testGetTemplateCacheTemplateIds( )
    {
        EasyMock.expect( templateCacheModelMapMock.keySet() ).andReturn( new HashSet<String>() );
        EasyMock.replay( templateCacheModelMapMock, readLockMock, writeLockMock );
        Assert.assertNotNull( templateCache.getTemplateCacheTemplateIds() );
        EasyMock.verify( templateCacheModelMapMock, readLockMock, writeLockMock );
    }


    /**
     *  Use reflection to set TemplateCache private fields to mocks
     */
    private void setTemplateCacheLocksToMocks( Lock readLock, Lock writeLock )
        throws Exception
    {
        Class<? extends TemplateCache> obj = templateCache.getClass();
        Field field = obj.getDeclaredField( "readLock" );

        field.setAccessible( true );
        field.set( templateCache, readLock );

        field = obj.getDeclaredField( "writeLock" );
        field.setAccessible( true );
        field.set( templateCache, writeLock );
    }

}
