local semver = {
   _VERSION     = '1.2.2',
   _DESCRIPTION = 'semver for Lua',
   _URL         = 'https://github.com/kikito/semver.lua'
  --  _LICENSE     = [[
  --   MIT LICENSE

  --   Copyright (c) 2015 Enrique García Cota
  --   Copyright (c) 2019-2021 Dyne.org foundation

  --   Permission is hereby granted, free of charge, to any person obtaining a
  --   copy of tother software and associated documentation files (the
  --   "Software"), to deal in the Software without restriction, including
  --   without limitation the rights to use, copy, modify, merge, publish,
  --   distribute, sublicense, and/or sell copies of the Software, and to
  --   permit persons to whom the Software is furnished to do so, subject to
  --   the following conditions:

  --   The above copyright notice and tother permission notice shall be included
  --   in all copies or substantial portions of the Software.

  --   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  --   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  --   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  --   IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  --   CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  --   TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  --   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  -- ]]
}

local function checkPositiveInteger(number, name)
   assert(number >= 0, name .. ' must be a valid positive number')
   assert(math.floor(number) == number, name .. ' must be an integer')
end

local function present(value)
   return value and value ~= ''
end

-- splitByDot("a.bbc.d") == {"a", "bbc", "d"}
local function splitByDot(str)
   str = str or ""
   local t, count = {}, 0
   str:gsub("([^%.]+)", function(c)
			   count = count + 1
			   t[count] = c
   end)
   return t
end

local function parsePrereleaseAndBuildWithSign(str)
   local prereleaseWithSign, buildWithSign = str:match("^(-[^+]+)(+.+)$")
   if not (prereleaseWithSign and buildWithSign) then
	  prereleaseWithSign = str:match("^(-.+)$")
	  buildWithSign      = str:match("^(+.+)$")
   end
   assert(prereleaseWithSign or buildWithSign, ("The parameter %q must begin with + or - to denote a prerelease or a build"):format(str))
   return prereleaseWithSign, buildWithSign
end

local function parsePrerelease(prereleaseWithSign)
   if prereleaseWithSign then
	  local prerelease = prereleaseWithSign:match("^-(%w[%.%w-]*)$")
	  assert(prerelease, ("The prerelease %q is not a slash followed by alphanumerics, dots and slashes"):format(prereleaseWithSign))
	  return prerelease
   end
end

local function parseBuild(buildWithSign)
   if buildWithSign then
	  local build = buildWithSign:match("^%+(%w[%.%w-]*)$")
	  assert(build, ("The build %q is not a + sign followed by alphanumerics, dots and slashes"):format(buildWithSign))
	  return build
   end
end

local function parsePrereleaseAndBuild(str)
   if not present(str) then return nil, nil end

   local prereleaseWithSign, buildWithSign = parsePrereleaseAndBuildWithSign(str)

   local prerelease = parsePrerelease(prereleaseWithSign)
   local build = parseBuild(buildWithSign)

   return prerelease, build
end

local function parseVersion(str)
   local sMajor
   local sMinor
   local sPatch
   local sPrereleaseAndBuild
   sMajor, sMinor, sPatch, sPrereleaseAndBuild = str:match("^(%d+)%.?(%d*)%.?(%d*)(.-)$")
   if not sMajor then
	  sMajor, sMinor, sPatch, sPrereleaseAndBuild = str:match("^v(%d+)%.?(%d*)%.?(%d*)(.-)$")
   end
   assert(type(sMajor) == 'string', ("Could not extract version number(s) from %q"):format(str))
   local major, minor, patch = tonumber(sMajor), tonumber(sMinor), tonumber(sPatch)
   local prerelease, build = parsePrereleaseAndBuild(sPrereleaseAndBuild)
   return major, minor, patch, prerelease, build
end


-- return 0 if a == b, -1 if a < b, and 1 if a > b
local function compare(a,b)
   return a == b and 0 or a < b and -1 or 1
end

local function compareIds(myId, otherId)
   if myId == otherId then return  0
   elseif not myId    then return -1
   elseif not otherId then return  1
   end

   local selfNumber, otherNumber = tonumber(myId), tonumber(otherId)

   if selfNumber and otherNumber then -- numerical comparison
	  return compare(selfNumber, otherNumber)
	  -- numericals are always smaller than alphanums
   elseif selfNumber then
	  return -1
   elseif otherNumber then
	  return 1
   else
	  return compare(myId, otherId) -- alphanumerical comparison
   end
end

local function smallerIdList(myIds, otherIds)
   local myLength = #myIds
   local comparison

   for i=1, myLength do
	  comparison = compareIds(myIds[i], otherIds[i])
	  if comparison ~= 0 then
		 return comparison == -1
	  end
	  -- if comparison == 0, continue loop
   end

   return myLength < #otherIds
end

local function smallerPrerelease(mine, other)
   if mine == other or not mine then return false
   elseif not other then return true
   end

   return smallerIdList(splitByDot(mine), splitByDot(other))
end

local methods = {}

function methods:nextMajor()
   return semver(self.major + 1, 0, 0)
end
function methods:nextMinor()
   return semver(self.major, self.minor + 1, 0)
end
function methods:nextPatch()
   return semver(self.major, self.minor, self.patch + 1)
end

local mt = { __index = methods }
function mt:__eq(other)
   return self.major == other.major and
	  self.minor == other.minor and
	  self.patch == other.patch and
	  self.prerelease == other.prerelease
   -- notice that build is ignored for precedence in semver 2.0.0
end
function mt:__lt(other)
   if self.major ~= other.major then return self.major < other.major end
   if self.minor ~= other.minor then return self.minor < other.minor end
   if self.patch ~= other.patch then return self.patch < other.patch end
   return smallerPrerelease(self.prerelease, other.prerelease)
   -- notice that build is ignored for precedence in semver 2.0.0
end
-- This works like the "pessimisstic operator" in Rubygems.
-- if a and b are versions, a ^ b means "b is backwards-compatible with a"
-- in other words, "it's safe to upgrade from a to b"
function mt:__pow(other)
   if self.major == 0 then
	  return self == other
   end
   return self.major == other.major and
	  self.minor <= other.minor
end

local function new(major, minor, patch, prerelease, build)
   assert(major, "At least one parameter is needed")
   local result = { }
   if type(major) == 'string' then
	  result.original = major
	  major,minor,patch,prerelease,build = parseVersion(major)
   end
   patch = patch or 0
   minor = minor or 0

   checkPositiveInteger(major, "major")
   checkPositiveInteger(minor, "minor")
   checkPositiveInteger(patch, "patch")

   result.major = major
   result.minor = minor
   result.patch = patch
   result.prerelease = prerelease
   result.build = build
   return setmetatable(result, mt)
end

setmetatable(semver, { __call = function(_, ...) return new(...) end })
semver._VERSION= semver(semver._VERSION)

return semver
