// Path: fauxflake-core/src/main/java/com/github/rholder/fauxflake/DefaultIdGenerator.java
// public class DefaultIdGenerator implements IdGenerator {
// 
//     // lock for modifying shared state
//     private final Object LOCK = new Object();
// 
//     // rolling sequence number, 0 to encodingProvider.maxSequenceNumbers()
//     private volatile int sequence;
// 
//     // last time we checked the current time
//     private volatile long lastTime;
// 
//     private final TimeProvider timeProvider;
//     private final EncodingProvider encodingProvider;
//     private final int startingSequenceNumber;
// 
//     public DefaultIdGenerator(TimeProvider timeProvider,
//                               EncodingProvider encodingProvider) {
//         this(timeProvider, encodingProvider, 0);
//     }
// 
//     public DefaultIdGenerator(TimeProvider timeProvider,
//                               EncodingProvider encodingProvider,
//                               int startingSequenceNumber) {
//         this.timeProvider = timeProvider;
//         this.encodingProvider = encodingProvider;
// 
//         this.lastTime = timeProvider.getCurrentTime();
//         this.startingSequenceNumber = startingSequenceNumber;
//         this.sequence = 0;
//     }
// 
//     private Values generateValues(int maxWait) throws InterruptedException {
//         long currentTime;
//         int currentSequence;
//         synchronized (LOCK) {
//             currentTime = timeProvider.getCurrentTime();
// 
//             // backwards time, likely due to NTP updates for clock drift
//             if(currentTime < lastTime) {
//                 long diff = lastTime - currentTime;
//                 throw new BackwardsTimeException("Backwards time detected, try again in " + diff + " ms", diff);
//             }
// 
//             if(sequence == encodingProvider.maxSequenceNumbers()) {
//                 // out of sequence numbers for this clock tick, be evil and hold the lock until the time changes
//                 int currentWait = 0;
//                 while(currentTime <= lastTime) {
//                     if(currentWait > maxWait) {
//                         throw new WaitTimeExceededException("The maximum time to wait to generate an id has been exceeded") ;
//                     }
//                     Thread.sleep(1);
//                     currentTime = timeProvider.getCurrentTime();
//                     currentWait++;
//                 }
//             }
// 
//             if(currentTime != lastTime) {
//                 lastTime = currentTime;
//                 sequence = 0;
//             }
//             currentSequence = sequence;
//             sequence++;
//         }
//         return new Values(currentTime, (currentSequence + startingSequenceNumber) % encodingProvider.maxSequenceNumbers());
//     }
// 
//     @Override
//     public Id generateId(int maxWait) throws InterruptedException {
//         Values values = generateValues(maxWait);
//         return new EncodedId(encodingProvider, values.currentTime, values.currentSequence);
//     }
// 
//     /**
//      * This is a simple tuple-y pair class.
//      */
//     public static class Values {
//         public Values(long currentTime, int currentSequence) {
//             this.currentTime = currentTime;
//             this.currentSequence = currentSequence;
//         }
//         public long currentTime;
//         public int currentSequence;
//     }
// }
// 
// Path: fauxflake-core/src/main/java/com/github/rholder/fauxflake/api/Id.java
// public interface Id {
// 
//     /**
//      * Return a unique identifier as a long.
//      */
//     public long asLong();
// 
//     /**
//      * Return a unique identifier as bytes.
//      */
//     public byte[] asBytes();
// 
//     /**
//      * Return a unique identifier as a String.
//      */
//     public String asString();
// }
// 
// Path: fauxflake-core/src/main/java/com/github/rholder/fauxflake/api/IdGenerator.java
// public interface IdGenerator {
// 
//     /**
//      * Return a unique identifier. This should be resilient enough to handle
//      * leap seconds, however, backwards time that occurs when the system time is
//      * updated, as from NTP, will result in an {@link InterruptedException}.
//      *
//      * NOTE: Generating more id's at a time than can be handled may result in
//      * the current thread blocking for maxWait ms to ensure a new timestamp is
//      * available.
//      *
//      * @param maxWait
//      *          the maximum amount of time to wait in ms before giving up
//      * @throws InterruptedException
//      *          thrown when thread is interrupted, the maximum amount of time
//      *          to wait for a new timestamp to become available has expired, or
//      *          backwards time has been detected
//      */
//     public Id generateId(int maxWait) throws InterruptedException;
// }
// 
// Path: fauxflake-core/src/main/java/com/github/rholder/fauxflake/provider/SystemTimeProvider.java
// public class SystemTimeProvider implements TimeProvider {
// 
//     @Override
//     public long getCurrentTime() {
//         return System.currentTimeMillis();
//     }
// }


import com.github.rholder.fauxflake.DefaultIdGenerator;
import com.github.rholder.fauxflake.api.Id;
import com.github.rholder.fauxflake.api.IdGenerator;
import com.github.rholder.fauxflake.provider.SystemTimeProvider;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.util.Date;
/*
 * Copyright 2012-2014 Ray Holder
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.github.rholder.fauxflake.provider.twitter;



public class SnowflakeDecodingUtilsTest extends SnowflakeDecodingUtils {

    private static final int TEST_MACHINE_ID = 53;
    private IdGenerator idGenerator;

    @Before
    public void before() {
        idGenerator = new DefaultIdGenerator(new SystemTimeProvider(), new SnowflakeEncodingProvider(TEST_MACHINE_ID));
    }

    @Test
    public void decodedValueCheck() {
        // known id
        long id = 278275685817122816L;
        Assert.assertEquals("Timestamps did not match", 1355181068268L, decodeDate(id).getTime());
        Assert.assertEquals("Machine id's did not match", 32, decodeMachineId(id));
        Assert.assertEquals("Sequence numbers did not match", 0, decodeSequence(id));
    }

    @Test
    public void encodedValueCheck() throws InterruptedException {
        Date now = new Date();
// Path: fauxflake-core/src/main/java/com/github/rholder/fauxflake/DefaultIdGenerator.java
// public class DefaultIdGenerator implements IdGenerator {
// 
//     // lock for modifying shared state
//     private final Object LOCK = new Object();
// 
//     // rolling sequence number, 0 to encodingProvider.maxSequenceNumbers()
//     private volatile int sequence;
// 
//     // last time we checked the current time
//     private volatile long lastTime;
// 
//     private final TimeProvider timeProvider;
//     private final EncodingProvider encodingProvider;
//     private final int startingSequenceNumber;
// 
//     public DefaultIdGenerator(TimeProvider timeProvider,
//                               EncodingProvider encodingProvider) {
//         this(timeProvider, encodingProvider, 0);
//     }
// 
//     public DefaultIdGenerator(TimeProvider timeProvider,
//                               EncodingProvider encodingProvider,
//                               int startingSequenceNumber) {
//         this.timeProvider = timeProvider;
//         this.encodingProvider = encodingProvider;
// 
//         this.lastTime = timeProvider.getCurrentTime();
//         this.startingSequenceNumber = startingSequenceNumber;
//         this.sequence = 0;
//     }
// 
//     private Values generateValues(int maxWait) throws InterruptedException {
//         long currentTime;
//         int currentSequence;
//         synchronized (LOCK) {
//             currentTime = timeProvider.getCurrentTime();
// 
//             // backwards time, likely due to NTP updates for clock drift
//             if(currentTime < lastTime) {
//                 long diff = lastTime - currentTime;
//                 throw new BackwardsTimeException("Backwards time detected, try again in " + diff + " ms", diff);
//             }
// 
//             if(sequence == encodingProvider.maxSequenceNumbers()) {
//                 // out of sequence numbers for this clock tick, be evil and hold the lock until the time changes
//                 int currentWait = 0;
//                 while(currentTime <= lastTime) {
//                     if(currentWait > maxWait) {
//                         throw new WaitTimeExceededException("The maximum time to wait to generate an id has been exceeded") ;
//                     }
//                     Thread.sleep(1);
//                     currentTime = timeProvider.getCurrentTime();
//                     currentWait++;
//                 }
//             }
// 
//             if(currentTime != lastTime) {
//                 lastTime = currentTime;
//                 sequence = 0;
//             }
//             currentSequence = sequence;
//             sequence++;
//         }
//         return new Values(currentTime, (currentSequence + startingSequenceNumber) % encodingProvider.maxSequenceNumbers());
//     }
// 
//     @Override
//     public Id generateId(int maxWait) throws InterruptedException {
//         Values values = generateValues(maxWait);
//         return new EncodedId(encodingProvider, values.currentTime, values.currentSequence);
//     }
// 
//     /**
//      * This is a simple tuple-y pair class.
//      */
//     public static class Values {
//         public Values(long currentTime, int currentSequence) {
//             this.currentTime = currentTime;
//             this.currentSequence = currentSequence;
//         }
//         public long currentTime;
//         public int currentSequence;
//     }
// }
// 
// Path: fauxflake-core/src/main/java/com/github/rholder/fauxflake/api/Id.java
// public interface Id {
// 
//     /**
//      * Return a unique identifier as a long.
//      */
//     public long asLong();
// 
//     /**
//      * Return a unique identifier as bytes.
//      */
//     public byte[] asBytes();
// 
//     /**
//      * Return a unique identifier as a String.
//      */
//     public String asString();
// }
// 
// Path: fauxflake-core/src/main/java/com/github/rholder/fauxflake/api/IdGenerator.java
// public interface IdGenerator {
// 
//     /**
//      * Return a unique identifier. This should be resilient enough to handle
//      * leap seconds, however, backwards time that occurs when the system time is
//      * updated, as from NTP, will result in an {@link InterruptedException}.
//      *
//      * NOTE: Generating more id's at a time than can be handled may result in
//      * the current thread blocking for maxWait ms to ensure a new timestamp is
//      * available.
//      *
//      * @param maxWait
//      *          the maximum amount of time to wait in ms before giving up
//      * @throws InterruptedException
//      *          thrown when thread is interrupted, the maximum amount of time
//      *          to wait for a new timestamp to become available has expired, or
//      *          backwards time has been detected
//      */
//     public Id generateId(int maxWait) throws InterruptedException;
// }
// 
// Path: fauxflake-core/src/main/java/com/github/rholder/fauxflake/provider/SystemTimeProvider.java
// public class SystemTimeProvider implements TimeProvider {
// 
//     @Override
//     public long getCurrentTime() {
//         return System.currentTimeMillis();
//     }
// }


// Path: fauxflake-core/src/test/java/com/github/rholder/fauxflake/provider/twitter/SnowflakeDecodingUtilsTest.java
import com.github.rholder.fauxflake.DefaultIdGenerator;
import com.github.rholder.fauxflake.api.Id;
import com.github.rholder.fauxflake.api.IdGenerator;
import com.github.rholder.fauxflake.provider.SystemTimeProvider;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.util.Date;

/*
 * Copyright 2012-2014 Ray Holder
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.github.rholder.fauxflake.provider.twitter;



public class SnowflakeDecodingUtilsTest extends SnowflakeDecodingUtils {

    private static final int TEST_MACHINE_ID = 53;
    private IdGenerator idGenerator;

    @Before
    public void before() {
        idGenerator = new DefaultIdGenerator(new SystemTimeProvider(), new SnowflakeEncodingProvider(TEST_MACHINE_ID));
    }

    @Test
    public void decodedValueCheck() {
        // known id
        long id = 278275685817122816L;
        Assert.assertEquals("Timestamps did not match", 1355181068268L, decodeDate(id).getTime());
        Assert.assertEquals("Machine id's did not match", 32, decodeMachineId(id));
        Assert.assertEquals("Sequence numbers did not match", 0, decodeSequence(id));
    }

    @Test
    public void encodedValueCheck() throws InterruptedException {
        Date now = new Date();
        Id id = idGenerator.generateId(5);