module(..., package.seeall);
local cassandra = require "lua_cassandra"
local schema_helpers = require "i6index.schema_helpers"

local lastUtf8Character  = string.char(0xf4, 0x80, 0x8f, 0xbf)
--return the terms that matched $term* for given field, campaign and documentName
--example
-- let's suppose that you have the following documents in the index:
-- {agent = "sato"}
-- {agent = "tanaka"}
-- {agent = "takahashi"}
-- {agent = "yamada"}
-- {agent = "yamaguchi"}
-- {agent = "yamamoto"}
-- {agent = "yamasaki"}
-- getMatchedTerms with field=agent and term=yama will return: {"yamada, yamaguchi, yamasaki"}
-- getMatchedTerms with field=agent and term=t returns: {"takahashi", "tanaka"}
-- getMatchedTerms with field=agent and term=sato returns: {"sato"}
function getMatchedTerms(connection, schema, documentName, campaign, field, term)
	local columnFamily = schema_helpers.getCFNameTerms(documentName)
	local matchedTerms = {}

	local startColumn  = cassandra.compose(field, term)
	local finishColumn = cassandra.compose(field, term .. lastUtf8Character)

	local blockSize = 200
	local columnsInSlice = blockSize
	local ignoreNextElement = false

	while columnsInSlice == blockSize do
		local range = connection:GetSliceRange(campaign, columnFamily, "", {
		                                            column_names={},
		                                            slice_range={
		                                                start    = startColumn,
		                                                finish   = finishColumn,
		                                                reversed = false,
		                                                count    = blockSize
		                                            }})
		columnsInSlice = 0
		for _, column in pairs(range) do
			if not ignoreNextElement then
				local compositeName = column.name_as_composite
				local value         = column.value_as_number

				if value > 0 then
					compositeName:GetString()
					table.insert(matchedTerms, compositeName:GetString())
				else
					-- Term's count self cleaning
					connection:RemoveColumnCounter(campaign, columnFamily, column.name)
				end

				startColumn    = column.name
				columnsInSlice = columnsInSlice + 1
			else
				ignoreNextElement = false
			end
		end
		ignoreNextElement = true
	end

	return matchedTerms
end

-- This function increments an array of indices to get all permutations
-- see makeMatchedTermPermutations function
local function incrementIndexes(indexes, maxValueIndexes, position)
	position = position or 1

	if position > #indexes then
		return indexes
	end

	if indexes[position] + 1 > maxValueIndexes[position] then
		indexes[position] = 1
		return incrementIndexes(indexes, maxValueIndexes, position + 1)
	else
		indexes[position] = indexes[position] + 1
		return indexes
	end
end

--Given the matched terms this function returns the permutation defined
-- by the array of indexes
-- see makeMatchedTermPermutations
local function getPermutation(matchedTerms, currentIndexes)
	local permutation = {}
	for i = 1, #matchedTerms do
		local idx = currentIndexes[i]
		table.insert(permutation, matchedTerms[i][idx])
	end
	return permutation
end

-- From the matched terms this functions builds all the combinations
-- to defer the query to the composite indexes example:
--  matchedTerms = {
--      {"hans", "heineken", "bunsen"},
--      {"one", "two"},
--      {"uno"}
--   }
--   mixes = makeMatchedTermPermutations(matchedTerms)
--  mixes will be:
--   {
--      {"hans", "one", "uno"}
--      {"heineken", "one", "uno"}
--      {"bunsen", "one", "uno"}
--      {"hans", "two", "uno"}
--      {"heineken", "two", "uno"}
--      {"bunsen", "two", "uno"}
--  }
--
function makeMatchedTermPermutations(matchedTerms)
	local indexes = {}
	local maxValueIndexes = {}
	local maxValues = 1
	for i = 1, #matchedTerms do
		table.insert(indexes, 1)
		table.insert(maxValueIndexes, #matchedTerms[i])
		maxValues = maxValues * #matchedTerms[i]
	end

	if maxValues > 50 then
		return nil, "too many matchedTerms"
	end

	local permutations = {}
	for i = 1, maxValues do
		table.insert(permutations, getPermutation(matchedTerms, indexes))
		indexes = incrementIndexes(indexes, maxValueIndexes)
	end

	return permutations
end

