

package gov.va.med.cds.template;


import gov.va.med.cds.clinicaldata.Operation;
import gov.va.med.cds.clinicaldata.TemplateMetaData;
import gov.va.med.cds.clinicaldata.TemplateMetaDataInterface;
import gov.va.med.cds.exception.ErrorCodeEnum;
import gov.va.med.cds.tfs.util.TemplateMetaDataHelper;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;


public class TemplateServiceSimulator
    implements
        TemplateServiceInterface
{
    private Map<String, Properties> templateMetaDataMap = new HashMap<String, Properties>();


    private File getTemplateJarFile( String aTemplateId )
    {
        File file = null;
        Properties properties = templateMetaDataMap.get( aTemplateId );
        if ( null != properties )
        {
            String filePath = properties.getProperty( "file" );
            if ( ( null != filePath ) && ( 0 < filePath.length() ) )
            {
                filePath = filePath.replace( '\\', File.separatorChar );
                file = new File( filePath );
            }
        }

        return file;
    }


    // Returns the contents of the file in a byte array.
    private byte[] getBytesFromFile( File file )
    {
        // Create the byte array to hold the data
        byte[] bytes;
        InputStream is = null;

        try
        {
            is = new FileInputStream( file );

            // Get the size of the file
            long length = file.length();

            // You cannot create an array using a long type.
            // It needs to be an int type.
            // Before converting to an int type, check
            // to ensure that file is not larger than Integer.MAX_VALUE.
            if ( length > Integer.MAX_VALUE )
            {
                // File is too large
                throw new IOException( "File to big to convert: " + file.getName() );
            }

            bytes = new byte[( int )length];

            // Read in the bytes
            int offset = 0;
            int numRead = 0;
            while ( offset < bytes.length && ( numRead = is.read( bytes, offset, bytes.length - offset ) ) >= 0 )
            {
                offset += numRead;
            }

            // Ensure all the bytes have been read in
            if ( offset < bytes.length )
            {
                throw new IOException( "Could not completely read file: " + file.getName() );
            }

            // Close the input stream and return bytes
            is.close();
        }
        catch ( IOException e )
        {
            throw new TemplateServiceException( ErrorCodeEnum.CANNOT_LOAD_TEMPLATE_METADATA, e,
                            "Couldn't create a File from the template file path: " + file.getAbsolutePath(), e.getMessage() );
        }
        finally
        {
            if ( is != null )
            {
                try
                {
                    is.close();
                }
                catch ( IOException e )
                {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                finally
                {
                    is = null;
                }
            }
        }

        return bytes;
    }


    public Collection<String> getActiveTemplateIds( String aVhimVersion )
    {
        Collection<String> templateIds = new ArrayList<String>();
        for ( Map.Entry<String, Properties> entry : templateMetaDataMap.entrySet() )
        {
            if ( entry.getValue().getProperty( "vhimVersion" ).equals( aVhimVersion ) )
            {
                String templateId = entry.getKey();
                templateIds.add( templateId );
            }
        }
        return templateIds;
    }


    public TemplateMetaDataInterface getTemplateMetaData( String aTemplateId )
    {
        TemplateMetaDataInterface metaData = null;
        Properties metaProps = templateMetaDataMap.get( aTemplateId );
        if ( null != metaProps )
        {
            metaData = new TemplateMetaData();
            metaData.setTemplateId( metaProps.getProperty( "id" ) );
            String[] vhimVersions = TemplateMetaDataHelper.buildVhimVersions( metaProps.getProperty( "vhimVersion" ) );
            metaData.setVhimVersions( vhimVersions );
            metaData.setDomainEntryPoints( TemplateMetaDataHelper.buildEntryPoints( metaProps.getProperty( "entryPoint" ) ) );
            metaData.setTemplateName( metaProps.getProperty( "name" ) );
            metaData.setOperation( Operation.valueOf( metaProps.getProperty( "requestType" ) ) );
            metaData.setTemplateDescription( metaProps.getProperty( "description" ) );
            metaData.setTestOnly( metaProps.getProperty( "testOnly", "false" ) );
            metaData.setStatus( "ACTIVE" );

            File jarFile = getTemplateJarFile( aTemplateId );
            metaData.setTemplateJar( getBytesFromFile( jarFile ) );
        }

        return metaData;
    }


    public Collection<String> getVhimVersions( )
    {
        Collection<String> vhimVersions = new ArrayList<String>();
        for ( Map.Entry<String, Properties> entry : templateMetaDataMap.entrySet() )
        {
            Properties props = entry.getValue();
            String vhimVersion = props.getProperty( "vhimVersion" );
            if ( ( null != vhimVersion ) && ( 0 < vhimVersion.length() ) && ( !vhimVersions.contains( vhimVersion ) ) )
            {
                vhimVersions.add( vhimVersion );
            }
        }

        return vhimVersions;
    }


    public void initialize( )
    {
    }


    /**
     * This method is used to load test templates at startup.
     * 
     * @param testProperties a {@code Properties} object, which at present contains a single property named "path". The
     *            value of "path" is either a directory containing property files with a ".meta" suffix or a single
     *            property file. The property files contain the following properties which describe a template:
     *            <ul>
     *            <li>file - the path to the jar file containing the template</li>
     *            <li>id - the template id</li>
     *            <li>name - the template name</li>
     *            <li>domain - the template domain</li>
     *            <li>description - the template's description</li>
     *            <li>vhimVersion - the template's version</li>
     *            <li>requestType - the operation for the template</li>
     *            <li>entryPoint - the template's entry point</li>
     *            <li>targetNamespace - the template's target namespace</li>
     *            </ul>
     * 
     */
    public synchronized void setTestTemplates( Properties testProperties )
        throws IOException
    {
        String path = testProperties.getProperty( "path", "." );
        boolean loadTestTemplates = Boolean.parseBoolean( testProperties.getProperty( "loadTestTemplates", "false" ) );

        File dir = new File( path );
        if ( !dir.exists() )
        {
            return;
        }

        if ( dir.isFile() )
        {
            loadTemplateMetaDataIntoMap( dir, loadTestTemplates );
        }
        else
        {
            File[] files = dir.listFiles( new FileFilter()
            {
                public boolean accept( File pathname )
                {
                    return ( pathname.isFile() && pathname.getName().endsWith( ".meta" ) );
                }
            } );

            for ( int i = 0; i < files.length; i++ )
            {
                loadTemplateMetaDataIntoMap( files[i], loadTestTemplates );
            }
        }
    }


    private void loadTemplateMetaDataIntoMap( File templatePropFile, boolean loadTestTemplates )
        throws IOException
    {
        FileInputStream propInputStream = null;

        try
        {
            propInputStream = new FileInputStream( templatePropFile );
            Properties templateProps = new Properties();
            templateProps.load( propInputStream );
            propInputStream.close();

            boolean testonly = Boolean.parseBoolean( templateProps.getProperty( "testOnly", "false" ) );

            if ( loadTestTemplates || !testonly )
            {
                templateMetaDataMap.put( templateProps.getProperty( "id" ), templateProps );
            }
        }
        finally
        {
            if ( propInputStream != null )
            {
                try
                {
                    propInputStream.close();
                }
                finally
                {
                    propInputStream = null;
                }
            }
        }
    }


    @Override
    public boolean isAlive( )
    {
        return true;
    }

}
