--[[
Copyright (c) 2012-2013 Kaarle Ritvanen
See LICENSE file for license details
--]]

local M = {}

local raise = require('acf2.error').raise
local Union = require('acf2.model.combination').Union

local fld = require('acf2.model.field')
local String = fld.String

local object = require('acf2.object')
local class = object.class
local super = object.super

local update = require('acf2.util').update


local stringy = require('stringy')


M.IPv4Address = class(String)

function M.IPv4Address:validate(context, value)
   super(self, M.IPv4Address):validate(context, value)
   local function test(...)
      if #{...} ~= 4 then return true end
      for _, octet in ipairs{...} do
	 if tonumber(octet) > 255 then return true end
      end
   end
   if test(value:match('^(%d+)%.(%d+)%.(%d+)%.(%d+)$')) then
      raise(context.path, 'Invalid IPv4 address')
   end
end


M.IPv6Address = class(String)

function M.IPv6Address:validate(context, value)
   super(self, M.IPv6Address):validate(context, value)

   local function invalid() raise(context.path, 'Invalid IPv6 address') end

   if value == '' then invalid() end

   local comps = stringy.split(value, ':')
   if #comps < 3 then invalid() end

   local function collapse(i, ofs)
      if comps[i] > '' then return end
      if comps[i + ofs] > '' then invalid() end
      table.remove(comps, i)
   end
   collapse(1, 1)
   collapse(#comps, -1)
   if #comps > 8 then invalid() end

   local short = false
   for _, comp in ipairs(comps) do
      if comp == '' then
	 if short then invalid() end
	 short = true
      elseif not comp:match('^%x%x?%x?%x?$') then invalid() end
   end
   if (
      short and #comps == 3 and comps[2] == ''
   ) or (not short and #comps < 8) then
      invalid()
   end
end


M.IPAddress = class(Union)

function M.IPAddress:init(params)
   super(self, M.IPAddress):init(
      update(
	 params,
	 {types={M.IPv4Address, M.IPv6Address}, error='Invalid IP address'}
      )
   )
end


M.Port = class(fld.Integer)

function M.Port:validate(context, value)
   super(self, M.Port):validate(context, value)
   if value < 0 or value > 65535 then raise(context.path, 'Invalid port') end
end


return M
