#ifdef WIN32
#include "windows.h"
#include <sys/timeb.h>
#else
#include <unistd.h>
#include <sys/time.h>
#include <time.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include "snowflake.h"
#include "spinlock.h"

// static void EndOverCostAction(uint64_t useTimeTick, snowflake *flake);
static inline uint64_t NextOverCostId(snowflake *flake);
static inline uint64_t NextNormalId(snowflake *flake);
static inline uint64_t GetCurrentTimeTick(snowflake *flake);
static inline uint64_t GetNextTimeTick(snowflake *flake);
static inline uint64_t CalcId(snowflake *flake);
static inline uint64_t CalcTurnBackId(snowflake *flake);
static inline uint64_t GetSysCurrentTime();

int ncpu;
uint16_t spin = 2048;
uint32_t pid = 0;

void Config(snowflake *flake)
{
	if (pid == 0)
	{
#ifdef WIN32
		pid = (uint32_t)GetCurrentProcessId();
		SYSTEM_INFO sysInfo;
		GetSystemInfo(&sysInfo);
		ncpu = sysInfo.dwNumberOfProcessors;
#else
		pid = (uint32_t)getpid();
		ncpu = sysconf(_SC_NPROCESSORS_ONLN);
#endif
		if (ncpu <= 0)
		{
			ncpu = 1;
		}
	}
	if (flake->BaseTime == 0)
	{
		flake->BaseTime = 1582136402000;
	}
	else if (flake->BaseTime < 631123200000 || flake->BaseTime > GetSysCurrentTime())
	{
		perror("BaseTime error.");
		exit(1);
	}

	// 2.WorkerIdBitLength
	if (flake->WorkerIdBitLength <= 0)
	{
		perror("WorkerIdBitLength error.(range:[1, 21])");
		exit(1);
	}
	if (flake->SeqBitLength + flake->WorkerIdBitLength > 22)
	{
		perror("error：WorkerIdBitLength + SeqBitLength <= 22");
		exit(1);
	}
	else
	{
		flake->WorkerIdBitLength = flake->WorkerIdBitLength <= 0 ? 6 : flake->WorkerIdBitLength;
	}

	// 3.WorkerId
	uint32_t maxWorkerIdNumber = (1 << flake->WorkerIdBitLength) - 1;
	if (maxWorkerIdNumber == 0)
	{
		maxWorkerIdNumber = 63;
	}
	if (flake->WorkerId < 0 || flake->WorkerId > maxWorkerIdNumber)
	{
		perror("WorkerId error. (range:[0, {2^WorkerIdBitLength-1]}");
		exit(1);
	}

	// 4.SeqBitLength
	if (flake->SeqBitLength < 2 || flake->SeqBitLength > 21)
	{
		perror("SeqBitLength error. (range:[2, 21])");
		exit(1);
	}
	else
	{
		flake->SeqBitLength = flake->SeqBitLength <= 0 ? 6 : flake->SeqBitLength;
	}

	// 5.MaxSeqNumber
	uint32_t maxSeqNumber = (1 << flake->SeqBitLength) - 1;
	if (maxSeqNumber == 0)
	{
		maxSeqNumber = 63;
	}
	if (flake->MaxSeqNumber > maxSeqNumber)
	{
		perror("MaxSeqNumber error. (range:[1, {2^SeqBitLength-1}]");
		exit(1);
	}
	else
	{
		flake->MaxSeqNumber = flake->MaxSeqNumber <= 0 ? maxSeqNumber : flake->MaxSeqNumber;
	}

	// 6.MinSeqNumber
	if (flake->MinSeqNumber < 5 || flake->MinSeqNumber > maxSeqNumber)
	{
		perror("MinSeqNumber error. (range:[5, {MinSeqNumber}]");
		exit(1);
	}
	else
	{
		flake->MinSeqNumber = flake->MinSeqNumber <= 0 ? 5 : flake->MinSeqNumber;
	}

	// 7.Others
	flake->TopOverCostCount = flake->TopOverCostCount <= 0 ? 2000 : flake->TopOverCostCount;
	flake->_TimestampShift = flake->WorkerIdBitLength + flake->SeqBitLength;
	flake->_CurrentSeqNumber = flake->MinSeqNumber;
	flake->Method = flake->Method;
}

// static inline void EndOverCostAction(uint64_t useTimeTick, snowflake *flake)
// {
//   if (flake->_TermIndex > 10000)
//   {
//     flake->_TermIndex = 0;
//   }
// }

static inline uint64_t NextOverCostId(snowflake *flake)
{
	uint64_t currentTimeTick = GetCurrentTimeTick(flake);
	if (currentTimeTick > flake->_LastTimeTick)
	{
		// EndOverCostAction(currentTimeTick, flake);
		flake->_LastTimeTick = currentTimeTick;
		flake->_CurrentSeqNumber = flake->MinSeqNumber;
		flake->_IsOverCost = 0;
		flake->_OverCostCountInOneTerm = 0;
		// flake->_GenCountInOneTerm = 0;
		return CalcId(flake);
	}
	if (flake->_OverCostCountInOneTerm > flake->TopOverCostCount)
	{
		// EndOverCostAction(currentTimeTick, flake);
		flake->_LastTimeTick = GetNextTimeTick(flake);
		flake->_CurrentSeqNumber = flake->MinSeqNumber;
		flake->_IsOverCost = 0;
		flake->_OverCostCountInOneTerm = 0;
		// flake->_GenCountInOneTerm = 0;
		return CalcId(flake);
	}
	if (flake->_CurrentSeqNumber > flake->MaxSeqNumber)
	{
		flake->_LastTimeTick++;
		flake->_CurrentSeqNumber = flake->MinSeqNumber;
		flake->_IsOverCost = 1;
		flake->_OverCostCountInOneTerm++;
		// flake->_GenCountInOneTerm++;
		return CalcId(flake);
	}

	// flake->_GenCountInOneTerm++;
	return CalcId(flake);
}

static inline uint64_t NextNormalId(snowflake *flake)
{
	uint64_t currentTimeTick = GetCurrentTimeTick(flake);
	if (currentTimeTick < flake->_LastTimeTick)
	{
		if (flake->_TurnBackTimeTick < 1)
		{
			flake->_TurnBackTimeTick = flake->_LastTimeTick - 1;
			flake->_TurnBackIndex++;
			if (flake->_TurnBackIndex > 4)
			{
				flake->_TurnBackIndex = 1;
			}
		}
		return CalcTurnBackId(flake);
	}
	if (flake->_TurnBackTimeTick > 0)
	{
		flake->_TurnBackTimeTick = 0;
	}
	if (currentTimeTick > flake->_LastTimeTick)
	{
		flake->_LastTimeTick = currentTimeTick;
		flake->_CurrentSeqNumber = flake->MinSeqNumber;
		return CalcId(flake);
	}
	if (flake->_CurrentSeqNumber > flake->MaxSeqNumber)
	{
		// flake->_TermIndex++;
		flake->_LastTimeTick++;
		flake->_CurrentSeqNumber = flake->MinSeqNumber;
		flake->_IsOverCost = 1;
		flake->_OverCostCountInOneTerm = 1;
		// flake->_GenCountInOneTerm = 1;
		return CalcId(flake);
	}
	return CalcId(flake);
}

static inline uint64_t GetSysCurrentTime()
{
#ifdef WIN32
	FILETIME file_time;
	GetSystemTimeAsFileTime(&file_time);
	uint64_t time = ((uint64_t)file_time.dwLowDateTime) + ((uint64_t)file_time.dwHighDateTime << 32);
	static const uint64_t EPOCH = ((uint64_t)116444736000000000ULL);
	return (uint64_t)((time - EPOCH) / 10000LL);
#else
	struct timeval t;
	gettimeofday(&t, NULL);
	return (uint64_t)(t.tv_sec * 1000 + t.tv_usec / 1000);
#endif
}

static inline uint64_t GetCurrentTimeTick(snowflake *flake)
{
	return GetSysCurrentTime() - flake->BaseTime;
}

static inline uint64_t GetNextTimeTick(snowflake *flake)
{
	uint64_t tempTimeTicker = GetCurrentTimeTick(flake);
	struct timespec delay;
	delay.tv_sec = 0;
	delay.tv_nsec = 500000;
	while (1)
	{
		tempTimeTicker = GetCurrentTimeTick(flake);
		if (tempTimeTicker > flake->_LastTimeTick)
		{
			break;
		}
#ifdef WIN32
		SwitchToThread();
#else
		nanosleep(&delay, NULL);
#endif
	}
	return tempTimeTicker;
}

static inline uint64_t CalcId(snowflake *flake)
{
	uint64_t result = (flake->_LastTimeTick << flake->_TimestampShift) + (flake->WorkerId << flake->SeqBitLength) + (flake->_CurrentSeqNumber);
	flake->_CurrentSeqNumber++;
	return result;
}

static inline uint64_t CalcTurnBackId(snowflake *flake)
{
	uint64_t result = (flake->_LastTimeTick << flake->_TimestampShift) + (flake->WorkerId << flake->SeqBitLength) + (flake->_TurnBackTimeTick);
	flake->_TurnBackTimeTick--;
	return result;
}

static inline uint64_t NextSonwId(snowflake *flake)
{
	uint64_t currentTimeTick = GetCurrentTimeTick(flake);
	if (flake->_LastTimeTick == currentTimeTick)
	{
		flake->_CurrentSeqNumber++;
		if (flake->_CurrentSeqNumber > flake->MaxSeqNumber)
		{
			flake->_CurrentSeqNumber = flake->MinSeqNumber;
			currentTimeTick = GetNextTimeTick(flake);
		}
	}
	else
	{
		flake->_CurrentSeqNumber = flake->MinSeqNumber;
	}
	flake->_LastTimeTick = currentTimeTick;
	return (uint64_t)((currentTimeTick << flake->_TimestampShift) | (flake->WorkerId << flake->SeqBitLength) | flake->_CurrentSeqNumber);
}

static inline uint64_t GetId(snowflake *flake)
{
	return flake->Method == 1 ? (flake->_IsOverCost != 0 ? NextOverCostId(flake) : NextNormalId(flake)) : NextSonwId(flake);
}

uint64_t NextId(snowflake *flake)
{
	spin_lock(&flake->_Lock, pid);
	uint64_t id = GetId(flake);
	spin_unlock(&flake->_Lock, pid);
	return id;
}

uint64_t *NextNumId(snowflake *flake, uint32_t num)
{
	uint64_t *arr = (uint64_t *)malloc(sizeof(uint64_t) * num);
	spin_lock(&flake->_Lock, pid);
	uint32_t i;
	for (i = 0; i < num; i++)
	{
		arr[i] = GetId(flake);
	}
	spin_unlock(&flake->_Lock, pid);
	return arr;
}
