---- Configuration ------

Config = {
	
}

------ Metatables -------

Engine = {}

-- Function tables
Scriptable = {}
setmetatable(Scriptable, { __tostring = function(self) return self:GetPath() end })

Scene = {}
setmetatable(Scene, {__index = Scriptable, __tostring = function(self) return self:GetPath() end }) -- a sort of inheritance

Game = {}
setmetatable(Game, {__index = Scene, __tostring = function(self) return "[Game]" end}) -- a sort of inheritance

Object = {}
setmetatable(Object, {__index = Scriptable, __tostring = function(self) return self:GetPath() end }) -- a sort of inheritance

TaskList = {}

Achievement = {}
setmetatable(Achievement, {__tostring = function(self) return '{Achievement `'..self:GetName()..'`}' end })

SocialManager = {}
setmetatable(SocialManager, {__tostring = function(self) return "[SocialManager]" end});

ClassMetatables = {
  [1396920905] = Scriptable, 
  [1396917582] = Scene, 
  [1195461957] = Game, 
  [1329744453] = Object, 
  [1414284622] = TaskList, 
  [1414285390] = TaskList, 
  [1414286916] = TaskList,
  [1094928457] = Achievement,
  [1397703497] = SocialManager
}

function LightUserdataIndexFunction(obj, key)
  local ot = GetTypeableType(obj)
  local objmeta = ClassMetatables[ot]
  if not objmeta then
		Game:ReportError("Incorrect object passed as Scriptable!")
		return nil
  end
  return objmeta[key]
end
--[[
function StringIndexFunction(obj, key)
  local ot = Game:Get(obj)
  if not ot then
		Game:ReportError("Object with path '"..obj.."' does not exist!")
		return nil
  end
  return LightUserdataIndexFunction(ot, key)
end
--]]
function CreateMeta(game) -- assigns a global metatable for all light userdata
  debug.setmetatable(game, {__index = LightUserdataIndexFunction})
 -- debug.setmetatable('', {__index = StringIndexFunction})
end

--------------------------

MouseCancel = {}
MouseCancel.Continue = false
MouseCancel.Cancel = true

PointMetatable = {}

function Point(x, y)
	if type(x) == "table" and #x == 2 and not y then
		setmetatable(x, PointMetatable)
		return x
	else
		local p = {x,y}
		setmetatable(p, PointMetatable)
		return p
	end
end

PointMetatable.__tostring = function(point)
	return "{"..point[1]..";"..point[2].."}"
end

PointMetatable.__add = function(o1, o2)
  if type(o2) ~= "table" or type(o1) ~= "table" then
    return o1
  end
  return {o1[1]+o2[1], o1[2]+o2[2]}
end

PointMetatable.__sub = function(o1, o2)
  if type(o2) ~= "table" or type(o1) ~= "table" then
    return o1
  end
  return {o1[1]-o2[1], o1[2]-o2[2]}
end

PointMetatable.__div = function(o1, o2)
  if type(o2) == "table" then
    return {o1[1]/o2[1], o1[2]/o2[2]}
  elseif type(o2) == "number" then
    return {o1[1]/o2, o1[2]/o2}
  else
    return o1
  end
end

PointMetatable.__mul = function(o1, o2)
  if type(o2) == "table" then
    return {o1[1]*o2[1], o1[2]*o2[2]}
  elseif type(o2) == "number" then
    return {o1[1]*o2, o1[2]*o2}
  else
    return o1
  end
end

function VecAdd(o1, o2)
  return {o1[1]+o2[1], o1[2]+o2[2]}
end

function VecSub(o1, o2)
  return {o1[1]-o2[1], o1[2]-o2[2]}
end

function VecMul(o1, o2)
  if type(o2) == "table" then
    return {o1[1]*o2[1], o1[2]*o2[2]}
  elseif type(o2) == "number" then
    return {o1[1]*o2, o1[2]*o2}
  else
    return o1
  end
end

function VecDiv(o1, o2)
  if type(o2) == "table" then
    return {o1[1]/o2[1], o1[2]/o2[2]}
  elseif type(o2) == "number" then
    return {o1[1]/o2, o1[2]/o2}
  else
    return o1
  end
end

function VecDist(o1,o2)
	local d = VecSub(o2,o1)
	return math.sqrt(d[1]*d[1]+d[2]*d[2])
end

function VecRotatedAround(o1, pivot, angle_in_radians)
	if (angle_in_radians == 0) then return o1 end
	
	local sa = math.sin(angle_in_radians)
	local ca = math.cos(angle_in_radians)
	
	return { ((o1[1]-pivot[1])*ca - (o1[2]-pivot[2])*sa) + pivot[1], ((o1[1]-pivot[1])*sa + (o1[2]-pivot[2])*ca) + pivot[2] }
end

function VecNormalize(o1)
	local length = math.sqrt(o1[1]*o1[1]+o1[2]*o1[2])
	return VecDiv(o1, length)
end

function VecDistSquare(o1,o2)
	local d = VecSub(o2,o1)
	return d[1]*d[1]+d[2]*d[2]
end
-- Scriptable

function Scene.ApplyToGroup(self, group, func, ...)
  for i=0,Scene.GetObjectCount(self)-1 do
    local obj = Scene.GetNthObject(self, i)
    if Object.GetGroupName(obj) == group then
      func(obj, ...)
    end
  end
end

function Scene.ApplyToObjects(self, func, ...)
  for i=0,Scene.GetObjectCount(self)-1 do
    local obj = Scene.GetNthObject(self, i)
    func(obj, ...)
  end
end

function Object.ApplyToSubObjects(self, func, ...)
	local parent = self:GetParentScene()
  for i=0,Scene.GetObjectCount(parent)-1 do
		local obj = Scene.GetNthObject(parent, i)
		if (Object.GetParentObject(obj) == self) then
			func(obj, ...)
		end
  end
end

function Scene.CheckGroup(self, group, func, expectedvalue)
  for i=0,Scene.GetObjectCount(self)-1 do
    local obj = Scene.GetNthObject(self, i)
    if Object.GetGroupName(obj) == group then
		if( func(obj) ~= expectedvalue ) then
			return false
		end
    end
  end
  return true
end

-- General
function split(inputstr, sep)
        if sep == nil then
                sep = "%s"
        end
        local t={} ; local i=1
        for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
                t[i] = str
                i = i + 1
        end
        return t
end

function string.startsWith (self, piece)
  return string.sub(self, 1, string.len(piece)) == piece
end

function string.endsWith (self, piece)
   return piece=='' or string.sub(self,-string.len(piece))==piece
end

function string.digitAtEnd (self)
   return string.match(self, '[%d]+$')
end



function shuffle(t)
  local n = #t
 
  while n >= 2 do
    -- n is now the last pertinent index
    local k = math.random(n) -- 1 <= k <= n
    -- Quick swap
    t[n], t[k] = t[k], t[n]
    n = n - 1
  end
 
  return t
end

function saturate(x)
  return math.max(0.0, math.min(1.0, x))
end

function smoothstep(edge0, edge1, x)
  x = saturate((x - edge0)/(edge1 - edge0))
  return x*x*(3 - 2*x)
end

function math.sign(x)
  return x>0 and 1 or x<0 and -1 or 0
end

function file_exists(file)
  return Game:FileExists(file)
end

function readwholefile(file)
	return Game:LoadTextFile(file)
end