unit uDefaultIdGenerator;

interface

uses
  uIIdGenerator, uISnowWorker, uIdGeneratorOptions, System.DateUtils, System.SysUtils;

type
  TDefaultIdGenerator = class(TInterfacedObject, IIdGenerator)
  private
    SnowWorker: ISnowWorker;
  public
    constructor Create(options: TIdGeneratorOptions); overload;
    function NewLong(): Int64;
  end;

implementation

uses
  uSnowWorkerM1, uSnowWorkerM2, uSnowWorkerM3;

{ TDefaultIdGenerator }

function GetMillisecondTimeStamp(ET: TDateTime): Int64;
var
  ST: TDateTime;
begin
  ST := EncodeDateTime(1970, 1, 1, 0, 0, 0, 0);
  Result := MilliSecondsBetween(ET, ST) - 28800000; // 8*60*60*1000;
end;

constructor TDefaultIdGenerator.Create(options: TIdGeneratorOptions);
var
  MaxLength, MaxWorkerIdNumber, MaxDataCenterIdNumber, MaxSeqNumber: Integer;
begin
  if (options = nil) then
  begin
    raise Exception.Create('options error.');
  end;

  // 1.BaseTime
  if (options.BaseTime < GetMillisecondTimeStamp(IncYear(Now(), -50))) or
    (options.BaseTime > GetMillisecondTimeStamp(Now())) then
  begin
    raise Exception.Create('BaseTime error.');
  end;

  // 2.WorkerIdBitLength
  if (options.TimestampType = 0) then
    MaxLength := 22
  else
    MaxLength := 31; // （秒级时间戳时放大到31位）
  if (options.WorkerIdBitLength <= 0) then
  begin
    raise Exception.Create('WorkerIdBitLength error.(range:[1, 21])');
  end;
  if (options.DataCenterIdBitLength + options.WorkerIdBitLength + options.SeqBitLength > MaxLength) then
  begin
    raise Exception.Create('error：DataCenterIdBitLength + WorkerIdBitLength + SeqBitLength <= ' + IntToStr(MaxLength));
  end;

  // 3.WorkerId
  MaxWorkerIdNumber := (1 shl options.WorkerIdBitLength) - 1;
  if (MaxWorkerIdNumber = 0) then
  begin
    MaxWorkerIdNumber := 63;
  end;
  if ((options.WorkerId < 0) or (options.WorkerId > MaxWorkerIdNumber)) then
  begin
    raise Exception.Create('WorkerId error. (range:[0, ' + IntToStr(MaxWorkerIdNumber) + ']');
  end;

  MaxDataCenterIdNumber := (1 shl options.DataCenterIdBitLength) - 1;
  if (options.DataCenterId < 0) or (options.DataCenterId > MaxDataCenterIdNumber) then
  begin
    raise Exception.Create('DataCenterId error. (range:[0, ' + IntToStr(MaxDataCenterIdNumber) + ']');
  end;

  // 4.SeqBitLength
  if ((options.SeqBitLength < 2) or (options.SeqBitLength > 21)) then
  begin
    raise Exception.Create('SeqBitLength error. (range:[2, 21])');
  end;

  // 5.MaxSeqNumber
  MaxSeqNumber := (1 shl options.SeqBitLength) - 1;
  if (MaxSeqNumber = 0) then
  begin
    MaxSeqNumber := 63;
  end;
  if ((options.MaxSeqNumber < 0) or (options.MaxSeqNumber > MaxSeqNumber)) then
  begin
    raise Exception.Create('MaxSeqNumber error. (range:[1, ' + IntToStr(MaxSeqNumber) + ']');
  end;

  // 6.MinSeqNumber
  if ((options.MinSeqNumber < 5) or (options.MinSeqNumber > MaxSeqNumber)) then
  begin
    raise Exception.Create('MinSeqNumber error. (range:[5, ' + IntToStr(MaxSeqNumber) + ']');
  end;

  // 7.TopOverCostCount
  if ((options.TopOverCostCount < 0) or (options.TopOverCostCount > 10000)) then
  begin
    raise Exception.Create('TopOverCostCount error. (range:[0, 10000]');
  end;

  case (options.Method) of
    2:
      begin
        SnowWorker := TSnowWorkerM2.Create(options);
      end;
  else
    begin
      if ((options.DataCenterIdBitLength = 0) and (options.TimestampType = 0)) then
      begin
        SnowWorker := TSnowWorkerM1.Create(options);
      end
      else
      begin
        SnowWorker := TSnowWorkerM3.Create(options);
      end;
    end;
  end;

  if (options.Method <> 2) then
  begin
    Sleep(500);
  end;
end;

function TDefaultIdGenerator.NewLong(): Int64;
begin
  Result := SnowWorker.NextId();
end;

end.
