local table = require "table"
local oo    = require "loop.base"
local Entity = require "LuaOrm.Entity"
local Utils = require "LuaOrm.Utils"
local AssociationArray = require "LuaOrm.Associations.AssociationArray"

local type, ipairs, pairs = type, ipairs, pairs
local string = require "string"

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

--tries to automatically determine foreign keys from database
--it depends on direction to know if are mymodel<--[fk]fromtable or mymodel[fk]-->totable
function ResolveForeignKeys(model, associatedModel, direction)
	local ds = model.dataSource
	local fn = ds["GetForeignKeys" .. direction]
	if fn then
		return fn(ds, model, associatedModel)
	end
	return nil
end

function GetRelationAliasedName(self)
	return self.m_associatedModelAlias
end

function GetRelatedName(self)
	return self.m_associatedModelName
end

function GetRelatedTableName(self)
	return self.m_associatedModel.tableName
end

--return the default values for the key elements of the related entity
--related to the original model, needed to index omiting the parent key
local function GetCompletionIdValue(self, id)
	local completionId = {}
	for k, v in pairs(id) do
		--only makes sense for the foreign key elements belonging to the primary key
		completionId[self.m_foreignKeys[k]] = v
	end
	return completionId
end

--returns new m_associatedModel objects
function LoadAll(self, parent)
	local relatedEntities = AssociationArray{m_relationship = self, m_parent = parent}
	if (parent ~= nil and parent:GetId()) then
		local sql = self:BuildQuery(parent)
		local ds = self.m_associatedModel.dataSource
		local rows = ds:FetchRows(self.m_associatedModel, sql)
		if (rows) then
			local id = parent:GetId()
			relatedEntities:Initialize(GetCompletionIdValue(self,id))
			for k,row in pairs(rows) do
				local newEntity = self.m_associatedModel:CreateEntityFromRow(row, parent.m_lazy)
				relatedEntities:AddItem(newEntity)
			end
		end
	end
	return relatedEntities
end

--applies a condition on a related model field
function GetRelatedCondition(self, fieldName, operator, fieldValue)
	if (self.m_associatedModel.columnInfo[fieldName]) then
		local ds = self.m_associatedModel.dataSource
		local escapedValue
		if operator:lower() == "like" then
			escapedValue = Utils.EscapeTypedValue(ds, "string", "%" .. fieldValue .. "%")
		else
			escapedValue = Utils.EscapeValue(ds, self.m_associatedModel.columnInfo[fieldName], fieldValue)
		end
		return ds.Format("%Q.%Q%s%s", self.m_associatedModel.tableName, fieldName, operator, escapedValue)
	else
		--that field does not exists in the related model, malformed query...
		error("Field:".. fieldName .." does not exists in association for:"..self.m_associatedModel.tableName)
	end
end

--returns the only new m_associatedModel object that matches
function Load(self, parent)
	if (parent ~= nil and parent:GetId()) then
		local sql = self:BuildQuery(parent)
		local ds = self.m_associatedModel.dataSource
		local row = ds:FetchRow(self.m_associatedModel, sql)
		if (row) then
			return self.m_associatedModel:CreateEntityFromRow(row, parent.m_lazy)
		end
	end
	return nil
end


