/*
 * Copyright (c) 2004 Brian McCallister. All Rights Reserved.
 */
package org.skife.gear.service.ojb;

import org.apache.ojb.broker.PBKey;
import org.apache.ojb.broker.PersistenceBroker;
import org.apache.ojb.broker.PersistenceBrokerFactory;
import org.apache.ojb.broker.accesslayer.LookupException;
import org.apache.ojb.broker.metadata.ConnectionRepository;
import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
import org.apache.ojb.broker.metadata.MetadataManager;
import org.skife.gear.service.Tx;
import org.skife.gear.service.TxRunner;

import javax.servlet.ServletContext;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;

public class TxRunnerImpl implements TxRunner
{
    public static final String DB_NAME = "gear";

    private final PBKey key;
    private final Properties sql;
    private static volatile Boolean initialized = Boolean.FALSE;

    public TxRunnerImpl() throws IOException, LookupException, SQLException
    {
        this(PersistenceBrokerFactory.getDefaultKey(), "/tmp/gear-db");
    }

    public TxRunnerImpl(final String db) throws SQLException, LookupException, IOException
    {
        this(PersistenceBrokerFactory.getDefaultKey(), db);
    }

    public TxRunnerImpl(final ServletContext ctx) throws IOException, LookupException, SQLException
    {
        this(PersistenceBrokerFactory.getDefaultKey(), ctx.getRealPath("WEB-INF/database"));
    }

    public TxRunnerImpl(final PBKey key, final String dbdir) throws IOException, LookupException, SQLException
    {

        this.key = key;
        final File file = new File(dbdir);
        InputStream in = TxRunnerImpl.class.getClassLoader().getResourceAsStream("sql.properties");
        (sql = new Properties()).load(in);
        in.close();

        // Skip Global OJB setup if completed
        synchronized (initialized)
        {
            if (initialized.booleanValue())
                return;
            else {
                mungeDBPath(dbdir);
                if (!file.exists()) file.mkdir();
                createTables();	
                initialized = Boolean.TRUE;
            }	
        }
    }

    private void mungeDBPath(final String dbdir)
    {
        final MetadataManager man = MetadataManager.getInstance();
        final ConnectionRepository cr = man.connectionRepository();
        final JdbcConnectionDescriptor dr = cr.getDescriptor(key);
        dr.setDbAlias(DB_NAME + ":" + dbdir);
        dr.setDefaultConnection(true);
        man.connectionRepository().removeDescriptor(key);
        man.connectionRepository().addDescriptor(dr);
    }

    private void createTables() throws LookupException, SQLException
    {
        final PersistenceBroker broker = PersistenceBrokerFactory.createPersistenceBroker(key);
        final Connection conn = broker.serviceConnectionManager().getConnection();
        for (final Iterator iterator = sql.entrySet().iterator(); iterator.hasNext();)
        {
            final Map.Entry entry = (Map.Entry) iterator.next();
            final String name = (String) entry.getKey();
            if ("test".equals(name.substring(name.indexOf('.') + 1, name.length())))
            {
                final Statement stmt = conn.createStatement();
                try
                {
                    stmt.execute((String) entry.getValue());
                }
                catch (Exception e)
                {
                    final String create_name = name.substring(0, name.indexOf('.')) + ".create";
                    stmt.execute((String) sql.get(create_name));
                }
                stmt.close();
            }
        }
        conn.close();
        broker.close();
    }

    public Object execute(final Tx tx) throws Exception
    {
        final PersistenceBroker broker = PersistenceBrokerFactory.createPersistenceBroker(key);
        try
        {
            broker.beginTransaction();
            final Object value = tx.execute(broker);
            broker.commitTransaction();
            broker.close();
            return value;
        }
        catch (Exception e)
        {
            if (!broker.isClosed() && broker.isInTransaction()) broker.abortTransaction();
            broker.close();
            throw e;
        }
    }

    public void clearCache()
    {
        final PersistenceBroker broker = PersistenceBrokerFactory.createPersistenceBroker(key);
        broker.clearCache();
        broker.close();
    }
}
