package snowflakes

import (
	"errors"
	"sync"
	"time"
)

const (
	counterLen  = 10
	counterMask = -1 ^ (-1 << counterLen)
)

var (
	errNoFuture = errors.New("Start Time cannot be set in the future")
)

// Generator is a fountain for new snowflakes. StartTime must be
// initialized to a past point in time and Instance ID can be any
// positive value or 0.
//
// If any value is not correctly set, new IDs cannot be produced.
type Generator struct {
	StartTime int64
	mutex     *sync.Mutex
	sequence  int32
	now       int64
}

func (g *Generator) IDToUnix(id int) int {
	return (id >> counterLen) + int(g.StartTime)
}

// NewID generates a new, unique snowflake value
//
// Up to 8192 snowflakes per second can be requested
// If exhausted, it blocks and sleeps until a new second
// of unix time starts.
//
// The return value is signed but always positive.
//
// Additionally, the return value is monotonic for a single
// instance and weakly monotonic for many instances.
func (g *Generator) NewID() (int64, error) {
	if g.mutex == nil {
		g.mutex = new(sync.Mutex)
	}
	if g.StartTime > time.Now().Unix() {
		return 0, errNoFuture
	}
	g.mutex.Lock()
	defer g.mutex.Unlock()

	var (
		now   int64
		flake int64
	)
	now = int64(time.Now().Unix())

	if now == g.now {
		g.sequence = (g.sequence + 1) & counterMask
		if g.sequence == 0 {
			for now <= g.now {
				now = int64(time.Now().Unix())
				time.Sleep(time.Microsecond * 100)
			}
		}
	} else {
		g.sequence = 0
	}

	g.now = now

	flake = int64(
		((now - g.StartTime) << counterLen) |
			int64(g.sequence))

	return flake, nil
}
