local table = require "table"
local oo    = require "loop.base"
local string = require "string"

local type = type
local ipairs = ipairs
local pairs = pairs
local print = print
local rawget = rawget
local getmetatable = getmetatable
local setmetatable = setmetatable
local tostring = tostring
local tonumber = tonumber

module("LuaOrm.Associations.AssociationArray", oo.class)


function __init(class, elem)
	local self = elem or {}
	self.m_innerElements = {}
	return oo.rawnew(class, self)
end

-----------------------------------------------
--Compare case insensitive two table keys
-----------------------------------------------
local function idMatches(self, key, searchKey, model)
	local k, v
	for k, v in pairs(key) do
		if not searchKey[k] then
			searchKey[k] = self.m_entityId[k]
		end
		local currentColumn = model.columnInfo[k]
		if (currentColumn.type == "int" or currentColumn.type == "integer" or currentColumn.type == "numeric" or
			currentColumn.type == "bit" or currentColumn.type == "smallint" or currentColumn.type == "tinyint" or currentColumn.type == "boolean") then
			if tonumber(key[k]) ~= tonumber(searchKey[k]) then
				return false
			end
		else
			--fecha varchar y text comparan ok aca
			if string.lower(tostring(key[k])) ~= string.lower(tostring(searchKey[k])) then
				return false
			end
		end
	end
	return true
end

-----------------------------------------------
--Index operator for the array
--always tries to index the array using the whole key of the entity
--Ej: entity.relatedEntity["key"] ; entity.relatedEntity[{id="key", name = "smith"}]
-----------------------------------------------
local function arrayIndexOperator(self, name)
	local relatedModel = nil
	local meta = getmetatable(self)
	if (meta.__classIndex and type(name) == "table") then
		--now I check if the request is for an element array
		local inner = rawget(self, "m_innerElements")
		for k, v in ipairs(inner) do
			local key = v:GetId()
			if (idMatches(self, key, name, v.m_model)) then
				return v
			end
		end
	end
	if (type(meta.__classIndex) == "table") then
		return meta.__classIndex[name]
	else
		if (type(meta.__classIndex) == "function") then
			return meta.__classIndex(self, name)
		else
			return nil
		end
	end	
end

-----------------------------------------------
--Total items in the relationship
-----------------------------------------------
function Count(self)
    if (self) then
        return #self.m_innerElements
    end
end

-----------------------------------------------
--Get nth item of the array
-----------------------------------------------
function GetItem(self, index)
    if (self and type(index) == "number") then
        return self.m_innerElements[index]
    end
end

-----------------------------------------------
--Adds new item to the relationship
-----------------------------------------------
function AddItem(self, item, key)
	if (not key or type(key) == "table") then
		self.m_innerElements[#self.m_innerElements + 1] = item
	else
		self[key] = item
	end
end

-----------------------------------------------
--Adds new item to the relationship using its primary
--key as the indexer for the entity, automatically solving the relationship fields
-----------------------------------------------
function Add(self, item)
    if (type(self.m_relationship.SetRelationshipFields) == "function") then
        self.m_relationship:SetRelationshipFields(self.m_parent, item)
    end
    self:AddItem(item, item:GetId())
end

function Initialize(self, baseid)
	self.m_entityId = baseid
	--change the index operator if not already changed...
	local classMetaTable = getmetatable(self)
	if (not classMetaTable.__classIndex) then
		newMetaTable = {}
		newMetaTable.__classIndex	 = classMetaTable.__index
		newMetaTable.__index = arrayIndexOperator
		setmetatable(self, newMetaTable)
	end
end

-----------------------------------------------
--Iterator that gives access to the internal
--array of items in the relationship
-----------------------------------------------
Iterator = function(t)
	local iterator = function(t, key)
		key = key + 1
		if (key > #t.m_innerElements) then
			return nil
		end
		return key, t.m_innerElements[key]
	end
	local initial = 0
	return iterator, t, initial
end

