/*
 * 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.xaframework;

import java.io.IOException;
import java.nio.channels.ReadableByteChannel;
import java.util.Map;
import java.util.StringTokenizer;

/**
 * <CODE>XaDataSource</CODE> is as a factory for creating
 * {@link XaConnection XaConnections}.
 * <p>
 * If you're writing a data source towards a XA compatible resource the
 * implementation should be fairly straight forward. This will basically be a
 * factory for your {@link XaConnection XaConnections} that in turn will wrap
 * the XA compatible <CODE>XAResource</CODE>.
 * <p>
 * If you're writing a data source towards a non XA compatible resource use the
 * {@link XaContainer} and extend the {@link XaConnectionHelpImpl} as your
 * {@link XaConnection} implementation. Here is an example:
 * <p>
 * 
 * <pre>
 * <CODE>
 * public class MyDataSource implements XaDataSource {
 *     MyDataSource( params ) {
 *         // ... initalization stuff
 *         container = XaContainer.create( myLogicalLogFile, myCommandFactory, 
 *             myTransactionFactory );
 *         // ... more initialization stuff
 *         container.openLogicalLog();
 *     }
 *     public XaConnection getXaCo nnection() {
 *         return new MyXaConnection( params );
 *     }
 *     public void close() {
 *         // ... cleanup
 *         container.close();
 *     }
 * }
 * </CODE>
 * </pre>
 */
public abstract class XaDataSource
{
    private byte[] branchId = null;
    private String name = null;
    
    /**
     * Constructor used by the Neo4j kernel to create datasources.
     * 
     * @param params
     *            A map containing configuration parameters
     */
    public XaDataSource( Map<?,?> params ) throws InstantiationException
    {
    }

    /**
     * Creates a XA connection to the resource this data source represents.
     * 
     * @return A connection to an XA resource
     */
    public abstract XaConnection getXaConnection();

    /**
     * Closes this data source. Calling <CODE>getXaConnection</CODE> after
     * this method has been invoked is illegal.
     */
    public abstract void close();

    /**
     * Used by the container/transaction manager in place to assign a branch 
     * id for this data source.
     * 
     * @param branchId the branch id
     */
    public void setBranchId( byte branchId[] )
    {
        this.branchId = branchId;
    }

    /**
     * Returns any assigned or default branch id for this data source.
     * 
     * @return the branch id
     */
    public byte[] getBranchId()
    {
        return this.branchId;
    }


    /**
     * Returns a timestamp when this data source was created. Note this is not 
     * the timestamp for the creation of the data source object instance, if  
     * the data source is for example a database timestamp is meant to be when 
     * the database was created.
     * <p>
     * Creation time together with random identifier can be used to uniqley 
     * identify a data source (since it is possible to have multiple sources 
     * of same type).
     * 
     * @return timestamp when this datasource was created
     */
    public long getCreationTime()
    {
        throw new UnsupportedOperationException();
    }
    
    public String getFileName( long version )
    {
        throw new UnsupportedOperationException();
    }

    /**
     * Returns a random identifier that gets generated when the data source is
     * created. Note with "created" we mean first time data source is created
     * and not object creation.
     * <p>
     * Creation time together with the random identifier can be used to uniquely
     * identify a data source (since it is possible to have multiple sources of
     * the same type).
     * 
     * @return random identifier for this data source
     */
    public long getRandomIdentifier()
    {
        throw new UnsupportedOperationException();
    }
    
    /**
     * Returns the current version of this data source. A invoke to the 
     * {@link #rotateLogicalLog()} when {@link #keepLogicalLogs(boolean)} is 
     * set to <code>true</code> will result in a log with that version created.
     * 
     * @return the current version of the logical log
     */
    public long getCurrentLogVersion()
    {
        throw new UnsupportedOperationException();
    }
    
    /**
     * Attempts to apply a logical log to this data source.
     * 
     * @param byteChannel readable channel containing the logical log data
     * 
     * @throws IOException if a problem with reading the log occurs
     * @throws IllegalStateException if log being applied is not of right 
     * version, if not in backup slave mode or there are active transactions
     */
    public void applyLog( ReadableByteChannel byteChannel ) throws IOException
    {
        throw new UnsupportedOperationException();
    }
    
    /**
     * Rotates this logical log. If {@link #keepLogicalLogs(boolean)} is 
     * configured to true the log will be saved and can be retrieved with the 
     * {@link #getLogicalLog(long)} method. If not it will be deleted. Active 
     * transactions get copied to a new logical log.
     * 
     * @throws IOException if unable to read old log or write to new one
     */
    public void rotateLogicalLog() throws IOException
    {
        throw new UnsupportedOperationException();
    }

    /**
     * Returns a readable byte channel of the specified logical log.
     * 
     * @param version version of the logical log
     * @return readable byte channel of the logical log
     * @throws IOException if no such log exist
     */
    public ReadableByteChannel getLogicalLog( long version ) throws IOException
    {
        throw new UnsupportedOperationException();
    }
    
    /**
     * Tests if a specific logical log exists.
     * 
     * @param version the version of the logical log
     * @return <CODE>true</CODE> if the log exists
     */
    public boolean hasLogicalLog( long version )
    {
        throw new UnsupportedOperationException();
    }


    public long getLogicalLogLength( long version )
    {
        throw new UnsupportedOperationException();
    }
    
    /**
     * Deletes a log specific logical log.
     * 
     * @param version version of log to delete
     * 
     * @return true if the log existed and was deleted
     */
    public boolean deleteLogicalLog( long version )
    {
        throw new UnsupportedOperationException();
    }

    /**
     * Sets wether logical logs should be saved upon rotation or not. Default 
     * is <CODE>false</CODE>.
     * 
     * @param keepLogs <CODE>true</CODE> means save, <CODE>false</CODE> means 
     * delete
     */
    public void keepLogicalLogs( boolean keepLogs )
    {
        throw new UnsupportedOperationException();
    }
    
    public boolean isLogicalLogKept()
    {
        throw new UnsupportedOperationException();
    }
    
    /**
     * Used by the container to assign a name to this resource. 
     * 
     * @param name name of this resource
     */
    public void setName( String name )
    {
        this.name = name;
    }

    /**
     * Returns the assigned name of this resource.
     * 
     * @return the assigned name of this resource
     */
    public String getName()
    {
        return name;
    }

    /**
     * Makes this data source a backup slave. This method can not be called 
     * while there are active transactions. Once set in "backup slave" mode 
     * no new transactions can start, the resource has to be closed and 
     * reopened for that.
     *
     * @throws IllegalStateException if this resource has active transactions
     */
    public void makeBackupSlave()
    {
        throw new UnsupportedOperationException();
    }
    
    /**
     * Turns off/on auto rotate of logical logs. Default is <CODE>true</CODE>.
     * 
     * @param rotate <CODE>true</CODE> to turn on
     */
    public void setAutoRotate( boolean rotate )
    {
        throw new UnsupportedOperationException();
    }

    /**
     * Sets the target size of the logical log that will cause a rotation of 
     * the log if {@link #setAutoRotate(boolean)} is set to <CODE>true</CODE>.
     * 
     * @param size target size in bytes
     */
    public void setLogicalLogTargetSize( long size )
    {
        throw new UnsupportedOperationException();
    }

    /**
     * If the config says that logical logs should be kept for the given
     * {@code resourceName} this method will return {@code true}, otherwise
     * {@code false}. The format of the configuration value is either:
     * <ul>
     *   <li><b>=</b> : no data sources will keep its logs</li>
     *   <li><b>=nioneodb,lucene</b> : the specified data sources will keep its logs</li>
     *   <li><b>=true</b> : all data sources will keep their logical logs</li>
     * </ul>
     * The first and last are recommended, since the data source names are really
     * an implementation detail.
     * 
     * @param config the configuration value specified by user configuration.
     * @param resourceName the name of the xa data source to check.
     * @return whether or not logical logs should be kept for the data source
     * by the name {@code resourceName} or not.
     */
    protected boolean shouldKeepLog( String config, String resourceName )
    {
        if ( config != null )
        {
            if ( config.equals( Boolean.TRUE.toString() ) )
            {
                return true;
            }
            StringTokenizer tok = new StringTokenizer( config, "," );
            while ( tok.hasMoreTokens() )
            {
                String element = tok.nextToken().trim();
                if ( resourceName.equals( element ) )
                {
                    return true;
                }
            }
        }
        return false;
    }
}