local moduleDates = require( "Module:Dates" )
local moduleWikidata = require( "Module:Wikidata" )

function deepcopy(orig)
    local orig_type = type(orig)
    local copy
    if orig_type == 'table' then
        copy = {}
        for orig_key, orig_value in next, orig, nil do
            copy[deepcopy(orig_key)] = deepcopy(orig_value)
        end
        setmetatable(copy, deepcopy(getmetatable(orig)))
    else -- number, string, boolean, etc
        copy = orig
    end
    return copy
end

-- accepts table of time+precision values
function ageCurrent ( bTable )
	local possibleAge = "NYA" -- it meansm "Not Yet Assigned", not what you imagined!

	for bKey, bValue in pairs(bTable) do
		if ( bValue.unknown ) then
			return nil
		end
		local bStructure = bValue.structure
		local bPrecision = bValue.precision

		local dStructure = os.date( "*t" )

		local calculatedAge = ageImpl ( bStructure, bPrecision, dStructure, 11 )
		if ( possibleAge == "NYA" ) then
			possibleAge = calculatedAge
		else
			if ( possibleAge ~= calculatedAge ) then
				possibleAge = nil
			end
		end
	end

	return possibleAge
end

-- accepts tables of time+precision values
function age ( bTable, dTable )
	local possibleAge = "NYA" -- it meansm "Not Yet Assigned", not what you imagined!

	for bKey, bValue in pairs( bTable ) do
		if ( bValue.unknown ) then
			return nil
		end
		local bStructure = bValue.structure
		local bPrecision = bValue.precision

		for dKey, dValue in pairs( dTable ) do
			if ( dValue.unknown ) then
				return nil
			end
			local dStructure = dValue.structure
			local dPrecision = dValue.precision

			local calculatedAge = ageImpl ( bStructure, bPrecision, dStructure, dPrecision )
			if ( possibleAge == "NYA" ) then
				possibleAge = calculatedAge
			else
				if ( possibleAge ~= calculatedAge ) then
					possibleAge = nil
				end
			end
		end
	end

	return possibleAge
end

function ageImpl ( bStructure, bPrecision, dStructure, dPrecision )
	if ( not bStructure or not dStructure or bPrecision < 10 or dPrecision < 10 ) then
		return nil
	end

 	if ( bPrecision == 10 or dPrecision == 10 ) then
 		if ( bStructure.month < dStructure.month ) then
 			return dStructure.year - bStructure.year
 		end
 		if ( bStructure.month == dStructure.month ) then
 			return nil
 		end
 		if ( bStructure.month > dStructure.month ) then
 			return dStructure.year - bStructure.year - 1
 		end
 	end
 
  	if ( bStructure.month < dStructure.month ) then
 		return dStructure.year - bStructure.year
 	end
 	if ( bStructure.month == dStructure.month ) then
	  	if ( bStructure.day <= dStructure.day ) then
	 		return dStructure.year - bStructure.year
	 	else 
	 		return dStructure.year - bStructure.year - 1
 		end
 	end
 	if ( bStructure.month > dStructure.month ) then
 		return dStructure.year - bStructure.year - 1
 	end

	return nil
end

-- returns table of time+precision values for specified property
function parseProperty ( propertyName )
	local entity = mw.wikibase.getEntityObject()
	if not entity or not entity.claims or not entity.claims[propertyName] then
		return nil
	end

	local result = {}
	for key, value in pairs( entity.claims[propertyName] ) do
		if ( value.mainsnak.snaktype == "value" ) then
			-- The calendar model used for saving the data is always the proleptic Gregorian calendar according to ISO 8601.
			local timeISO6801 = tostring( value.mainsnak.datavalue.value.time )
			local unixtime = moduleDates.parseISO8601( timeISO6801 )
			local structure = os.date("*t", unixtime)
			local precision = tonumber( value.mainsnak.datavalue.value.precision )
			local calendarmodel = 'gregorian';
			if (mw.ustring.find(value.mainsnak.datavalue.value.calendarmodel, 'Q1985786', 1, true)) then
				calendarmodel = 'julian';
			end
			local item = { time=unixtime, structure=structure, precision=precision, calendarmodel=calendarmodel }
			table.insert ( result, item )
		elseif ( value.mainsnak.snaktype == "somevalue" ) then
			--unknown 
			local item = { unknown="unknown" }
			table.insert ( result, item )
		elseif ( value.mainsnak.snaktype == "novalue" ) then
			-- novalue
			local item = { unknown="novalue" }
			table.insert ( result, item )
		end
	end
	return result
end

-- проверка на совпадающие даты с разной моделью календаря
function checkDupDates( t )
	if #t > 1 then
		local removed = false;
		local j = 1;
		-- проверка на совпадающие даты с разной моделью календаря
		while (j <= #t)  do
			local i = 1;
			while (i <= #t)  do
				if i ~= j then
					if (os.time(t[j].structure) == os.time(t[i].structure)) then
						if ((t[j].calendarmodel == 'gregorian') and 
							(t[i].calendarmodel == 'julian')) then
							removed = true;
							break;
						else
							table.remove(t, i)
						end
					else
					  i = i + 1;
					end
				else
					i = i + 1;
				end
			end
			if removed then
				removed = false;
				table.remove(t, j);
			else
				j = j+1;
			end
		end
	end
end

local p = {}

-- entry points from templates
function p.dateOfBirth( frame )
    local appendToCategory = mw.title.getCurrentTitle():inNamespace ( 0 )
	local bTable = parseProperty ( "p569" )
	local dTable = parseProperty ( "p570" )
	if not bTable then
		return ''
	end

    local options = {}
    options['property'] = 'p569'
    options['conjunction'] = '&#32;или&#32;'
    options['value-module'] = 'Wikidata/Огноо'
    options['value-function'] = 'formatBirthDate'
    options['somevalue'] = '\'\'неизвестно\'\'[[К:Персоналии, чья дата рождения не установлена]]'
    local childFrame = frame:newChild{ title = 'some test title', args = options }
    local result =  moduleWikidata.formatStatements( childFrame )

	if ( not dTable ) then
		local age = ageCurrent( bTable )
		if ( age ) then
			result = result .. ' <span style="white-space:nowrap;">(' .. age .. ' ' .. mw.language.new( 'ru' ):plural( age, 'год', 'лет', 'года') .. ')</span>'
		    if ( age > 150 and appendToCategory ) then
		        result = result .. '[[К:Википедия:Статьи о персоналиях с большим текущим возрастом]]'
		    end
		end
	end

	return result;
end

-- entry points from templates
function p.dateOfDeath( frame )
    local appendToCategory = mw.title.getCurrentTitle():inNamespace ( 0 )
	local bTable = parseProperty ( "P569" )
	local dTable = parseProperty ( "P570" )
	if not dTable then
		return ''
	end

    local options = {}
    options['property'] = 'p570'
    options['conjunction'] = '&#32;или&#32;'
    options['value-module'] = 'Wikidata/Огноо'
    options['value-function'] = 'formatDeathDate'
    options['somevalue'] = '\'\'неизвестно\'\'[[К:Персоналии, чья дата смерти не установлена]]'
    local childFrame = frame:newChild{ title = 'some test title', args = options }
    local result =  moduleWikidata.formatStatements( childFrame )

	if ( bTable and dTable ) then
		local age = age( bTable, dTable )
		if ( age ) then
			result = result .. ' <span style="white-space:nowrap;">(' .. age .. ' ' .. mw.language.new( 'ru' ):plural( age, 'год', 'лет', 'года') .. ')</span>'
		    if ( age > 150 and appendToCategory ) then
		        result = result .. '[[К:Википедия:Статьи о персоналиях с большим возрастом во время смерти]]'
		    end
		end
	end

	return result;
end

-- Reentry point for Wikidata Snak formatting
function p.formatBirthDate( value, options )
	return formatDateImpl( value, options, 'bday', 'Мэндэлсэн ' )
end

-- Reentry point for Wikidata Snak formatting
function p.formatDeathDate( value, options )
	return formatDateImpl( value, options, 'dday', 'Нас нөгчсөн ' )
end

-- Reentry point for Wikidata Snak formatting
function p.formatDate( value, options )
	return formatDateImpl( value, options, nil, nil )
end

function formatDateImpl( value, options, infocardClass, categoryPrefix )
	-- The calendar model used for saving the data is always the proleptic Gregorian calendar according to ISO 8601.
	local timeISO6801 = tostring( value.time )
	local unixtime = moduleDates.parseISO8601( timeISO6801 )

	if ( options.plain ) then
		return unixtime;
	end

	local structure = os.date("*t", unixtime)
	local precision = tonumber( value.precision )

	if precision == 9 then
		local tCopy = deepcopy( structure )
		tCopy.day = nil
		tCopy.month = nil
		return moduleDates.formatWikiImpl(tCopy, tCopy, infoclass, categoryPrefix)
	end

	-- year and month only
	if precision == 10 then
		local tCopy = deepcopy( structure )
		tCopy.day = nil
		return moduleDates.formatWikiImpl(tCopy, tCopy, infoclass, categoryPrefix)
	end

	local calendarmodel = 'gregorian';
	if (mw.ustring.find(value.calendarmodel, 'Q1985786', 1, true)) then
		calendarmodel = 'julian';
	end

	if (calendarmodel == 'gregorian') then
    	return moduleDates.formatWikiImpl( structure, structure, infocardClass, categoryPrefix )
    else
		return moduleDates.formatWiki( unixtime, infoclass, categoryPrefix )
	end
end

return p