/*
 *    GeoTools - OpenSource mapping toolkit
 *    http://geotools.org
 *    (C) 2003-2006, GeoTools Project Managment Committee (PMC)
 *    
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation;
 *    version 2.1 of the License.
 *
 *    This library 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
 *    Lesser General Public License for more details.
 */
package org.geotools.data;

/**
 * Default implementation of the FeatureLockFactory.  Generates id numbers.
 *
 * @author Jody Garnett, Refractions Research, Inc.
 * @author Chris Holmes, TOPP.
 * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/main/src/main/java/org/geotools/data/DefaultFeatureLockFactory.java $
 * @version $Id: DefaultFeatureLockFactory.java 20651 2006-07-21 07:51:54Z jgarnett $
 *
 * @task REVISIT: Should more of this code move to the parent?  I guess if
 *       other implementations came along they may want to implement
 *       differently.
 * @task REVISIT: The generation code can move to the parent. Even if other
 *       implementations come along we do not want the to implement the
 *       generation differently.
 */
public class DefaultFeatureLockFactory extends FeatureLockFactory {
    /** Count used to generate unique ID numbers */
    static long count = 0;

    protected FeatureLock createLock(String name, long duration) {
        long number = nextIdNumber(duration);

        return new DefaultFeatureLock(name + "_" + Long.toString(number, 16),
            duration);
    }

    /**
     * Package visiable generate function for JUnit tests
     *
     * @param name The name to give this lock.
     * @param duration How long it is to last.
     *
     * @return The new FeatureLock.
     */
    static FeatureLock createTestLock(String name, long duration) {
        return new DefaultFeatureLock(name, duration);
    }

    /**
     * Used to seed nextIDNumber to allow for reproduceable JUnit tests.  Not
     * part of the public API -  package protected method used for JUnits
     * Tests
     *
     * @param seed The number to start seeding with.
     */
    static void seedIdNumber(long seed) {
        count = seed;
    }

    /**
     * Produces the next ID number based on count, duration and the current
     * time.  The uniquity is 'good enough' although not provable as per
     * security systems. The method used will probably have to be changed
     * later, the api is what is important right now. 
     * <table border=1, bgcolor="lightgray", width="100%"><tr><td><code><pre>
     * count: 000000000000000000000000000001011 (increasing count)
     *  date: 000000000001111010101010101000000 (expriry date milliseconds usually empty)
     *    now: 000101100111011010011111000000000 (bit order reverse of current time)
     *         ---------------------------------
     * number: 000101100110100000110101001001011
     * </pre></code></td></tr></table>
     * Once again method is package visible for testing.
     *
     * @param duration The time for the lock to last.
     *
     * @return The next generated id number.
     */
    static long nextIdNumber(long duration) {
        long number;
        long now = System.currentTimeMillis();
        long time = now + duration;

        // start of with number
        count++;
        number = count;
        number = number ^ time; // count fills 'empty' milliseconds

        StringBuffer reverse = new StringBuffer(asBits(now));
        now = Long.parseLong(reverse.reverse().toString(), 2); // flip now around

        number = number ^ now;

        return number;
    }

    /**
     * Utility method used to reverse bits.
     * <p>
     * Since generating ID numbers is not performance critical I won't care
     * right now.
     * </p>
     *
     * @param number
     * @return Number represented as bits
     */
    private static String asBits(long number) {
        long yum = number;
        StringBuffer buf = new StringBuffer(32);

        for (int i = 0; i < 63; i++) {
            buf.append(yum & 1);
            yum = yum >> 1;
        }

        buf.reverse();

        return buf.toString();
    }
}
