mtable = { "ינואר", "פברואר", "מרץ", "אפריל", "מאי", "יוני",
	"יולי", "אוגוסט", "ספטמבר", "אוקטובר", "נובמבר", "דצמבר", }

monthtable = {
	["תשרי"] = 7,
	["חשוון"] = 8,
	["כסלו"] = 9,
	["טבת"] = 10,
	["שבט"] = 11,
	["אדר א'"] = 12,
	["אדר ב'"] = 13,
	["אדר"] = 13,
	["ניסן"] = 1,
	["אייר"] = 2,
	["סיוון"] = 3,
	["תמוז"] = 4,
	["אב"] = 5,
	["אלול"] = 6,
}

typomonthtable = {
	["כסליו"] = "כסלו",
	["חשון"] = "חשוון",
	["סיון"] = "סיוון",
	["מרחשון"] = "חשוון",
	["מרחשוון"] = "חשוון",
	["מר-חשוון"] = "חשוון",
	["מנחם אב"] = "אב",
	["מנחם-אב"] = "אב",
	["שבט (חודש)|שבט"] = "שבט",
	["שבט (חודש)"] = "שבט"
}

lastletters = { ["כ"] = "ך", ["מ"] = "ם", ["נ"] = "ן", ["פ"] = "ף", ["צ"] = "ץ" }

local langObject = mw.getContentLanguage()

function sub1(str, from, to)
	return mw.ustring.sub(str, from, to)
end

function find1(str, what, where)
	return mw.ustring.find(str, what, where, true) or 0
end

function len1(s)
	if (s == nil) then
		return 0
	end
	return mw.ustring.len(s)
end

function hebnum(s)
	numberstable = {
		["א"] = 1,
		["ב"] = 2,
		["ג"] = 3,
		["ד"] = 4,
		["ה"] = 5,
		["ו"] = 6,
		["ז"] = 7,
		["ח"] = 8,
		["ט"] = 9,
		["י"] = 10,
		["כ"] = 20,
		["ך"] = 20,
		["ל"] = 30,
		["מ"] = 40,
		["ם"] = 40,
		["נ"] = 50,
		["ן"] = 50,
		["ס"] = 60,
		["ע"] = 70,
		["פ"] = 80,
		["ף"] = 80,
		["צ"] = 90,
		["ץ"] = 90,
		["ק"] = 100,
		["ר"] = 200,
		["ש"] = 300,
		["ת"] = 400,
		["'"] = 0,
		["\""] = 0,
	}
	local ans = 0
	local str = s
	while len1(str) > 0 do
		ans = ans + numberstable[sub1(str, 1, 1)]
		str = sub1(str, 2, len1(str))
	end
	return ans
end

function mindate(fix)
	local indenttable = { 176, 206, 235, 265, 294, 324, 0, 30, 59, 88, 117, 147, 147 }
	return same("28-08-" .. takeyear(fix[3]) .. " + " .. (indenttable[monthtable[sub1(fix[2], 2)]]
		+ hebnum(fix[1]) - 1) .. " days")
end

function takeyear(year)
	local new
	if 0 < find1(year, "אלפים") then
		new = hebnum(sub1(year, 1, 2)) * 1000
	else
		new = hebnum(sub1(year, 3, len1(year))) + 1000
			* hebnum(sub1((year), 1, 2))
	end
	return frame:callParserFunction("padleft", { new - 3761, 4 })
end

function exacttdate(hdate, mindate, format)
	local count = 0
	local ans
	local greg
	while (count < 82) do
		ans = mindate .. " + " .. count .. " days"
		if (remove(from(ans)) == hdate) then
			greg = same(ans)
			local diffs = diff(greg)
			if diffs < 0 then diffs = 0 end
			greg = same(greg .. " - " .. diffs .. "days")
			checkcasp(greg)
			if makeformat then
				return langObject:formatDate(makeformat, greg)
			end
			return format(greg)
		end
		count = count + 1
	end
	error("Date not found", 0)
end

function exacttdate1(hyear)
	local wyear = frame:callParserFunction("padleft", { hebnum(sub1(hyear, 3, len1(hyear))) + 1000
	* hebnum(sub1(hyear, 1, 2)) - 3761, 4 })
	checkcasp("01-01-" .. wyear)
	return wyear .. "-" .. frame:callParserFunction("padleft", { wyear + 1, 4 })
end

function opposite(gdate, format)
	if samesame == true then
		samesame = false
		return format(gdate)
	end
end

function from(gdate)
	local ans = opposite(gdate, to)
	if ans ~= nil then
		return ans
	end
	if frame.args["links"] == "yes" then
		return "[[" .. langObject:formatDate("xhxjj xjx", gdate) .. "]] [["
			.. langObject:formatDate("xhxjY", gdate) .. "]]"
	end
	return langObject:formatDate("xhxjj xjx xhxjY", gdate)
end

function frommonth(gdate)
	local ans = opposite(gdate, tomonth)
	if ans ~= nil then
		return ans
	end
	if frame.args["links"] == "yes" then
		return "[[" .. langObject:formatDate("xjF", gdate) .. "]] [["
			.. langObject:formatDate("xhxjY", gdate) .. "]]"
	end
	return langObject:formatDate("xjF xhxjY", gdate)
end

function fromonlymonth(gdate)
	local ans = opposite(gdate, toonlymonth)
	if ans ~= nil then
		return ans
	end
	if frame.args["links"] == "yes" then
		return "[[" .. langObject:formatDate("xjF", gdate) .. "]]"
	end
	return langObject:formatDate("xjF", gdate)
end

function fromyear(gdate)
	local ans = opposite(gdate, toyear)
	if ans ~= nil then
		return ans
	end
	if frame.args["links"] == "yes" then
		return "[[" .. langObject:formatDate("xhxjY", gdate) .. "]]"
	end
	return langObject:formatDate("xhxjY", gdate)
end

function same(gdate)
	return langObject:formatDate("d-m-Y", gdate)
end

function makeyear(gdate)
	local year = langObject:formatDate("Y", gdate)
	if tonumber(year) < 1000 and which
	then
		year = sub1(year, 2)
	end
	return year
end

function to(gdate)
	local year = makeyear(gdate)
	if frame.args["links"] == "yes" then
		return "[[" .. langObject:formatDate("j בF", gdate) .. "]] [["
			.. year .. "]]"
	end
	return langObject:formatDate("j בF", gdate) .. " " .. year
end

function tomonth(gdate)
	local year = makeyear(gdate)
	if frame.args["links"] == "yes" then
		return "[[" .. langObject:formatDate("F", gdate) .. "]] [["
			.. year .. "]]"
	end
	return langObject:formatDate("F", gdate) .. " " .. year
end

function toonlymonth(gdate)
	if frame.args["links"] == "yes" then
		return "[[" .. langObject:formatDate("F", gdate) .. "]]"
	end
	return langObject:formatDate("F", gdate)
end

function toyear(gdate)
	local year = makeyear(gdate)
	if frame.args["links"] == "yes" then
		return "[[" .. year .. "]]"
	end
	return year
end

function fixmonth(hdate)
	local point
	local max = 0
	local place = 0
	for m, mm in pairs(typomonthtable) do
		point = find1(hdate, m)
		if point > 0 and max < len1(m) then
			max = len1(m)
			place = m
		end
	end
	if max > 0 then
		point = find1(hdate, place)
		return sub1(hdate, 1, point - 1) .. typomonthtable[place] .. sub1(hdate, point + len1(place))
	end
	return hdate
end

function fixdate(hdate1)
	local hdate = fixmonth(hdate1)
	if 1 == find1(hdate, "ראש חודש") then
		hdate = "א' ב" .. sub1(hdate, 10)
	end
	local h1, h2, h3 = parts(hdate)
	--[=[
	local s1 = find1(hdate, " ")
	local s2 = find1(hdate, " ", s1 + 1)
	local s3 = find1(hdate, " ", s2 + 1)
	if (s3 > 0) then
		s2 = s3 end
	h1 = sub1(hdate, 1, s1 - 1)
	h2 = sub1(hdate, s1 + 1, s2 - 1)
	h3 = sub1(hdate, s2 + 1, len1(hdate))
]=]
	if (len1(h3) > 1 and sub1(h3, 2, 2) ~= "'") then
		h3 = "ה'" .. h3
	end
	--[=[
	for m, mm in pairs(typomonthtable) do
		if h2 == "ב" .. m
		then h2 = "ב" .. mm end
	end
]=]
	for y, yy in pairs(lastletters) do
		if sub1(h3, len1(h3)) == y then
			h3 = sub1(h3, 1, len1(h3) - 1) .. yy
		end
	end
	return { h1, h2, h3, h1 .. " " .. h2 .. " " .. h3 }
end

function fixerror(message)
	if message == "---" then
		return ""
	end
	return frame:preprocess(message)
end

function aserror(message)
	error(message .. "[[קטגוריה:דפים עם שגיאות בתאריך]]", 0)
end

function takenumbers(wdate)
	local s1 = find1(wdate, " ")
	local s2 = find1(wdate, " ", s1 + 1)
	local h1 = sub1(wdate, 1, s1 - 1)
	local h2 = sub1(wdate, s1 + 1, s2 - 1)
	local h3 = sub1(wdate, s2 + 1, len1(wdate))
	for f, ff in pairs(mtable) do
		if "ב" .. ff == h2 then
			return h1 .. "-" .. frame:callParserFunction("padleft", { f, 2 })
				.. "-" .. frame:callParserFunction("padleft", { h3, 4 })
		end
	end
end

function numtonums(wdate)
	local s1 = find1(wdate, "-")
	local s2 = find1(wdate, "-", s1 + 1)
	return { tonumber(sub1(wdate, 1, s1 - 1)),
		tonumber(sub1(wdate, s1 + 1, s2 - 1)),
		tonumber(sub1(wdate, s2 + 1, len1(wdate))) }
end

function diff(gdate)
	local ans
	local wdate = numtonums(gdate)
	local wday = wdate[1]
	local wmonth = wdate[2]
	local wyear = wdate[3]
	if wyear > 1582 or (wyear == 1582 and (wmonth > 10 or (wmonth == 10 and wday > 14))) then
		ans = 0
	else
		local ytable = { 200, 300, 500, 600, 700, 900, 1000, 1100, 1300, 1400, 1500 }
		ans = 10
		if wmonth < 3 then
			wyear = wyear - 1
		end
		for y, yy in pairs(ytable) do
			if yy > wyear then
				ans = ans - 1
			end
		end
	end
	return ans
end

function remove(dateStr) -- Thanks to Eran
	dateStr = mw.ustring.gsub(dateStr, "[%[%]]", "")
	dateStr = mw.ustring.gsub(dateStr, "(\127UNIQ[^\127]+QINU\127)", "")
	dateStr = mw.ustring.gsub(dateStr, "&rlm;", "")
	dateStr = mw.ustring.gsub(dateStr, "&lrm;", "")
	return dateStr
end

function checkcasp(wdate)
	local wnumbers = numtonums(wdate)
	if wnumbers[3] < 360 or wnumbers[3] > 4357 or
		(wnumbers[3] == 1582 and wnumbers[2] == 10 and wnumbers[1] > 4 and wnumbers[1] < 15)
	then
		error("The date is not in the allowed casp", 0)
	end
end

function fromhebrewyear(hyear)
	ans = fromhebrewdate("א' בניסן " .. hyear, toyear)
	if frame.args["exact"] ~= "כן" then
		ans = ans .. "-" .. fromhebrewdate("א' בתשרי " .. hyear, toyear)
	end
	return ans
end

function fromhebrewmonth(hdate)
	if frame.args["exact"] == "כן" then
		return fromhebrewdate('ט"ו ב' .. hdate, tomonth)
	end
	local date1 = fromhebrewdate("א' ב" .. hdate, same)
	local month1 = toonlymonth(date1)
	local year1 = toyear(date1)
	local date2 = same(date1 .. " + 30 days")
	date2 = frommonth(date2)
	date2 = fromhebrewdate("א' ב" .. date2, same)
	date2 = same(date2 .. " - 1 days")
	local month2 = toonlymonth(date2)
	local year2 = toyear(date2)
	if year1 ~= year2 then
		return month1 .. " " .. year1 .. " - " .. month2 .. " " .. year2
	elseif month1 ~= month2 then
		return month1 .. "-" .. month2 .. " " .. year1
	else
		return month1 .. " " .. year1
	end
end

function fromhebrewdate(rem, format)
	local fix = fixdate(remove(rem))
	return exacttdate(fix[4], mindate(fix), format)
end

function fromhebrew1(hdate)
	local ht, rem = findhdatetype(fixmonth(hdate))
	if ht == "dmy" then
		return ifwhich(fromhebrewdate(rem, to))
	elseif ht == "my" then
		return ifwhich(fromhebrewmonth(rem))
	elseif ht == "y" then
		return ifwhich(fromhebrewyear(rem))
	else
		error("Wrong type: " .. ht, 0)
	end
end

function findhdatetype(hdate)
	local rem = mw.text.trim(remove(hdate))
	local day, month, year = parts(hdate)
	if day ~= nil then
		return "dmy", rem
	end
	if month ~= nil then
		return "my", rem
	end
	return "y", rem
	--[=[
	for m, mm in pairs(monthtable) do
		if find1(rem, m) == 1 then
			return "my", rem end
	end
	if find1(rem, " ") == 0 then
		return "y", rem end
	return "dmy", rem
]=]
end

function fromhebrew(f)
	which = true
	return fromhebrewandverify(f)
end

function fromhebrewformat(f)
	which = true
	makeformat = f.args["format"]
	return fromhebrewandverify(f)
end

function verify(f)
	frame = f
	which = false
	local success, res = pcall(pick1, frame.args[1], frame.args[2], "[[קטגוריה:חוסר מתאם בין תאריך עברי לתאריך לועזי]]")
	if not success then
		res = ""
	end
	local ans = ""
	if f.args[2] ~= "" then
		ans = tohebrew0(f.args[2])
	end
	if f.args[1] ~= "" then
		ans = ans .. fromhebrewandverify(f)
	end
	return res .. ans
end

function fromhebrewandverify(f)
	frame = f
	local success, res = pcall(fromhebrew1, frame.args[1])
	if success then
		return ifwhich(res)
	elseif frame.args["about"] == "כן" and
		frame:expandTemplate({ title = "גיל לערכי אישים/מקורב", args = { str = frame.args[1] } }) ~= "" then
		return ifwhich(frame.args[1], "[[קטגוריה:דפים עם תאריך עברי מקורב]]")
	elseif frame.args["error"] == nil then
		return aserror(res)
	end
	return fixerror(frame.args["error"])
end

function tohebrewyear(gyear)
	ans = tohebrewnum("01-01-" .. gyear, fromyear)
	if frame.args["exact"] ~= "כן" then
		ans = ans .. "-" .. tohebrewnum("01-12-" .. gyear, fromyear)
	end
	return ifwhich(ans)
end

function tohebrewmonth(wdate)
	if frame.args["exact"] == "כן" then
		return tohebrewdate("15 ב" .. wdate, frommonth)
	end
	local date1 = tohebrewdate("1 ב" .. wdate, same)
	local month1 = fromonlymonth(date1)
	local year1 = fromyear(date1)
	local date2 = same(date1 .. " + 31 days")
	date2 = tomonth(date2)
	date2 = tohebrewdate("1 ב" .. date2, same)
	date2 = same(date2 .. " - 1 days")
	local month2 = fromonlymonth(date2)
	local year2 = fromyear(date2)
	if year1 ~= year2 then
		return ifwhich(month1 .. " " .. year1 .. " - " .. month2 .. " " .. year2)
	elseif month1 ~= month2 then
		return ifwhich(month1 .. "-" .. month2 .. " " .. year1)
	else
		return ifwhich(month1 .. " " .. year1)
	end
end

function tohebrewdate(rem, format)
	local s1 = find1(rem, " ")
	local s2 = find1(rem, " ", s1 + 1)
	if len1(rem) - s2 == 3 then
		rem = sub1(rem, 1, s2) .. "0" .. sub1(rem, s2 + 1)
	end
	local take = takenumbers(rem)
	if remove(to(take)) ~= rem then
		error("wrong date", 0)
	end
	checkcasp(take)
	take = same(take .. " + " .. diff(take) .. " days")
	return ifwhich(format(take))
end

function tohebrewnum(wdate, format)
	if find1(wdate, "-", 3) == 3 and find1(wdate, "-", 6) == 6 and len1(wdate) == 9 then
		wdate = sub1(wdate, 1, 6) .. "0" .. sub1(wdate, 7, 9)
	end
	if same(wdate) ~= wdate then
		error("wrong date", 0)
	end
	checkcasp(wdate)
	wdate = same(wdate .. " + " .. diff(wdate) .. " days")
	return ifwhich(format(wdate))
end

function findwdatetype(wdate)
	local rem = mw.text.trim(remove(wdate))
	if find1(rem, "-") ~= 0 then
		return "num", rem
	end
	for m, mm in pairs(mtable) do
		if find1(rem, mm) == 1 then
			return "my", rem
		end
	end
	if tonumber(rem) ~= nil and tonumber(rem) .. "" == rem then
		return "y", rem
	end
	return "dmy", rem
end

function tohebrew1(wdate)
	local w, rem = findwdatetype(wdate)
	if w == "num" then
		return tohebrewnum(rem, from)
	elseif w == "dmy" then
		return tohebrewdate(rem, from)
	elseif w == "my" then
		return tohebrewmonth(rem)
	elseif w == "y" then
		return tohebrewyear(rem)
	else
		error("Wrong type: " .. ht, 0)
	end
end

function pick1(hdate, wdate, cat)
	local ht, wt, hrem, wrem, hdw, wdh, hsuccess, wsuccess, thsuccess, twsuccess
	if cat == nil then
		which = true
		ht, hrem = findhdatetype(hdate)
		wt, wrem = findwdatetype(wdate)
	else
		thsuccess, ht, hrem = pcall(findhdatetype, hdate)
		twsuccess, wt, wrem = pcall(findwdatetype, wdate)
		hsuccess = false
		wsuccess = false
	end
	if hrem == "" then
		ht = "empty"
	else
		hsuccess, hdw = pcall(fromhebrew1, hrem)
		if not hsuccess then
			ht = "about"
		end
	end
	if wrem == "" then
		wt = "empty"
	else
		wsuccess, wdh = pcall(tohebrew1, wrem)
		if not wsuccess then
			wt = "about"
		end
	end
	if cat ~= nil then
		if ht == "dmy" and wt ~= "dmy"
		then
			return "[[קטגוריה:חוסר מתאם בתאריכים - תאריך "
				.. frame.args["type"] .. " עברי מדויק]]"
		end
		if ht ~= "dmy" and wt == "dmy" then
			return "[[קטגוריה:חוסר מתאם בתאריכים - תאריך "
				.. frame.args["type"] .. " לועזי מדויק]]"
		end
		--			if ht == "dmy" or ht == "my" or ht == "y"
		--				then return "[[קטגוריה:חוסר מתאם בתאריכים - תאריך "
		--							.. frame.args["type"] .. " לועזי מדויק]]" end
		return ""
	end
	if wsuccess and hsuccess then
		if wt == "dmy" then
			return wdate
		elseif ht == "dmy" then
			return hdw
		elseif wt == "my" then
			return wdate
		elseif ht == "my" then
			return hdw
		elseif wt == "y" then
			return wdate
		else
			return hdw
		end
	elseif wsuccess then
		return wdate
	elseif hsuccess then
		return hdw
	elseif wt == "about" then
		return wdate
	else
		return hdate
	end
end

function checkcat(adate, cat)
	if cat == nil then
		return adate
	end
	return cat
end

function pick(f)
	frame = f
	local hebrew = frame.args["hebrew"]
	local world = frame.args["world"]
	local habout = false
	local wabout = false
	if frame:expandTemplate({ title = "גיל לערכי אישים/מקורב", args = { str = hebrew } }) ~= ""
	then
		hebrew = ""
		habout = true
	end
	if frame:expandTemplate({ title = "גיל לערכי אישים/מקורב", args = { str = world } }) ~= ""
	then
		world = ""
		wabout = true
	end
	local success, res = pcall(pick1, hebrew, world)
	if success then
		if habout and world == "" then
			return frame.args["hebrew"]
		end
		if wabout and hebrew == "" then
			return frame.args["world"]
		end
		return res
	elseif frame.args["error"] == nil then
		return aserror(res)
	end
	return fixerror(frame.args["error"])
end

function tohebrew0(wdate)
	local success, res = pcall(tohebrew1, wdate)
	if success then
		return res
	elseif frame.args["about"] == "כן" and
		frame:expandTemplate({ title = "גיל לערכי אישים/מקורב", args = { str = wdate } }) ~= "" then
		return ifwhich(frame.args[1], "[[קטגוריה:דפים עם תאריך מקורב]]")
	elseif frame.args["error"] == nil then
		return aserror(res)
	end
	return fixerror(frame.args["error"])
end

function tohebrew(f)
	frame = f
	which = true
	return tohebrew0(frame.args[1])
end

function samedate(f)
	frame = f
	which = true
	samesame = true
	return tohebrew0(frame.args[1])
end

function ifwhich(ans, noans)
	if noans == nil then
		noans = ""
	end
	if which then
		return ans
	else
		return noans
	end
end

function parts(hdate)
	local funcs = {
		{ function() return w[1] end },
		{ function() return w[2], w[1] end,
			function() return c(w[2], w[1]) end },
		{ function() return w[3], w[2], w[1] end,
			function() return c(w[3], w[2]), w[1] end,
			function() return w[3], c(w[2], w[1]) end },
		{ function() return w[4], c(w[3], w[2]), w[1] end,
			function() return c(w[4], w[3]), w[2], w[1] end,
			nil,
			function() return c(w[4], w[3]), c(w[2], w[1]) end },
		{ nil,
			function() return c(w[5], w[4]), c(w[3], w[2]), w[1] end } }
	function c(x, y) return y .. " " .. x end

	w = takewords(hdate)
	local code = 1
	if 0 < find1(hdate, "אלפים") then
		code = code + 1
	end
	if 1 == find1(hdate, "אדר א") then
		code = code + 2
	end
	if 1 == find1(hdate, "אדר ב") then
		code = code + 2
	end
	local year, month, day = funcs[#w][code]()
	return day, month, year
end

function takewords(str)
	local count = 1
	local ans = {}
	local cur = str
	while find1(cur, " ") > 0 do
		ans[count] = sub1(cur, 1, find1(cur, " ") - 1)
		count = count + 1
		cur = sub1(cur, find1(cur, " ") + 1)
	end
	ans[count] = cur
	return ans
end

return {
	fromhebrew = fromhebrew,
	tohebrew = tohebrew,
	verify = verify,
	pick = pick,
	samedate = samedate,
	fromhebrewformat = fromhebrewformat
}