/*
 * Copyright (c) 2002-2009 "Neo Technology,"
 *     Network Engine for Objects in Lund AB [http://neotechnology.com]
 *
 * This file is part of Neo4j.
 * 
 * Neo4j is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package org.neo4j.kernel.impl.transaction;

import java.util.Map;

import javax.transaction.TransactionManager;

import org.neo4j.graphdb.TransactionFailureException;
import org.neo4j.kernel.impl.core.KernelPanicEventGenerator;
import org.neo4j.kernel.impl.transaction.xaframework.XaDataSource;

/**
 * Can reads a XA data source configuration file and registers all the data
 * sources defined there or be used to manually add XA data sources.
 * <p>
 * This module will create a instance of each {@link XaDataSource} once started
 * and will close them once stopped.
 * 
 * @see XaDataSourceManager
 */
public class TxModule
{
    private static final String MODULE_NAME = "TxModule";

    private boolean startIsOk = true;
    private String txLogDir = "var/tm";

    private final TransactionManager txManager;
    private final XaDataSourceManager xaDsManager;
    private final KernelPanicEventGenerator kpe;

    public TxModule( String txLogDir, KernelPanicEventGenerator kpe )
    {
        this.txLogDir = txLogDir;
        this.kpe = kpe;
        this.txManager = new TxManager( txLogDir, kpe );
        this.xaDsManager = new XaDataSourceManager();
    }
    
    public TxModule( boolean readOnly, KernelPanicEventGenerator kpe )
    {
        this.kpe = kpe;
        if ( readOnly )
        {
            this.txManager = new ReadOnlyTxManager(); 
            this.xaDsManager = new XaDataSourceManager();
        }
        else
        {
            throw new IllegalStateException( "Read only must be set for this constructor" );
        }
    }

    public void init()
    {
    }

    public void start()
    {
        if ( !startIsOk )
        {
            return;
        }
        if ( txManager instanceof TxManager )
        {
            ((TxManager)txManager).init( xaDsManager );
        }
        else
        {
            ((ReadOnlyTxManager)txManager).init( xaDsManager );
        }
        startIsOk = false;
    }

    public void reload()
    {
        stop();
        start();
    }

    public void stop()
    {
        xaDsManager.unregisterAllDataSources();
        if ( txManager instanceof TxManager )
        {
            ((TxManager)txManager).stop();
        }
        else
        {
            ((ReadOnlyTxManager)txManager).stop();
        }
    }

    public void destroy()
    {
    }

    public String getModuleName()
    {
        return MODULE_NAME;
    }

    /**
     * Use this method to add data source that can participate in transactions
     * if you don't want a data source configuration file.
     * 
     * @param name
     *            The data source name
     * @param className
     *            The (full) class name of class
     * @param resourceId
     *            The resource id identifying datasource
     * @param params
     *            The configuration map for the datasource
     * @throws LifecycleException
     */
    public XaDataSource registerDataSource( String dsName, String className,
        byte resourceId[], Map<?,?> params )
    {
        XaDataSourceManager xaDsMgr = xaDsManager;
        String name = dsName.toLowerCase();
        if ( xaDsMgr.hasDataSource( name ) )
        {
            throw new TransactionFailureException( "Data source[" + name
                + "] has already been registered" );
        }
        try
        {
            XaDataSource dataSource = xaDsMgr.create( className, params );
            xaDsMgr.registerDataSource( name, dataSource, resourceId );
            return dataSource;
        }
        catch ( Exception e )
        {
            throw new TransactionFailureException( 
                "Could not create data source [" + name
                + "], see nested exception for cause of error", e );
        }
    }

    public XaDataSource registerDataSource( String dsName, String className,
        byte resourceId[], Map<?,?> params, boolean useExisting )
    {
        XaDataSourceManager xaDsMgr = xaDsManager;
        String name = dsName.toLowerCase();
        if ( xaDsMgr.hasDataSource( name ) )
        {
            if ( useExisting )
            {
                return xaDsMgr.getXaDataSource( name );
            }
            throw new TransactionFailureException( "Data source[" + name
                + "] has already been registered" );
        }
        try
        {
            XaDataSource dataSource = xaDsMgr.create( className, params );
            xaDsMgr.registerDataSource( name, dataSource, resourceId );
            return dataSource;
        }
        catch ( Exception e )
        {
            throw new TransactionFailureException( 
                "Could not create data source " + name + "[" + name + "]", e );
        }
    }

    public String getTxLogDirectory()
    {
        return txLogDir;
    }

    public TransactionManager getTxManager()
    {
        return txManager;
    }
    
    public XaDataSourceManager getXaDataSourceManager()
    {
        return xaDsManager;
    }

    public int getStartedTxCount()
    {
        if ( txManager instanceof TxManager )
        {
            return ((TxManager) txManager).getStartedTxCount();
        }
        return 0;
    }

    public int getCommittedTxCount()
    {
        if ( txManager instanceof TxManager )
        {
            return ((TxManager) txManager).getCommittedTxCount();
        }
        return 0;
    }
    
    public int getRolledbackTxCount()
    {
        if ( txManager instanceof TxManager )
        {
            return ((TxManager) txManager).getRolledbackTxCount();
        }
        return 0;
    }
    
    public int getActiveTxCount()
    {
        if ( txManager instanceof TxManager )
        {
            return ((TxManager) txManager).getActiveTxCount();
        }
        return 0;
    }
    
    public int getPeakConcurrentTxCount()
    {
        if ( txManager instanceof TxManager )
        {
            return ((TxManager) txManager).getPeakConcurrentTxCount();
        }
        return 0;
    }
}