local DEG2RAD = 0.017453292519943295
local RAD2DEG = 57.29577951308232

local DEG2TA = 182.04444444444445
local TA2DEG = 0.0054931640625

local RAD2TA = 20860.756700940907
local TA2RAD = 0.000047936899621426287

local Builder = -3
local CanAttack = -2

local yield = coroutine.yield

local function sleep(delay)
    yield(delay, 1)
end

local function wait()
    yield(0, 2)
end

local function end_thread()
    yield(0, 4)
end

local function set_signal_mask(sig)
    yield(sig, 3)
end

local function vector()
    local vec = {x=0, y=0, z=0}
    return setmetatable(vec, __vector_metatable)
end

-- conservative functions that don't overwrite "permanent" objects but produce a temporary one
local function __vec_add(v0,v1)
    return setmetatable({x=v0.x + v1.x, y=v0.y + v1.y, z=v0.z + v1.z}, __fast_vector_metatable)
end

local function __vec_sub(v0,v1)
    return setmetatable({x=v0.x - v1.x, y=v0.y - v1.y, z=v0.z - v1.z}, __fast_vector_metatable)
end

local function __vec_mul(m,v)
    return setmetatable({x=m * v.x, y=m * v.y, z=m * v.z}, __fast_vector_metatable)
end

local function __vec_dot(v0,v1)
    return v0.x * v1.x + v0.y * v1.y + v0.z * v1.z
end

local function __vec_cross(v0,v1)
    return setmetatable({x=v0.y * v1.z - v0.z * v1.y, y=v0.z * v1.x - v0.x * v1.z, z=v0.x * v1.y - v0.y * v1.x}, __fast_vector_metatable)
end

-- convert a temporary object into a "permanent" one
local function fix_vector(v)
    return setmetatable(v, __vector_metatable)
end

-- use fast functions for temporary objects (we don't need to create new tables since we already have one we won't reuse)
local function __fast_vec_add(v0,v1)
    v0.x = v0.x + v1.x
    v0.y = v0.y + v1.y
    v0.z = v0.z + v1.z
    return v0
end

local function __fast_vec_sub(v0,v1)
    v0.x = v0.x - v1.x
    v0.y = v0.y - v1.y
    v0.z = v0.z - v1.z
    return v0
end

local function __fast_vec_mul(m,v)
    v.x = m * v.x
    v.y = m * v.y
    v.z = m * v.z
    return v
end

__vector_metatable = {__add=__vec_add, __sub=__vec_sub, __mul=__vec_mul, __mod=__vec_dot, __pow=__vec_cross}
__fast_vector_metatable = {__add=__fast_vec_add, __sub=__fast_vec_sub, __mul=__fast_vec_mul, __mod=__vec_dot, __pow=__vec_cross}

-- normalize v, modifies the content of v
local function normalize(v)
    local l = 1.0 / math.sqrt( v % v )
    v.x = l * v.x
    v.y = l * v.y
    v.z = l * v.z
    return v
end

-- returns the normalized version of v, doesn't change v
local function normalized(v)
    local l = 1.0 / math.sqrt( v % v )
    local tmp = vector()
    tmp.x = l * v.x
    tmp.y = l * v.y
    tmp.z = l * v.z
    return tmp
end

-- returns the angle between 2 vectors
local function vangle(v1, v2)
    return math.acos((v1 % v2) / math.sqrt( (v1 % v1) * (v2 % v2) )) * RAD2DEG
end

local game_signal = coroutine.yield

local function set_cam_mode( mode )
    if mode == CAM_NORMAL_MODE then
        game_signal( SIGNAL_CAM_DEF )
    else
        if mode == CAM_FREE_MODE then
            game_signal( SIGNAL_FREECAM )
        end
    end
end

local function piece(this, ...)
    __piece_list = {...}
    if #__piece_list == 0 then
        return
    end
    for i = 1, #__piece_list do
        this[__piece_list[i]] = i - 1
    end
    this["__piece_list"] = __piece_list
end

__units = {}

local function createUnitScript(name)
	_G["__name"] = name
	local script = {name=name}
	script.piece = piece
	script.yield = coroutine.yield
	script.is_turning = is_turning
	script.is_moving = is_moving
	script.wait_for_turn = 	function (this, obj, axis)
								while this:is_turning(obj, axis) do
									this.yield()
								end
							end
	script.wait_for_move = 	function (this, obj, axis)
								while this:is_moving(obj, axis) do
									this.yield()
								end
							end

	script.move = move
	script.explode = explode
	script.turn = turn
	script.get_value_from_port = get_value_from_port
	script.spin = spin
	script.stop_spin = stop_spin
	script.show = show
	script.hide = hide
	script.emit_sfx = emit_sfx
	script.move_piece_now = move_piece_now
	script.turn_piece_now = turn_piece_now
	script.get = get
	script.set_value = set_value
	script.set = set
	script.attach_unit = attach_unit
	script.drop_unit = drop_unit
	script.unit_position = unit_position
	script.cache = cache
	script.dont_cache = dont_cache
	script.shade = shade
	script.dont_shade = dont_shade
	script.set_script_value = set_script_value
	script.signal = _signal
	script.set_signal_mask = 	function(this, sig)
									this.yield(sig, 3)
								end
	script.sleep = 	function (this, delay)
				   		this.yield(delay, 1)
					end
	script.wait =	function ()
    					this.yield(0, 2)
					end
	script.end_thread = 	function ()
    							this.yield(0, 4)
							end
	script.start_script = _start_script
	_G[name] = script
	_G["__this"] = script
end

function cloneUnitScript(name, unitID)
	local script = _G[name]
	local unit = {unitID = unitID, __threads = {} }
	
	for k, v in pairs(script) do		-- clone the script table
		unit[k] = v
	end
	
	__units[unitID] = unit
end

__unit_virtual_table = {}	-- This is the table that will store additionnal data, so virtually we don't lose anything

local function __unit_virtual_index (table, key)
	local v = rawget(table, key)
	if v == nil then
		v = rawget(__unit_virtual_table, rawget(table, "index"))
		if v == nil then
			return nil
		elseif rawget(v, "UID") == rawget(table, "UID") then		-- We need this because we don't want to face an UID mismatch :)
			return rawget(v, key)
		else
			return nil
		end
	end
	return v
end

local function __unit_virtual_newindex (table, key, value)
	local v = rawget(table, key)
	if v == nil then
		local idx = rawget(table, "index")
		v = rawget(__unit_virtual_table, idx)
		if v == nil or rawget(v, "UID") ~= rawget(table, "UID") then
			v = {index=idx, UID=rawget(table, "UID")}
			rawset(v, key, value)
			rawset(__unit_virtual_table, idx, v)
		else
			rawset(v, key, value)
		end
	else
		rawset(table, key, value)
	end
end

__unit_metatable = {__index=__unit_virtual_index, __newindex=__unit_virtual_newindex}

-- Let's do the same thing with unit types :P
__type_virtual_table = {}	-- This is the table that will store additionnal data, so virtually we don't lose anything

local function __type_virtual_index (table, key)
	local v = rawget(table, key)
	if v == nil then
		v = rawget(__type_virtual_table, rawget(table, "name"))
		if v == nil then
			return nil
		else
			return rawget(v, key)
		end
	end
	return v
end

local function __type_virtual_newindex (table, key, value)
	local v = rawget(table, key)
	if v == nil then
		local name = rawget(table, "name")
		v = rawget(__type_virtual_table, name)
		if v == nil then
			v = {name=name}
			rawset(v, key, value)
			rawset(__type_virtual_table, name, v)
		else
			rawset(v, key, value)
		end
	else
		rawset(table, key, value)
	end
end

function wrap(this, func)
	return function(this, func, ...) func(this, ...) end
end

__type_metatable = {__index=__type_virtual_index, __newindex=__type_virtual_newindex}
