
require ( "usermessage" )

local table 			= table
local pairs 			= pairs
local ipairs 			= ipairs
local concommand 		= concommand
local SERVER 			= SERVER
local CLIENT 			= CLIENT
local saverestore 		= saverestore
local tostring			= tostring
local umsg				= umsg
local Msg				= Msg
local usermessage		= usermessage
local tonumber			= tonumber
local CurTime			= CurTime
local PrintTable		= PrintTable

//Client
local GetControlPanel 	= GetControlPanel

module("undo")



// undo.Create("Wheel")
// undo.AddEntity( axis )
// undo.AddEntity( constraint )
// undo.SetPlayer( self.Owner )
// undo.Finish()

if ( CLIENT ) then

	local ClientUndos = {}

	function UpdateUI()
	
		local Panel = GetControlPanel( "Undo" )
		
		Panel:ClearControls()
		Panel:AddHeader()
		
		// Sort the table in descending order
		local Sorted = {}
		local NUM = 100
		local Count = table.Count( ClientUndos )
		for k, v in pairs( ClientUndos ) do
		
			if ( Count < NUM ) then
				Sorted[ Count ] = {k=k, v=v}
			end
			
			Count = Count - 1
		
		end
	
		for k, v in ipairs( Sorted ) do
		
			local params = {}
			params.Text = tostring(v.v)
			params.Command = "gmod_undonum " .. tostring( v.k )
			
			Panel:AddControl( "Button", params )
		
		end		
		
	end
	
	local function AddUndo( message )

		local k 	= message:ReadLong();
		local v 	= message:ReadString();
		
		ClientUndos[ k ] = v
		
		UpdateUI()

	end
	
	usermessage.Hook( "AddUndo", AddUndo )
	
	
	local function Undone( message )

		local key 	= message:ReadLong();
		
		ClientUndos[ key ] = nil
		UpdateUI()

	end
	
	usermessage.Hook( "Undone", Undone )

end


if (!SERVER) then return end

local PlayerUndo = {}
// PlayerUndo
//	- Player UniqueID
//		- Undo Table
//			- Name
//			- Entities (table of ents)
//			- Owner (player)

local Current_Undo = nil


local function Save( save )

	saverestore.WriteTable( PlayerUndo, save )

end

local function Restore( restore )

	PlayerUndo = saverestore.ReadTable( restore )

end

saverestore.AddSaveHook( "UndoTable", Save )
saverestore.AddRestoreHook( "UndoTable", Restore )

// Starts an undo
function Create( text )

	Current_Undo = {}
	Current_Undo.Name 			= text
	Current_Undo.Entities 		= {}
	Current_Undo.Owner 			= nil
	
end

// Add entity to undo
function AddEntity( ent )

	if ( Current_Undo == nil ) then return end
	if ( !ent ) then return end
	if ( !ent:IsValid() ) then return end
	
	table.insert( Current_Undo.Entities, ent )
	
end

// Set the player owner od the undo
function SetPlayer( player )

	if (Current_Undo == nil) then return end	
	if ( !player || !player:IsValid() ) then return end
	
	Current_Undo.Owner = player
	
end

// Finish Undo and add it to the player's list
function Finish( NiceText )

	if (Current_Undo == nil) then return end
	if (Current_Undo.Owner == nil) then return end
	if (!Current_Undo.Owner:IsValid()) then return end
	
	local index = Current_Undo.Owner:UniqueID()
	PlayerUndo[ index ] = PlayerUndo[ index ] or {}
	
	local id = table.insert( PlayerUndo[ index ], Current_Undo )
	
	NiceText = NiceText or Current_Undo.Name
	
	umsg.Start( "AddUndo", Current_Undo.Owner )
		umsg.Long( id )
		umsg.String( NiceText )
	umsg.End()
			
	Current_Undo = nil
	
end


local function Do_Undo( undo )

	if ( !undo ) then return false end
	
	local count = 0
	
	// Remove each entity in this undo
	for index, entity in pairs( undo.Entities ) do
	
		if ( entity:IsValid() ) then
			entity:Remove()
			count = count + 1
		end
		
	end
	
	if (count > 0) then
		// Sends tooltip to client
		undo.Owner:SendLua( "GAMEMODE:OnUndo( '"..undo.Name.."' )" )
	end
	
	return count;
	
end

local function CC_UndoLast( pl, command, args )

	local index = pl:UniqueID()
	PlayerUndo[ index ] = PlayerUndo[ index ] or {}
	
	local last = nil
	local lastk = nil
	
	for k, v in pairs( PlayerUndo[ index ] ) do
	
		lastk = k
		last = v
	
	end
	
	// No undos
	if (!last) then	return end
	
	local count = Do_Undo( last )
	
	umsg.Start( "Undone", pl )
		umsg.Long( lastk )
	umsg.End()
	
	PlayerUndo[ index ][ lastk ] = nil
	
	// If our last undo object is already deleted then compact
	// the undos until we hit one that does something
	if ( count == 0 ) then
		CC_UndoLast( pl )
	end

end

local function CC_UndoNum( pl, command, args )

	if (!args[1]) then return end	
	local index = pl:UniqueID()
	PlayerUndo[ index ] = PlayerUndo[ index ] or {}
	
	local UndoNum = tonumber( args[1] )
	
	// Delete the undo whatever happens
	umsg.Start( "Undone", pl )
		umsg.Long( UndoNum )
	umsg.End()
	
	if ( !PlayerUndo[ index ][ UndoNum ] ) then return end
	
	// Undo!
	Do_Undo( PlayerUndo[ index ][ UndoNum ] )
	PlayerUndo[ index ][ UndoNum ] = nil

end

concommand.Add("undo",					CC_UndoLast		)
concommand.Add("gmod_undo",				CC_UndoLast		)
concommand.Add("gmod_undonum",			CC_UndoNum		)

	
