/*
 *************************************************************************
 * Copyright (c) 2004, 2005 Actuate Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *  Actuate Corporation  - initial API and implementation
 *  
 *************************************************************************
 */

package org.eclipse.birt.data.engine.odaconsumer;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.channels.FileChannel;
import java.util.Properties;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import org.eclipse.birt.core.framework.IBundle;
import org.eclipse.birt.core.framework.IPlatform;
import org.eclipse.birt.core.framework.Platform;
import org.eclipse.core.internal.runtime.InternalPlatform;
import org.eclipse.datatools.connectivity.oda.util.manifest.ExtensionManifest;
import org.eclipse.datatools.connectivity.oda.util.manifest.ManifestExplorer;
import org.eclipse.datatools.connectivity.oda.util.manifest.TraceLogging;

import testutil.JDBCOdaDataSource;

/**
 *
 */
public class TraceLoggingTest extends OdaconsumerTestCase
{

    private static String sm_jdbcId = JDBCOdaDataSource.DATA_SOURCE_TYPE;
    private static String sm_pluginFileName = "plugin.xml"; //$NON-NLS-1$
    private static String sm_installDirectory;
    private static String sm_pluginFile = null;
    private static String sm_manifestsDir = null;
    private static String sm_datasourceExtension = "org.eclipse.datatools.connectivity.oda.dataSource";

    private Connection m_conn;
    private File m_logDir;
    
    // flags to determine which tests to run
    private boolean m_pluginTest;
    private boolean m_tracing;
    private int m_pluginFileNum;
    
    /*
     * @see TestCase#setUp()
     */
    protected void setUp() throws Exception
    {
        super.setUp();
        
        if( sm_installDirectory == null )
            sm_installDirectory =
                TraceLogTesterUtil.getInstance().findInstallationDirectory( sm_jdbcId );
        if( m_logDir == null ) 
            m_logDir = new File( sm_installDirectory, "OdaLogs/" );
        if ( ! m_logDir.exists() )
         	m_logDir.mkdir();
         
        // set up member variables
	    setupFlags();
	    setupDirectories();
           
	    // test #4 must have tracing enabled; otherwise, skip test
	    if ( ! m_tracing && 
	         getName().equals( "testLogPrecedence" ) )
   	     	return; 
	    
	    // copy test plugin.xml to plugin directory, save existing file
        setPluginFile();

        // settings for .options
        if( getName().equals( "testLogPrecedence" ) )
            setDebugOption(); // test #4 is testing .options settings precedence
        else
        	clearDebugOption(); // tests #1-3 are testing plugin.xml settings only

        // testOpenConnection from ConnectionManagerTest
        setupConnection();             
    }

    /*
     * @see TestCase#tearDown()
     */
    protected void tearDown() throws Exception
    {   
        clearHandlers();
        
        removeDirectory( m_logDir );
        
        if( getName().equals( "testEmptyLogDirectory" ) )
        {
            // logfile was created in installation directory
            File[] files = new File( sm_installDirectory ).listFiles();
            for ( int i = 0; i < files.length; i++ )
            {
                if( ! files[i].isFile() )
                    continue;
                String fileName = files[i].getName();
                if( fileName.startsWith( "OdaJdbcLog-")  )
                {
                    files[i].delete();
                }
            }
        }

        if ( ! m_tracing && 
             getName().equals( "testLogPrecedence" ) )
        	return; // if not tracing, test #4 didn't run
        
        restorePluginFile();
        
        if ( m_conn != null )
            m_conn.close();   
        
        super.tearDown();
    }
    
    public void testEmptyLogPrefix() throws Exception
	{
        assertEquals( 1, m_pluginFileNum );
        
        ExtensionManifest config = 
			ManifestExplorer.getInstance().getExtensionManifest( sm_jdbcId, sm_datasourceExtension );
		assertNotNull( config );
		
		TraceLogging traceLogging = config.getTraceLogging();
		assertEquals( "./OdaLogs/", traceLogging.getLogDirectory() );
		assertNull( traceLogging.getLogFileNamePrefix() );
		assertEquals( 400, traceLogging.getLogLevel() );
		assertEquals( "java.util.logging.SimpleFormatter", traceLogging.getLogFormatterClass() );

		assertSingleHandlerAndLogLevel( 400 );
	}
    
    public void testEmptyLogDirectory() throws Exception
	{
        assertEquals( 2, m_pluginFileNum );  
        
		ExtensionManifest config = 
			ManifestExplorer.getInstance().getExtensionManifest( sm_jdbcId, sm_datasourceExtension );
		assertNotNull( config );
		
		TraceLogging traceLogging = config.getTraceLogging();
		assertNull( traceLogging.getLogDirectory() );
		assertEquals( "OdaJdbcLog", traceLogging.getLogFileNamePrefix() );
		assertEquals( 500, traceLogging.getLogLevel() );
		assertEquals( "java.util.logging.SimpleFormatter", traceLogging.getLogFormatterClass() );

		assertSingleHandlerAndLogLevel( 500 );
	}
    

    public void testLogFile() throws Exception
	{
        assertEquals( 3, m_pluginFileNum );
        
		ExtensionManifest config = 
			ManifestExplorer.getInstance().getExtensionManifest( sm_jdbcId, sm_datasourceExtension );
		assertNotNull( config );
		
		TraceLogging traceLogging = config.getTraceLogging();
		assertEquals( "./OdaLogs/", traceLogging.getLogDirectory() );
		assertEquals( "OdaJdbcLog", traceLogging.getLogFileNamePrefix() );
		assertEquals( 700, traceLogging.getLogLevel() );
		assertEquals( "java.util.logging.SimpleFormatter", traceLogging.getLogFormatterClass() );
       
		assertSingleHandlerAndLogLevel( 700 );
	}
    
    public void testLogPrecedence() throws Exception
	{
        if ( m_tracing )
		{
            assertEquals( 4, m_pluginFileNum );
            
            ExtensionManifest config = 
    			ManifestExplorer.getInstance().getExtensionManifest( sm_jdbcId, sm_datasourceExtension );
    		assertNotNull( config );
    		
    		TraceLogging traceLogging = config.getTraceLogging();
    		assertEquals( "./OdaLogs/", traceLogging.getLogDirectory() );
    		assertEquals( "OdaJdbcLog", traceLogging.getLogFileNamePrefix() );
    		assertEquals( 500, traceLogging.getLogLevel() );
    		assertEquals( "java.util.logging.SimpleFormatter", traceLogging.getLogFormatterClass() );
           
    		// the actual log level will be from the .options settings
    		assertSingleHandlerAndLogLevel( 800 ); 
		}
	}

    private void setPluginFile() throws Exception
    {
        File pluginFile = new File( sm_pluginFile );
        File backupFile = new File( sm_pluginFile + ".bak" );
        
        // if there is an existing plugin.xml file, save it
        
        if ( backupFile.exists() )
            backupFile.delete();
        
        if ( pluginFile.exists() )
            pluginFile.renameTo( backupFile );

        String testCaseName = getName();
        m_pluginFileNum = 0;	// reset
        if( testCaseName.equals( "testEmptyLogPrefix" ) )
            m_pluginFileNum = 1;
        else if( testCaseName.equals( "testEmptyLogDirectory" ) )
            m_pluginFileNum = 2;
        else if( testCaseName.equals( "testLogFile" ) )
            m_pluginFileNum = 3;
        else if( testCaseName.equals( "testLogPrecedence" ) )
            m_pluginFileNum = 4;
        else
            fail( "Test case name is not recognized: " +  testCaseName );
        
        // move a new plugin.xml into driver plugin directory
        pluginFile.createNewFile();
        File sourceFile = new File( sm_manifestsDir + "plugin.xml." + m_pluginFileNum );
        FileChannel sourceChan = new FileInputStream( sourceFile ).getChannel();
        FileChannel destChan = new FileOutputStream( pluginFile ).getChannel();
        destChan.transferFrom( sourceChan, 0, sourceChan.size() );
        sourceChan.close();
        destChan.close();
        
        initializeExtensionRegistry();
    }
    
    private void restorePluginFile()
    {       
        File pluginFile = new File( sm_pluginFile );
        File backupFile = new File( sm_pluginFile + ".bak" );
        
        // if a plugin.xml was saved, restore it
        if ( backupFile.exists() )
        {
            if ( pluginFile.exists() )
                pluginFile.delete();
            
            backupFile.renameTo( pluginFile );
        }
        
        initializeExtensionRegistry();
        
    }
    
    /*
     * This class is used to access the protected member variable 'platform' in
     * the Platform class. This testclass uses the 'setPlatform' method to create
     * new ServerPlatform objects, each testing a different ExtensionRegistry.
     */
    private static class PlatformAccessor extends Platform
    {
        public static void setPlatform( IPlatform p )
        {
            platform = p;
        }
    }
        
    /*
     * Uses same connection as ConnectionManagerTest
     */
    private void setupConnection() throws Exception
	{
		if( System.getProperty( "odaJdbcUrl" ) == null ||
			System.getProperty( "odaJdbcUser" ) == null ||
			System.getProperty( "odaJdbcPassword" ) == null ||
			System.getProperty( "odaJdbcDriver" ) == null )
		{
			System.setProperty( "odaJdbcUrl", "jdbc:derby:DtETest" );
			System.setProperty( "odaJdbcUser", "sa" );
			System.setProperty( "odaJdbcPassword", "sa" );
			System.setProperty( "odaJdbcDriver", "org.apache.derby.jdbc.EmbeddedDriver" );
		}
		
		ConnectionManager manager = ConnectionManager.getInstance();
		Properties connProperties = new Properties();
		connProperties.setProperty( "odaURL", System.getProperty( "odaJdbcUrl" ) );
		connProperties.setProperty( "odaUser", System.getProperty( "odaJdbcUser" ) );
		connProperties.setProperty( "odaPassword", System.getProperty( "odaJdbcPassword" ) );
		connProperties.setProperty( "odaDriverClass", System.getProperty( "odaJdbcDriver" ) );
		m_conn = manager.openConnection( JDBCOdaDataSource.DATA_SOURCE_TYPE, connProperties );
		assertNotNull( m_conn );
	}
        
/*  	Comment out code not used.
	private void reinitializeRegistry()
    {
        // taken from org.eclipse.core.internal.runtime.PlatformActivator.startRegistry()
        ExtensionRegistry registry = new ExtensionRegistry();
        InternalPlatform.getDefault().setExtensionRegistry( registry );
        EclipseBundleListener pluginBundleListener = new EclipseBundleListener( registry );
        BundleContext runtimeContext = PlatformActivator.getContext();
        runtimeContext.addBundleListener( pluginBundleListener );
		pluginBundleListener.processBundles( runtimeContext.getBundles() );
    }
*/    
    private void clearDirectory( File dir ) throws IOException
    {
        File[] files = dir.listFiles();
        for ( int i = 0; i < files.length; i += 1 )
        {
            boolean deleted = files[i].delete();
            if ( ! deleted )
                throw new IOException( "Cannot delete file: " + files[i].getName() ); 
        }
    }
    
    private void removeDirectory( File dir ) throws IOException
    {       
        // reassigning settings causes logger to close existing FileHandler,
        org.eclipse.datatools.connectivity.oda.util.logging.LogManager.getLogger( 
                "org.eclipse.datatools.connectivity.oda", 
                org.eclipse.datatools.connectivity.oda.util.logging.Level.ALL, 
                "./", "OdaLogs", "org.eclipse.datatools.connectivity.oda.util.logging.SimpleFormatter" );
        
        // set logger settings to OFF
        org.eclipse.datatools.connectivity.oda.util.logging.LogManager.getLogger( 
                "org.eclipse.datatools.connectivity.oda", 
                org.eclipse.datatools.connectivity.oda.util.logging.Level.OFF, 
                null, null, null );
       
        clearDirectory( dir );
        dir.delete();
    }
       
    private void clearDebugOption()
    {
        if ( m_tracing )
        {
	        InternalPlatform.getDefault().setOption( sm_jdbcId + "/debug", "false" );
	        String option = Platform.getDebugOption( sm_jdbcId + "/debug" );
	        assertEquals( "false", option );
        }
    }
    
    private void setDebugOption()
    {
		if ( m_tracing )
		{
			InternalPlatform.getDefault().setOption( sm_jdbcId + "/debug", "true" );
	        String option = Platform.getDebugOption( sm_jdbcId + "/debug" );     
	        assertEquals( "true", option );
	        
	        InternalPlatform.getDefault().setOption( sm_jdbcId + "/traceLogging/logLevel", "800" );
	        option = Platform.getDebugOption( sm_jdbcId + "/traceLogging/logLevel" );
	        assertEquals( "800", option );
	        
	        InternalPlatform.getDefault().setOption( sm_jdbcId + "/traceLogging/logFormatterClass", "java.util.logging.SimpleFormatter" );
	        option = Platform.getDebugOption( sm_jdbcId + "/traceLogging/logFormatterClass" );
	        assertEquals( "java.util.logging.SimpleFormatter", option );
	        
	        InternalPlatform.getDefault().setOption( sm_jdbcId + "/traceLogging/logFileNamePrefix", "OdaJdbcOptionsLog" );
	        option = Platform.getDebugOption( sm_jdbcId + "/traceLogging/logFileNamePrefix" );
	        assertEquals( "OdaJdbcOptionsLog", option );
	        
	        InternalPlatform.getDefault().setOption( sm_jdbcId + "/traceLogging/logDirectory", "./OdaLogs/" );
	        option = Platform.getDebugOption( sm_jdbcId + "/traceLogging/logDirectory" );
	        assertEquals( "./OdaLogs/", option );
		}
    }
    
    private void setupFlags()
    {
        boolean runningUnderEclipse; 
        
        // get BIRT property RUNNING_UNDER_ECLIPSE
        String runningEclipse = System.getProperty( Platform.PROPERTY_RUN_UNDER_ECLIPSE );
        runningUnderEclipse = Boolean.valueOf( runningEclipse ).booleanValue();
        
        // get the Eclipse runtime isRunning flag; if running as a standard 
        // JUnit test, Platform will not be running
        m_pluginTest = org.eclipse.core.runtime.Platform.isRunning();
        
        // get tracing flag; after setting an option with setOption
        // and the return value from getOption is null, tracing is disabled
        InternalPlatform.getDefault().setOption( sm_jdbcId + "/debug", "false" );
	    String option = Platform.getDebugOption( sm_jdbcId + "/debug" );
	    if ( option == null )
	        m_tracing = false;
	    else
	        m_tracing = true;
	    
	    // ensure that the two flags are consistent
	    if ( runningUnderEclipse == m_pluginTest )
	    {
	        return; // the two flags are the same, so return early;
	        		// otherwise, the two flags are different and must be corrected
	    }
	    else if ( m_pluginTest == false )
	    {
	        System.setProperty( Platform.PROPERTY_RUN_UNDER_ECLIPSE, "false" );
	        m_tracing = false; // ensure that tracing is off
	    }
	    else if ( m_pluginTest == true )
	    {
	        System.setProperty( Platform.PROPERTY_RUN_UNDER_ECLIPSE, "true" );
	    }
	    
    }
    
    private void setupDirectories() throws IOException
    {
        String dataDir;

        if ( sm_pluginFile != null && sm_manifestsDir != null )
            return; // already set, so return early
        
        if ( m_pluginTest )
        {       	
        	// set directory where test plugin.xml files are stored 
            IBundle dataBundle = Platform.getBundle( "org.eclipse.birt.data" );
    	    URL url = dataBundle.getEntry( "/" );
    	    dataDir = Platform.asLocalURL( url ).getPath();

    	    // set location where plugin.xml will be parsed 	
            IBundle jdbcBundle = Platform.getBundle( "org.eclipse.birt.report.data.oda.jdbc" );
		    URL jdbcUrl = jdbcBundle.getEntry( "/" );
		    sm_pluginFile = Platform.asLocalURL( jdbcUrl ).getPath() + sm_pluginFileName;
        }
        else
        {
        	// set directory where test plugin.xml files are stored
        	dataDir = ".";
        	
        	// set location where plugin.xml will be parsed
            sm_pluginFile = dataDir + "/test/plugins/" + sm_jdbcId + "/" + sm_pluginFileName;
            System.setProperty( "BIRT_HOME", dataDir + "/test" );  
        }

        sm_manifestsDir = dataDir + "/test/plugins/" + sm_jdbcId + "/manifests/";
    }

    private void assertSingleHandlerAndLogLevel( int level )
    {
        Logger logger = Logger.getLogger( sm_jdbcId );
		Handler[] handlers = logger.getHandlers();
		assertEquals( 1, handlers.length );
		Formatter formatter = handlers[0].getFormatter();
		
		assertTrue( formatter instanceof SimpleFormatter );
		assertTrue( handlers[0] instanceof FileHandler );
		assertEquals( level, logger.getLevel().intValue() );
    }
    
    private void clearHandlers()
    {
        Logger logger = Logger.getLogger( sm_jdbcId );
		Handler[] handlers = logger.getHandlers();
		if ( handlers.length > 0 )
		{
		    for ( int i = 0; i < handlers.length; i += 1 )
		    {
		        handlers[i].close();
				logger.removeHandler( handlers[i] );
		    }
		}
    }
    
    private void initializeExtensionRegistry()
    {
        // initialize the extension registry
        if ( m_pluginTest )
        {
            // reinitializeRegistry();
            fail( "Does not support Plugin Test.");
        }
        else
        {
            PlatformAccessor.setPlatform( null );
        	Platform.initialize( null );
        }
        
        DriverManager.getInstance().getLoadedDrivers().remove( sm_jdbcId );
    }
    
}


