local assert = assert
local lpeg = require "lpeg"
local stdnse = require "stdnse"
local pairs = pairs
local string = require "string"
local tonumber = tonumber
local rawset = rawset
_ENV = {}
function caseless (literal)
  local caseless = lpeg.Cf((lpeg.P(1) / function (a) return lpeg.S(a:lower()..a:upper()) end)^1, function (a, b) return a * b end)
  return assert(caseless:match(literal))
end
function anywhere (patt)
  return lpeg.P {
    patt + 1 * lpeg.V(1)
  }
end
function localize (grammar)
  return lpeg.P(lpeg.locale(grammar))
end
function split (str, sep)
  return lpeg.P {
    lpeg.V "elem" * (lpeg.V "sep" * lpeg.V "elem")^0,
    elem = lpeg.C((1 - lpeg.V "sep")^0),
    sep = sep,
  } :match(str)
end
function atwordboundary (patt)
  return _ENV.localize {
    patt + lpeg.V "alpha"^0 * (1 - lpeg.V "alpha")^1 * lpeg.V(1)
  }
end
function escaped_quote (quot, esc)
  quot = quot or '"'
  esc = esc or '\\'
  return lpeg.P {
    lpeg.Cs(lpeg.V "quot" * lpeg.Cs((lpeg.V "simple_char" + lpeg.V "noesc" + lpeg.V "unesc")^0) * lpeg.V "quot"),
    quot = lpeg.P(quot)/"",
    esc = lpeg.P(esc),
    simple_char = (lpeg.P(1) - (lpeg.V "quot" + lpeg.V "esc")),
    unesc = (lpeg.V "esc" * lpeg.C( lpeg.V "esc" + lpeg.P(quot) ))/"%1",
    noesc = lpeg.V "esc" * lpeg.V "simple_char"
  }
end
function debug (grammar, printer)
  printer = printer or stdnse.debug1
  for k, p in pairs(grammar) do
    local enter = lpeg.Cmt(lpeg.P(true), function(s, p, ...)
      printer("ENTER %s", k) return p end)
    local leave = lpeg.Cmt(lpeg.P(true), function(s, p, ...)
      printer("LEAVE %s", k) return p end) * (lpeg.P("k") - lpeg.P "k");
    grammar[k] = lpeg.Cmt(enter * p + leave, function(s, p, ...)
      printer("---%s---", k) printer("pos: %d, [%s]", p, s:sub(1, p-1)) return p end)
  end
  return grammar
end
do
  local getquote = escaped_quote()
  local unescape = lpeg.P {
    lpeg.Cs((lpeg.V "simple_char" + lpeg.V "unesc")^0),
    esc = lpeg.P "\\",
    simple_char = lpeg.P(1) - lpeg.V "esc",
    unesc = (lpeg.V "esc" * lpeg.Cs( lpeg.V "specials" + lpeg.V "code" + lpeg.P(1) ))/"%1",
    specials = lpeg.S "trn0" / {t="\t", r="\r", n="\n", ["0"]="\0"},
    code = (lpeg.P "x" * lpeg.C(lpeg.S "0123456789abcdefABCDEF"^-2))/function(c)
    return string.char(tonumber(c,16)) end,
  }
  function get_response (fp, probe)
    fp = string.gsub(fp, "\nSF:", "")
    local i, e = string.find(fp, string.format("%s,%%x+,", probe))
    if i == nil then return nil end
    return unescape:match(getquote:match(fp, e+1))
  end
  local svfp_parser = lpeg.P ({
      anywhere("%r(") * lpeg.Cf(lpeg.Ct("") * (lpeg.V "probematch" * lpeg.P(")%r(")^-1)^1, rawset),
      probematch = lpeg.Cg(lpeg.C((lpeg.P(1) - ",")^1) * "," * (lpeg.R("09") + lpeg.R("AF"))^1 * "," * lpeg.Cs(getquote/function(q) return unescape:match(q) end)),
    })
  function parse_fp (fp)
    fp = string.gsub(fp, "\nSF:", "")
    return svfp_parser:match(fp)
  end
end
return _ENV
