hist = {
	name = "history",
	version = "23",
	initialised = false,
	SV={},
	L={},
	me = {},
	playerName = "",	-- might change
	playerID = "",  	-- Unique through renames
	tz_offset =0,
	debug = true
}

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,
							})
end

local function log_me(text)	-- per char log

	table.insert(hist.me.log,{["TimeStamp"]=GetTimeStamp(),
							["text"] = text,
							})
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)
	local description
	local category_id  = GetCategoryInfoFromAchievementId(id)
	local category_name =GetAchievementCategoryInfo(category_id)
		_,description,_,_,_,_,_= GetAchievementInfo(id)
	hist.me.ach[id]={
		["Category_ID"] = category_id,
		["Category"] = category_name,
		["time"] = GetTimeStamp(),
		["points"] = points,
		["name"] = zo_strformat(name),
		["description"] = zo_strformat(description),
		["link"] = link
		}

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

local function  log_clear()
	hist.SV.log = {}
	log("Cleared")
end
function load_history()
-- load up historic Achievements for Category 7  (Dungeons)
	log("Request to load historic Dungeon Achievements")
	local name,points,description,completed,adate,atime
  local cat
  local count = 0
	for i = 1,1800 do
		name,description,points,_,completed,adate,atime= GetAchievementInfo(i)
		if completed then
			cat,_,_ =GetCategoryInfoFromAchievementId(i)
--			if cat == 7 then
			if true then
				if hist.me.ach[i] == nil then
				--	log("Added historic achievement " ..i .. " " .. name)
						local category_id  = GetCategoryInfoFromAchievementId(i)
						local category_name =GetAchievementCategoryInfo(category_id)
						hist.me.ach[i]={
							["Category_ID"] = category_id,
							["Category"] = category_name,
							["time"] = (luatz_esodate(adate .. " " .. atime)+ hist.tz_offset),
							["points"] = points,
							["name"] = zo_strformat(name),
							["description"] = zo_strformat(description),
							["link"] = GetAchievementLink(i)
							}
						count = count + 1
				end
			end
		end
	end
	log("Added " .. tostring(count) .. " historic achievements.")
	d("reloaded achievements")
end

local function fix()  -- fix links etc for any old data
local description
	hist.me.ach_updates = nil
	log("Request to fix Achievement data")
 for i,_ in pairs(hist.me.ach) do
 		_, description,_,_,_,_,_= GetAchievementInfo(i)
		local category_id  = GetCategoryInfoFromAchievementId(i)
		local category_name =GetAchievementCategoryInfo(category_id)
		hist.me.ach[i]["Category_ID"] = category_id
		hist.me.ach[i]["Category"] = category_name
		hist.me.ach[i]["description"] = zo_strformat(description)
		hist.me.ach[i]["link"] = GetAchievementLink(i)
 end

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].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 = math.floor(GetSecondsPlayed() /60)
	hist.me.levels[hist.me.level].time = hist.me.timeplayed

	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
	local oldlevel = hist.me.level
	hist.me.level =  GetUnitLevel("player") + GetUnitVeteranRank("player")
	log("Levelled up. Was " .. oldlevel .. "  now " .. hist.me.level)
    hist.me.levels[hist.me.level] = {
        begin = GetTimeStamp(),
        time  = 0,		-- Accumulated time in Minutes
        Ach_Points = GetEarnedAchievementPoints(),
        deaths = 0
    }
	local map = new_map()
	--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



	-- Duplication of info, but easier to work with
	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.debug then
      d("Hist: Leveled:.. ")
          d(hist.me.level)
    end
	else  -- not "player"
		if hist.debug then
		d("Hist: Got Levelup Event, but not for me")
        d(unitTag)
		end
	end
end


local function CP_Levelup(_,unitTag, old, new)
	if unitTag == "player" then
		local oldlevel = hist.me.CP.level
		hist.me.level =  GetUnitLevel("player") + GetUnitVeteranRank("player")
		log("CP Gained was " .. old .. ",  now " .. new ..  ",  level: " .. hist.me.level)
		hist.me.levels[hist.me.level] = {
			begin = GetTimeStamp(),
			time  = 0,		-- Accumulated time in Minutes
			Ach_Points = GetEarnedAchievementPoints(),
			deaths = 0
		}
		local map = new_map()
		--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 CP Gained in map " .. map
			log (msg)
			if hist.debug then
				d(msg)
			end
		end



		-- Duplication of info, but easier to work with
		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.debug then
		  d("Hist: CP Gained:.. ")
			  d(hist.me.level)
		end
	end
end

local function CP_Achieved(_,_)
	log("Achieved Champion Rank.")
	hist.me.CP.FirstLocation = new_map()
	hist.me.CP.FirstTime  = GetTimeStamp()

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.level = GetUnitLevel("player") + GetUnitVeteranRank("player")
		hist.me.Alliance = zo_strformat("<<C:1>>",GetAllianceName(GetUnitAlliance("player")))
		hist.me.ach={}
		hist.me.maps={}
		hist.me.log={}
		hist.me.CP={}
		hist.me.CP.level =0
		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
	hist.me.levels[hist.me.level].deaths = hist.me.levels[hist.me.level].deaths +1
	-- 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
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()

	local level = GetUnitLevel("player") + GetUnitVeteranRank("player")
    -- 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.SV.data == nil ) then
		hist.SV.data = {}
		log("hist.SV.data  created")
	end
	if (hist.SV.old ~= nil) then -- Delete legacy data that we never used anyway
		hist.SV.old = nil
	end

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

	hist.SV.this_version = hist.version

 --- hist.SV.version    -- Used by ZOS System

	if hist.SV.data[hist.playerID] == nil then
		if 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()
		else
			-- Ok old name data, fix it.
			log("New data format, saving " .. hist.playerName .. " as " ..  hist.playerID)
			hist.SV.data[hist.playerID] = hist.SV.data[hist.playerName]
			hist.SV.data[hist.playerID].name = hist.playerName

			hist.SV.data[hist.playerName] = nil		-- and on the eighth day God invented Lua
		end
	end

	hist.me = hist.SV.data[hist.playerID]
	if hist.me.level == nil then		-- fix if doesn't exist
		hist.me.level = level
	end

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

	if hist.me.CP == nil then
		hist.me.CP = {}
		hist.me.CP.level =0
	end

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




	-- fix missed level, leveled up with addon disabled


	if hist.me.level ~=  level then
		log("Missed level, Was " .. hist.me.level .. "  now " .. level)
		hist.me.level =  level
		hist.me.levels[hist.me.level] = {
			begin = GetTimeStamp(),
			time  = 0,		-- Accumulated time in Minutes
			Ach_Points = GetEarnedAchievementPoints(),
			deaths = 0
			}
	end

	if hist.debug then
		log_truncate(500)
	else
		log_truncate(20)
	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.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


	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, EVEN_ZONE_CHANGED, new_map)
	hist.initialised = true
end



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

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