local modname = "wechat_random2"
local _M = { _VERSION = '0.0.1' }
_G[modname] = _M

local require    = require
local ffi        = require "ffi"
local ffi_new    = ffi.new
local ffi_str    = ffi.string
local ffi_typeof = ffi.typeof
local C          = ffi.C
local type       = type
local random     = math.random
local randomseed = math.randomseed
local concat     = table.concat
local tostring   = tostring
local pcall      = pcall

ffi.cdef[[
typedef unsigned char u_char;
u_char * ngx_hex_dump(u_char *dst, const u_char *src, size_t len);
int RAND_bytes(u_char *buf, int num);
]]

local ok, new_tab = pcall(require, "table.new")
if not ok then
  new_tab = function () return {} end
end

local alnum  = {
  'A','B','C','D','E','F','G','H','I','J','K','L','M',
  'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
  'a','b','c','d','e','f','g','h','i','j','k','l','m',
  'n','o','p','q','r','s','t','u','v','w','x','y','z',
  '0','1','2','3','4','5','6','7','8','9'
}

local t = ffi_typeof "uint8_t[?]"

local function bytes(len, format)
  local s = ffi_new(t, len)
  C.RAND_bytes(s, len)
  if not s then return nil end
  if format == "hex" then
    local b = ffi_new(t, len * 2)
    C.ngx_hex_dump(b, s, len)
    return ffi_str(b, len * 2), true
  else
    return ffi_str(s, len), true
  end
end

local function seed()
  local a,b,c,d = bytes(4):byte(1, 4)
  return randomseed(a * 0x1000000 + b * 0x10000 + c * 0x100 + d)
end

local function number(min, max, reseed)
  if reseed then seed() end
  if min and max then return random(min, max)
  elseif min     then return random(min)
  else                return random() end
end
_M.number = number

function _M.token(len, chars, sep)
  chars = chars or alnum
  local count
  local token = new_tab(len, 0)
  if type(chars) ~= "table" then
    chars = tostring(chars)
    count = #chars
    local n
    for i=1,len do
      n = number(1, count)
      token[i] = chars:sub(n, n)
    end
  else
    count = #chars
    for i=1,len do token[i] = chars[number(1, count)] end
  end
  return concat(token, sep)
end

seed()

return _M
