--[[
Name: AceLocale-2.1
Revision: $Rev: 17638 $
Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
Inspired By: Ace 1.x by Turan (turan@gryphon.com)
Website: http://www.wowace.com/
Documentation: http://www.wowace.com/index.php/AceLocale-2.1
SVN: http://svn.wowace.com/root/trunk/Ace2/AceLocale-2.1
Description: Localization library for addons to use to handle proper
             localization and internationalization.
Dependencies: AceLibrary
]]

local MAJOR_VERSION = "AceLocale-2.1"
local MINOR_VERSION = "$Revision: 17638 $"

if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end

if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check
local curTranslation, baseTranslation, translations, baseLocale, curLocale, strictTranslations, dynamic, reverseTranslation
local AceLocale = {}
local backbone = {}
backbone.class, backbone.super = false, false

local function initReverse(self)
   self[reverseTranslation] = {}

   for k, v in pairs(self[curTranslation]) do self[reverseTranslation][v] = k end

   setmetatable(self[reverseTranslation], {
      __index = function(tbl, key)  
         AceLocale:error("Reverse translation for %s not found", key)
      end
   })
end

local function __call(obj, text, flag)
   if flag == nil then return obj[text] end
   
   if flag == true then
      if rawget(obj[curTranslation], text) then AceLocale:error("Strict translation for %s not found", text) end
      return rawget(obj[curTranslation], text)
   elseif flag == false then
      return rawget(obj[curTranslation], arg2) or obj[baseTranslation][arg2]
   elseif flag == "reverse" then
      if not rawget(obj, reverseTranslation) then initReverse(obj) end
      return obj[reverseTranslation][text]	
   else
      AceLocale:error("Invalid flag given to __call.  Should be true/false/\"reverse\" but %s was given", flag)
   end
end

local function NewInstance(self, uid)
   if self.registry[uid] then return self.registry[uid] end

   self.registry[uid] = {}
   self.registry[uid][translations] = {}
   
   setmetatable(self.registry[uid], {
     __tostring = function()
        return "AceLocale(" .. uid .. ")"
     end,
     __call = __call,
     __index = backbone
   })
   
   return self.registry[uid]
end

function AceLocale:RegisterTranslation(uid, locale, func)
   self:argCheck(uid, 1, "string")
   self:argCheck(locale, 2, "string")
   self:argCheck(func, 3, "function")
	
   local instance = self.registry[uid] or NewInstance(self, uid)
   
   if instance[translations][locale] then
      self:error("Cannot provide the same locale more than once. %q provided twice for %s.", locale, uid)
   end
	
    if rawget(instance, baseLocale) then 
        for k, v in pairs(func()) do
			if not rawget(instance[baseTranslation], k) then
				self:error("Improper translation exists. %q is likely misspelled for locale %s.", k, locale)
			elseif value == true then
				self:error( "Can only accept true as a value on the base locale. %q is the base locale, %q is not.", instance[baseLocale], locale)
			end
		end
    else
        instance[baseTranslation] = func() 
        instance[baseLocale] = locale
        
        for k, v in pairs(instance[baseTranslation]) do
            if type(v) ~= "string" and type(v) ~= "table" then
                if type(v) == "boolean" then 
                    instance[baseTranslation][k] = k 
                else
                    self:error("Translation for %s is invalid.  Must be either string or boolean", k)
                end
            end
        end
        
        setmetatable(instance[baseTranslation], {__index = backbone})
    end
    
    instance[translations][locale] = func
end

function AceLocale:GetInstance(uid, locale)
   self:argCheck(uid, 1, "string")
   
   local instance = self.registry[uid]
   
   if not instance then self:error("At least one translation must be registered before you can GetInstance().") end
    
   instance:SetLocale(locale)
   
   return instance
end

function AceLocale:HasInstance(uid)
    self:argCheck(uid, 1, "string")
    return self.registry[uid] and true or false
end

setmetatable(backbone, {__index = 
    function(tbl, key)  
        AceLocale:error("Translation for %s not found", key)
    end})

function backbone:SetLocale(locale)
    local loose = false
    if locale == nil then return end
   
    if locale == true then 
        locale = GetLocale() 
        if rawget(self, curLocale) and self[curLocale] == locale then return end
        if not self[translations][locale] then locale = self[baseLocale] end
    end
    
    if rawget(self, curLocale) and self[curLocale] == locale then return end
    
    if not self[translations][locale] then
            AceLocale:error("Cannot SetLocale to %s for %s,  It has not been registered.", locale, tostring(self))
    end
    
    if self[translations][locale] and self[baseLocale] == locale then
        self[curLocale] = self[baseLocale]
        self[curTranslation] = {}
        getmetatable(self).__index = self[baseTranslation]
    else
        self[curLocale] = locale
        self[curTranslation] = self[translations][locale]()
        getmetatable(self).__index = self[curTranslation]
    end    

    if rawget(self, strictTranslations) then
        setmetatable(self[curTranslation], {
            __index = function(tbl, key)  
                AceLocale:error("Translation for %s not found", key)
            end
        })
    else
        setmetatable(self[curTranslation], {
            __index = self[baseTranslation]
        })
    end

    if not rawget(self, dynamic) then
        self[translations] = {}
    end

    if rawget(self, reverseTranslation) then
        self[reverseTranslation] = nil
    end
end

function backbone:ClearLocales()
    self[translations] = {}
    self[curLocale] = nil
    self[baseLocale] = nil
end

function backbone:SetDynamicLocales(flag)
    AceLocale:argCheck(flag, 1, "boolean")
    self[dynamic] = flag
end

function backbone:SetStrictness(flag)
    AceLocale:argCheck(flag, 1, "boolean")
    local mt
    
    if rawget(self, curTranslation) then
        mt = getmetatable(self[curTranslation])
    end
    
    if strict and mt then
        mt.__index = function(tbl, key)  
            AceLocale:error("Translation for %s not found", key)
        end
    elseif mt then
        mt.__index = self[baseTranslation]
    end

    self[strictTranslations] = strict
end

function backbone:HasTranslation(text)
   AceLocale:argCheck(text, 1, "string")
   
   if not rawget(self, curTranslation) then AceLocale:error("A locale must be chosen before you can call HasTranslation().") end
   
   return rawget(self[curTranslation], text) and true or false
end

function backbone:HasReverseTranslation(text)
   AceLocale:argCheck(text, 1, "string")
    
    if not rawget(self, curTranslation) then AceLocale:error("A locale must be chosen before you can call HasReverseTranslation().") end
    
    if not rawget(self, reverseTranslation) then
		initReverse(self)
	end
    
    return rawget(self[reverseTranslation], text) and true or false
end

function backbone:GetIterator()
   if not rawget(self, curTranslation) then AceLocale:error("A locale must be chosen before you can call GetIterator().") end
   return pairs(self[curTranslation])
end

function backbone:GetReverseIterator()
    if not rawget(self, curTranslation) then AceLocale:error("A locale must be chosen before you can call HasReverseTranslation().") end
    
    if not rawget(self, reverseTranslation) then
		initReverse(self)
	end
    
    return pairs(self[reverseTranslation])
end

function backbone:GetLocaleList()
    local results = {}
    for k, v in pairs(self[translations]) do tinsert(results, k) end
    return results
end

local function activate(self, oldLib, oldDeactivate)
   AceLocale = self
	
   if oldLib then
      self.registry = oldLib.registry
      self.curTranslation = oldLib.curTranslation
      self.baseTranslation = oldLib.baseTranslation
      self.translations = oldLib.translations
      self.baseLocale = oldLib.baseLocale
      self.curLocale = oldLib.curLocale
      self.strictTranslations = oldLib.strictTranslations
      self.dynamic = oldLib.dynamic
      self.reverseTranslation = oldLib.reverseTranslation
   end
	
   if not self.registry then self.registry = {} end
   if not self.curTranslation then	self.curTranslation = {} end
   if not self.baseTranslation then self.baseTranslation = {} end
   if not self.translations then self.translations = {} end
   if not self.baseLocale then self.baseLocale = {} end
   if not self.curLocale then self.curLocale = {} end
   if not self.strictTranslations then	self.strictTranslations = {} end
   if not self.dynamic then self.dynamic = {} end
   if not self.reverseTranslation then	self.reverseTranslation = {} end
	
   if oldDeactivate then
      oldDeactivate(oldLib)
   end
	
   curTranslation = self.curTranslation
   baseTranslation = self.baseTranslation
   translations = self.translations
   baseLocale = self.baseLocale
   curLocale = self.curLocale
   strictTranslations = self.strictTranslations
   dynamic = self.dynamic
   reverseTranslation = self.reverseTranslation
end

AceLibrary:Register(AceLocale, MAJOR_VERSION, MINOR_VERSION, activate)
AceLocale = AceLibrary(MAJOR_VERSION)