function FixAngle(angle)
	while angle < 0. do
		angle = angle + M_TWO_PI
    end

	while angle >= M_TWO_PI do
		angle = angle - M_TWO_PI
    end

    return angle
end

function RandomBool()
    return math.random(0, 1)
end

function RandomTableSort(t)
    table_size = table.getn(t)
    for i = 1, table_size, 1 do
        ind1 = math.random(1, table_size)
        ind2 = math.random(1, table_size)
        tmp = t[ind1]
        t[ind1] = t[ind2]
        t[ind2] = tmp
    end
end

function CEntity:Move(dx, dy, time)
    -- TODO: Need using adopt policy
    local af = CreateAnimationEffector()--AnimationEffector()
    af:AttachTo(self)
    af:SetKey(_TRACK_X, 0.0, self:GetX())
    af:SetKey(_TRACK_Y, 0.0, self:GetY())
    af:SetKey(_TRACK_X, time, self:GetX() + dx)
    af:SetKey(_TRACK_Y, time, self:GetY() + dy)
    af:Play()
end

function CEntity:Rotate(dangle, time)
    local af = CreateAnimationEffector()--AnimationEffector()
    af:AttachTo(self)
    af:SetKey(_TRACK_A, 0.0, self:GetAngle())
    af:SetKey(_TRACK_A, time, self:GetAngle() + dangle)
    af:Play()
end

function CEntity:ChangeScale(start, finish, time)
    local af = CreateAnimationEffector()--AnimationEffector()
    af:AttachTo(self)
    af:SetKey(_TRACK_S, 0.0, start)
    af:SetKey(_TRACK_S, time, finish)
    af:Play()
end

function ChangeOpacity(entity, start, finish, time, delay)
    if not delay then   
        delay = 0.
    end
    local af = CreateAnimationEffector()--AnimationEffector()
    af:AttachTo(entity)
    af:SetKey(_TRACK_O, 0.0 + delay, start)
    af:SetKey(_TRACK_O, time + delay, finish)
    af:Play()
end

--[[
function CEntity:ChangeOpacity(start, finish, time, delay)
    if not delay then   
        delay = 0.
    end
    local af = CreateAnimationEffector()--AnimationEffector()
    af:AttachTo(self)
    af:SetKey(_TRACK_O, 0.0 + delay, start)
    af:SetKey(_TRACK_O, time + delay, finish)
    af:Play()
end
]]

function CreateCloseButton(pos, message, listener)
	local button = CreateButton('ButtonZoomZoneCloseUp',
								'ButtonZoomZoneCloseOver',
								'ButtonZoomZoneCloseDown',
								message,
								listener)
	button:AttachTo(play_field)
	button:SetX(pos.x)
	button:SetY(pos.y)
	button:SetZ(500)
	--button:SetRect(-30, -30, 60, 60)
	button:GetCaption():SetText(GetString('OK'))
	button:GetCaption():SetFont('data/Font/options.fnt')
	button:GetCaption():SetXY(-4., 2.)
    return button
end

function CreateCloseBigZoneButton(pos, message, listener)
	local button = CreateButton('ButtonBigZoomZoneClose',
								'ButtonBigZoomZoneClose',
								'ButtonBigZoomZoneClose',
								message,
								listener)
	button:AttachTo(play_field)
	--button:SetMouseOverScale(1.1)
	button:SetX(pos.x)
	button:SetY(pos.y)
	button:SetZ(500)
	button:SetLayer(3)
	--button:SetRect(-512, -128, 1024, 128)
	--button:GetCaption():SetText(GetString('BACK'))
	--button:GetCaption():SetFont('data/Font/options.fnt')
	--button:GetCaption():SetXY(512., -64.)

	--te->SetTextAlignment(HGETEXT_CENTER | HGETEXT_MIDDLE);
	--CFontPtr font = _FONTS(TASK_LIST_FONT);
	--te->SetFont(font);
	--te->SetPosXY(x, y);

    return button
end

function CreateCloseInfoButton(pos, message, listener)
	local button = CreateButton('OKButton',
                                'OKButtonOver',
								'OKButtonDown',
								message,
								listener)
	button:AttachTo(play_field)
	--button:SetMouseOverScale(1.05)
	button:SetX(pos.x)
	button:SetY(pos.y)
	button:SetZ(500)
	--button:SetRect(-80, -50, 160, 10)
	--button:GetCaption():SetText(GetString('OK'))
	--button:GetCaption():SetFont('data/Font/options.fnt')
	--button:GetCaption():SetXY(-4., 2.)
    return button
end

function CreateNextButton(pos, message, listener)
	local button = CreateButton('ButtonNextUp',
								'ButtonNextOver',
								'ButtonNextDown',
								message,
								listener)
	button:AttachTo(play_field)
	button:SetX(pos.x)
	button:SetY(pos.y)
	button:SetZ(220)
	--button:SetRect(-17, -14, 34, 28)
    return button
end

function Vector:__tostring()
    return 'X: '..self.x..' Y: '..self.y
end

function CAnimation:__tostring()
    local str = "HotX: "..self:GetHotX().." HotY: "..self:GetHotY()
    if self:GetFrames() > 0 then
        str = str..' NFrames: '..self:GetFrames()
    end
    str = str..' Color:'..tostring(self:GetColorStr())
    return str
end

function CGameObject:__tostring()
    local str = 'Name: '..self:GetName().." X: "..self:GetX().." Y: "..self:GetY().." Z: "..self:GetZ()
    if self:GetAnimation():GetFrames() > 0 then
        str = str..' NFrames: '..self:GetAnimation():GetFrames()
    end
    str = str..' Color:'..tostring(self:GetAnimation():GetColorStr())
    return str
end

function CTask:__tostring()
    return 'Text singular: '..self:GetTextSingular()
end

function GetAnimationLength(anim)
    assert(anim:GetSpeed() ~= 0.)
    return anim:GetFrames() / anim:GetSpeed()
end

function CScreenEx:__tostring()
    return self:GetName()
end

function CPanel:__tostring()
    return self:GetName()
end

function CInventory:__tostring()
    return self:GetName()
end

function CButtonExEntity:__tostring()
    return self:GetName()
end

function CTextEntity:__tostring()
    return self:GetText()
end

function CGameObject:Move(dx, dy, time)
    local af = CreateAnimationEffector()--AnimationEffector()
    af:AttachTo(self)
    af:SetKey(_TRACK_X, 0.0, self:GetX())
    af:SetKey(_TRACK_Y, 0.0, self:GetY())
    af:SetKey(_TRACK_X, time, self:GetX() + dx)
    af:SetKey(_TRACK_Y, time, self:GetY() + dy)
    af:Play()
end

function CGameObject:Rotate(dangle, time)
    local af = CreateAnimationEffector()--AnimationEffector()
    af:AttachTo(self)
    af:SetKey(_TRACK_A, 0.0, self:GetAngle())
    af:SetKey(_TRACK_A, time, self:GetAngle() + dangle)
    af:Play()
end

function CGameObject:ChangeScale(start, finish, time)
    local af = CreateAnimationEffector()--AnimationEffector()
    af:AttachTo(self)
    af:SetKey(_TRACK_S, 0.0, start)
    af:SetKey(_TRACK_S, time, finish)
    af:Play()
end

function CGameObject:ChangeOpacity(start, finish, time, delay)
    if not delay then
        delay = 0.
    end
    local af = CreateAnimationEffector()--AnimationEffector()
    af:AttachTo(self)
    af:SetKey(_TRACK_O, 0.0 + delay, start)
    af:SetKey(_TRACK_O, time + delay, finish)
    af:Play()

	--[[
    if self.opacity_effector then
		self.opacity_effector:Stop()--Destroy()
	end
	self.opacity_effector = af
	--]]
    --[[
    if not game_state.opacity_effectors then
        game_state.opacity_effectors = {}
    end
    if game_state.opacity_effectors[self:GetName()] then
		game_state.opacity_effectors[self:GetName()]:Stop()
	end
	game_state.opacity_effectors[self:GetName()] = af
	--]]
end

function CGameObject:MarkWithPS()
    self:AddParticleSystem('data/ParticleSystem/ObjectHighlight.psi',
                           'SolveLaterObjectParticle')
end

function IsAboveReturnFromZoomZone()
    local panel_obj = play_field:GetLevelScreen():GetPanel():GetObjectUnderMouse()
    local inv_obj = play_field:GetLevelScreen():GetInventory():GetObjectUnderMouse()
    local is_in_proper_pos = false
    if game_state.level_mode == 'hidden_object' then
        is_in_proper_pos = panel_obj and
                           (panel_obj:GetName() == 'background' or panel_obj:GetName() == 'drop_curtain')
    else
        is_in_proper_pos = GetMousePos().y > 768. - (RETURN_ZONE_HEIGHT + RETURN_ZONE_DHEIGHT) and
                           GetMousePos().x > 110. and -- left panel gui border
                           GetMousePos().x < 870. and -- right panel gui border
                           not panel_obj and
                           not inv_obj
    end
    return is_in_proper_pos
end