hist = {
	name = "history",
	version = 67,
	initialised = false,
	SV={},
	L={},
	me = {},
	playerName = "",	-- might change
	playerID = "",  	-- Unique through renames
	tz_offset =0,
	debug = false
}
--GetWorldName()
local function log_truncate(max)
	--Limit log to max
	for _ = max, (#hist.SV.log-1)  do
		table.remove(hist.SV.log,1)
		if hist.debug then
		d("Truncated log.  Size now:  " .. tostring(#hist.SV.log))
		end
	end
end


local function log(text)

table.insert(hist.SV.log,{["TimeStamp"]=GetTimeStamp(),
	["text"] = text,
	["Char"] = hist.playerName,
	["CharID"] = hist.playerID,
	["world"] = GetWorldName(),
	})
end

local function log_me(text)	-- per char log

table.insert(hist.me.log,{["TimeStamp"]=GetTimeStamp(),
["text"] = text,
})
end

local Keep_Achievement = function (id) -- is this an achievement we want to save?
	if hist.IDs[id] then
		return true
	else
		return false
	end
end

local function get_start ()	-- get better start date from Achievements
	-- Check Achievements
	local earliest_time = 0
	local earliest_Achievement
	for id, ach in pairs(hist.me.ach) do
		if earliest_time == 0 then
		earliest_time = ach.time
		earliest_Achievement = id
		elseif earliest_time > ach.time then
		earliest_time = ach.time
	end
end

if earliest_time == 0 then	-- No Achievements
	earliest_time = GetTimeStamp()
end

if hist.me.Created == nil	then
	hist.me.Created = earliest_time
	elseif hist.me.Created > earliest_time then		-- Quest was earlier, then reset to earlier
	if hist.debug then
d("Start Time Reset for " .. hist.playerName)
end
	log ("Start Time Reset.")
	hist.me.Created = earliest_time
	end
end

local function Achievement(_, name, points, id, link)
	if Keep_Achievement(id)  then
		hist.me.ach[id] = {}
		hist.me.ach[id].name=name
		hist.me.ach[id].time = GetTimeStamp()
	--	hist.me.ach[id].link = link
		local numCriteria= GetAchievementNumCriteria(id)
		if numCriteria > 0  then
			hist.me.ach[id]["Criteria"]= {}
			for Criteria = 1, numCriteria do
				local c_Description, c_completed, c_required = GetAchievementCriterion(id, Criteria)
				hist.me.ach[id].Criteria[Criteria]={}
				hist.me.ach[id].Criteria[Criteria].Description=c_Description
				hist.me.ach[id].Criteria[Criteria].completed = c_completed
				hist.me.ach[id].Criteria[Criteria].required=c_required
			end
		end

		if hist.debug then
		d("Achievement Awarded:.. ")
		d(hist.me.ach[id])
		end
	end
end

local function  log_clear()
hist.SV.log = {}
log("Cleared")
end
function load_history()
-- load up historic Achievements.
-- Note some achievements like "Level 40 Hero" have become obsolete. CategoryID is nil
	log("Request to load historic Dungeon Achievements")
	local name,points,description,completed,adate,atime
	local count = 0
	local reject = 0
	local flag = 0

	for i,_ in pairs (hist.IDs) do

			local name,description,points,_,completed,adate,atime= GetAchievementInfo(i)
			if completed then
					if hist.me.ach[i] == nil then

						hist.me.ach[i] = {}
						hist.me.ach[i].name =name
						hist.me.ach[i].description=description
						hist.me.ach[i].time = (luatz_esodate(adate .. " " .. atime)+ hist.tz_offset)
					--	hist.me.ach[i].link = GetAchievementLink(i)
						local numCriteria= GetAchievementNumCriteria(i)
						if numCriteria > 1  then
							hist.me.ach[i].numCriteria = numCriteria
							hist.me.ach[i].Criteria={}
							for Criteria = 1, numCriteria do
								local c_Description, c_completed, c_required = GetAchievementCriterion(i, Criteria)
								hist.me.ach[i].Criteria[Criteria]= {}
								hist.me.ach[i].Criteria[Criteria].Description=c_Description
								hist.me.ach[i].Criteria[Criteria].completed = c_completed
								hist.me.ach[i].Criteria[Criteria].required=c_required
							end
						end
						count = count + 1
					end
			end --Keep
	end	--for
	log("Added " .. tostring(count) .. " historic achievements.")
end

local function new_map()
local map = zo_strformat("<<C:1>>",GetMapName())
	if hist.me.maps[map] == nil then
		hist.me.maps[map] = {}
		hist.me.maps[map].visit = 1
		hist.me.maps[map].firstvisitdate = GetTimeStamp()
		if hist.debug then
			d("New Map: " .. map )
		end
		log("New map: " .. map)
	end
	return map
end

local function Activated()
	hist.me.timeplayed_last = hist.me.timeplayed
	hist.me.timeplayed = math.floor(GetSecondsPlayed() /60)
	if hist.me.levels[hist.me.level] ~= nil then
		hist.me.levels[hist.me.level].time = hist.me.timeplayed
	end

	if hist.world.CP_Level > 0 and hist.world.CP[hist.world.CP_Level] ~= nil then
		if hist.me.timeplayed_last == nil then	-- is nil on new chars
			hist.me.timeplayed_last = 0
		end
		hist.world.CP[hist.world.CP_Level].time = hist.world.CP[hist.world.CP_Level].time + hist.me.timeplayed - hist.me.timeplayed_last
	end

	local map = new_map()
	if hist.me.maps[map].visit == nil then
		hist.me.maps[map].visit = 1
	else
		hist.me.maps[map].visit = hist.me.maps[map].visit +1
	end
end


local function levelup(_, unitTag,_)
	if unitTag ~= "player" then
		return
	end

	hist.me.base_level = GetUnitLevel("player")
	hist.me.veteran_level =  GetUnitVeteranRank("player")

	-- Duplication of info, but easier to work with
	local map = new_map()
	if hist.me.maps[map].timeslevel == nil then
		hist.me.maps[map].timeslevel = 1
		hist.me.maps[map].firstlevel = hist.me.level
	else
	hist.me	.maps[map].timeslevel = hist.me.maps[map].timeslevel + 1
	end


--	if hist.me.veteran_level > 0 then return end

	hist.me.level = hist.me.base_level + hist.me.veteran_level

	log("Level up to " .. hist.me.level)
	hist.me.levels[hist.me.level] = {
		begin = GetTimeStamp(),
		time  = 0,		-- Accumulated time in Minutes
		Ach_Points = GetEarnedAchievementPoints(),
		deaths = 0
		}

	--Update Level data with map
	if hist.me.levels[hist.me.level].map == nil then
		hist.me.levels[hist.me.level].map = map

		local msg = "First Levelup in map " .. map
		log (msg)
		if hist.debug then
			d(msg)
		end
	end
end


local function CP_Levelup(_,unitTag, _, _)
	if unitTag ~= "player" then
		return
	end

	hist.world.CP_Level=GetPlayerChampionPointsEarned()


	hist.world.CP[hist.world.CP_Level] = {}
	hist.world.CP[hist.world.CP_Level].begin= GetTimeStamp()
	hist.world.CP[hist.world.CP_Level].playerID = hist.playerID
	hist.world.CP[hist.world.CP_Level].map = new_map()
	hist.world.CP[hist.world.CP_Level].deaths =0
	hist.world.CP[hist.world.CP_Level].time=0

	hist.me.base_level = GetUnitLevel("player")
	hist.me.veteran_level =  GetUnitVeteranRank("player")
	hist.me.level = hist.me.base_level + hist.me.veteran_level

	log("CP_Level up to " .. hist.world.CP_Level)

end

local function CP_Achieved(_,_)
	log("Achieved Champion Rank.")
	log_me("Achieved Champion Rank.")
end

local function gendertext()
	if (GetUnitGender("player") == GENDER_MALE)
		then return "M"
	end

	if (GetUnitGender("player") == GENDER_FEMALE)
		then return "F"
	end
	return "U"
end

local function setup_char()
	log("Set up: " .. hist.playerName )
	hist.SV.data[hist.playerID] = {}		-- initialise data for current char, using unique GUID
	hist.me = hist.SV.data[hist.playerID]
	hist.me.name=hist.playerName

	hist.me.Class = zo_strformat("<<C:1>>",GetUnitClass("player"))
	hist.me.Race = zo_strformat("<<C:1>>",GetUnitRace("player"))
	hist.me.Gender = gendertext()
	hist.me.base_level = GetUnitLevel("player")
	hist.me.veteran_level =  GetUnitVeteranRank("player")
	hist.me.level = hist.me.base_level + hist.me.veteran_level
	hist.me.world = GetWorldName()
	hist.me.Alliance = zo_strformat("<<C:1>>",GetAllianceName(GetUnitAlliance("player")))
	hist.me.ach={}
	hist.me.maps={}
	hist.me.log={}

	hist.me.levels = {}
	hist.me.levels[hist.me.level] = {
		begin = GetTimeStamp(),
		time  = 0,		-- Accumulated time in Minutes
		Ach_Points = GetEarnedAchievementPoints(),
		deaths = 0
		}  -- end of defaults
	hist.me["LoadTime"] = GetTimeStamp()
	load_history()
	get_start()
	log_me("Started")
end

local function Dead()
	-- Per Level
	if hist.me.levels[hist.me.level] ~= nil then
		hist.me.levels[hist.me.level].deaths = hist.me.levels[hist.me.level].deaths +1	-- low level deaths -level 1 to 49, then jump to CP
	end
	-- Per Map

	local map = new_map()

	--Update map data with Deaths
	if hist.me.maps[map].deaths == nil then
		hist.me.maps[map].deaths = 0
		hist.me.maps[map].firstdeathlevel = hist.me.level
		local msg = "First Death in map " .. map
		log (msg)
			if hist.debug then
				d("First Death in map " ..map)
			end
	end
	hist.me.maps[map].deaths = hist.me.maps[map].deaths +1

	if hist.me.maps[map].firstdeathdate == nil then
		hist.me.maps[map].firstdeathdate  = GetTimeStamp()
	end

	if hist.world.CP_Level == 0 then
		d("Death: world CP level == 0,aborting processing of CP" )
	return
	end



	if hist.world.CP[hist.world.CP_Level] ~= nil then
		hist.world.CP[hist.world.CP_Level].deaths = hist.world.CP[hist.world.CP_Level].deaths +1
	else
		local msg = "Died but CP entry for " .. tostring(hist.world.CP_Level) .. " was nil."
		log (msg)
		if hist.debug then
			d(msg)
		end
	end

end



function hist.Initialise(_, addOnName)
if (hist.name ~= addOnName) then return end

	-- find computed time difference.
	local now = GetTimeStamp()
	hist.datestr = GetDateStringFromTimestamp(now)
	hist.timestr = GetTimeString()
	hist.luatz_ts = luatz_esodate(hist.datestr .." " .. hist.timestr)
	hist.tz_offset = now - hist.luatz_ts



	hist.playerName = GetUnitName("player")
	hist.playerID = GetCurrentCharacterId()

	-- Load the saved variables
	hist.SV = ZO_SavedVars:NewAccountWide("History_SV", 1, nil, nil)


	if hist.SV.log == nil then
	hist.SV.log = {}
	log("hist.SV.log created")
	end

	if hist.debug then
		log("debug is on")
		log("now: " .. now)
		log("hist.datestr: " .. hist.datestr)
		log("hist.timestr: " .. hist.timestr)
		log("hist.luatz_ts: " .. hist.luatz_ts)
		log("hist.tz_offset: " ..hist.tz_offset)
	end

	if hist.SV.worlds == nil then
	hist.SV.worlds = {}
	end

	if hist.SV.worlds[GetWorldName()] == nil then
	hist.SV.worlds[GetWorldName()] = {}
	end

	hist.world = hist.SV.worlds[GetWorldName()]

	if hist.world.CP == nil then
	hist.world.CP = {}
	end

	hist.world.CP_Level = GetPlayerChampionPointsEarned()

	if (hist.SV.data == nil ) then
	hist.SV.data = {}
	log("hist.SV.data  created")
	end




	-- CP leveling screwed, after all of this .. at least make current

	if hist.world.CP[hist.world.CP_Level] == nil then
	--[[	hist.world.CP[hist.world.CP_Level] = {}
		hist.world.CP[hist.world.CP_Level].begin= GetTimeStamp()
		hist.world.CP[hist.world.CP_Level].playerID = hist.playerID
		hist.world.CP[hist.world.CP_Level].map = "BORKY"
		hist.world.CP[hist.world.CP_Level].deaths =0
		hist.world.CP[hist.world.CP_Level].time=0
		hist.world.CP[hist.world.CP_Level].borked = true --]]
		end


	if (hist.SV.start_version  == nil ) then
	hist.SV.start_version = hist.version
	end




	--- hist.SV.version    -- Used by ZOS System, dont touch

	if hist.SV.data[hist.playerID] == nil then
		if hist.debug then
		log("No data found for PlayerID")
		end
	-- Ok playerID may be nil, but we might still have data under player name
	-- In which case we rename it, otherwise it is a new char.

		if hist.SV.data[hist.playerName] == nil then
		--No name, no ID, must be new.
			setup_char()
		end
	end

	hist.me = hist.SV.data[hist.playerID]


	hist.me.base_level = GetUnitLevel("player")
	hist.me.veteran_level =  GetUnitVeteranRank("player")
	hist.me.level = hist.me.base_level + hist.me.veteran_level
	hist.me.world = GetWorldName()

	if 	hist.me.levels == nil then
	hist.me.levels = {}
	end

	if hist.me.maps == nil then
	hist.me.maps = {}
	end

	if hist.me.log == nil then	-- special events we want to keep, like name changes
	hist.me.log = {}
	end

	if hist.me.IDVersion == nil then
		hist.me.IDVersion = ""
	end

	if hist.debug then
	log_truncate(500)
	else
	log_truncate(200)
	end

	hist.SV.lang=GetCVar("language.2")	-- For offline use

	if hist.me.logins == nil then
		hist.me.logins = 0
	end
	hist.me.logins = hist.me.logins +1		-- count them.

	hist.me["LoginTime"] = GetTimeStamp()

	if hist.me["ReloadTime"] == nil then
		hist.me["ReloadTime"] = GetTimeStamp()
	end

	if  hist.me["ReloadTime"] + 80080 <= GetTimeStamp() then	 --  23 hours or more have past
		load_history()		-- pickup anything that might be missed
		hist.me["ReloadTime"] = GetTimeStamp()
		log ("Startup: Timed check on missed achievements.")
	end


	if hist.me.IDVersion ~= hist.IDVersion then
		log ("ID file version changed, loading")
		hist.me.IDVersion = hist.IDVersion
		load_history()
	end


	if hist.me.name ~= hist.playerName then
		log("Changed Name, Was " ..  hist.me.name)
		log_me("Changed Name, Was " ..  hist.me.name)
		hist.me.name = hist.playerName
	end

	if 	hist.me.Race ~= zo_strformat("<<C:1>>",GetUnitRace("player")) then
		log("Changed Race, Was " .. hist.me.Race)
		log_me("Changed Race, Was " .. hist.me.Race)
		hist.me.Race = zo_strformat("<<C:1>>",GetUnitRace("player"))
	end

	if 	hist.me.Gender ~= gendertext() then
		log("Changed Gender, Was " .. hist.me.Gender)
		log_me("Changed Gender, Was " .. hist.me.Gender)
		hist.me.Gender = gendertext()
	end

	if (hist.SV.this_version == nil) then hist.SV.this_version = 0 end

	if (type(hist.SV.this_version) == "string") then
		hist.SV.this_version = tonumber(hist.SV.this_version)
	end


	if (hist.SV.this_version <55) then
		log("Erasing Achievements ready for format 55.")
		hist.me.ach = {}
		hist.SV.old = nil
		hist.SV.cleaned1 = nil
		load_history()
	end

	hist.SV.this_version = hist.version


	EVENT_MANAGER:RegisterForEvent(hist.name, EVENT_ACHIEVEMENT_AWARDED, Achievement)
	EVENT_MANAGER:RegisterForEvent(hist.name, EVENT_LEVEL_UPDATE, levelup)
	EVENT_MANAGER:RegisterForEvent(hist.name, EVENT_CHAMPION_POINT_UPDATE, CP_Levelup)
	EVENT_MANAGER:RegisterForEvent(hist.name, EVENT_CHAMPION_LEVEL_ACHIEVED , CP_Achieved)
	--	EVENT_MANAGER:RegisterForEvent(hist.name, EVENT_VETERAN_RANK_UPDATE, levelup)
	EVENT_MANAGER:RegisterForEvent(hist.name, EVENT_PLAYER_ACTIVATED, Activated)
	--	EVENT_MANAGER:RegisterForEvent(hist.name, EVENT_PLAYER_DEACTIVATED, Activated)
	EVENT_MANAGER:RegisterForEvent(hist.name, EVENT_PLAYER_DEAD, Dead)
	EVENT_MANAGER:RegisterForEvent(hist.name, EVENT_ZONE_CHANGED, new_map)
	hist.initialised = true

end



SLASH_COMMANDS["/histload"] = load_history
SLASH_COMMANDS["/histclear"] = log_clear
SLASH_COMMANDS["/histstart"] = get_start

EVENT_MANAGER:RegisterForEvent(hist.name, EVENT_ADD_ON_LOADED, hist.Initialise)