-- $LastChangedDate: 2010-05-25 21:26:05 -0400 (Tue, 25 May 2010) $
-- LEGAL: COPYRIGHT (C) 2010 JIM E. BROOKS WWW.PALOMINO3D.ORG
-- Basic Lua functions.
-- See module.txt for documentation and pitfalls.

lib = { }  -- namespace

--------------------------------------------------------------------------------
-- Mnemonics.
--------------------------------------------------------------------------------

-- Define in global table for speed/terseness.
KEYS      = pairs
KEYS_VALS = pairs

--------------------------------------------------------------------------------
-- Basic functions.
--------------------------------------------------------------------------------

-- Emulate int (truncation).
function lib.int( f )
    -- f % 1 is fractional part which is being subtracted out.
    if ( f > 0 ) then
        return f - f % 1
    elseif ( f < 0 ) then
        return f - f % -1
    else
        return 0
    end
end

-- Set default of a function arg if arg is nil.
-- Use this instead of the idiom "arg = arg or default"
-- which is wrong if arg is passed as false.
function lib.Default( arg, default )
    if ( arg ~= nil ) then
        return arg
    else
        return default
    end
end

--------------------------------------------------------------------------------
-- Functions for classes/objects.
--------------------------------------------------------------------------------

-- Creates an "instance table" from a "class table" (metatable).
-- For use in a Class:New() method.  Examples in module.txt.
function lib.NewInstance( classTable )
    -- Construct instance table.
    local instance = { }
    setmetatable( instance, classTable )
    classTable.__index = classTable
    return instance
end

-- Creates an "instance table" from a "class table" unless
-- self is an instance (of a derived class) rather than class table.
-- It solves the problem: is an instance of the base being constructed,
-- or is a base constructor being chained by derivative constructor?
function lib.NewInstanceChain( class, self_ )
    assert( self_ )
    if ( self_ == class ) then
        return lib.NewInstance( class )  -- class was passed implicitly by "Class:New()"
    else
        return self_  -- self was passed explicitly by "Base.New( self )" by derived constructor
    end
end

-- Produces a metatable (class table) that inherits methods from another one.
-- Examples in module.txt.
function lib.DerivedClass( BaseMT )
    local derivedMT = { }
    setmetatable( derivedMT, BaseMT )
    BaseMT.__index = BaseMT
    return derivedMT
end

-- Destroy an object stored as a member of a table.
-- Pass "member" arg as a string.  Calls table.Destroy() if defined.
-- Example: For self.object : lib.Destroy( self, "object" )
function lib.Destroy( table, member )
    local obj = table[member]
    if ( obj ) then
        if ( obj.Destroy ) then
            obj.Destroy( obj )  -- pass as self
        end
        table[member] = nil
    end
end

--------------------------------------------------------------------------------
-- Functions for tables.
--------------------------------------------------------------------------------

-- Return a copy of a table (doesn't do a deep/recursive copy).
function lib.CopyTable( t )
    local t2 = { }
    for k,v in KEYS_VALS(t) do
        t2[k] = v
    end
    return t2
end

-- True if two tables are equal by value.
-- Comparison is done in terms of a map (not an array).
-- { a=1, b=2 } == { b=2, a=1 }  same key:value pairs
-- { "a", "b" } ~= { "b", "a" }  because { [1]="a", [2]="b" } ~= { [1]="b", [2]="a" }
function lib.CompareTables( t0, t1 )
    for k in KEYS(t0) do
        if ( t0[k] ~= t1[k] ) then
            return false
        end
    end
    return true    
end

-- Iterates thru a table starting at a key.
-- If starting key is nil, begins at the first key.
-- Class tables (and many others) have literal strings as keys
-- (t.member is equivalent to t["member"]).
function lib.Iterator( t, startingKey )
    local k = startingKey  -- part of closure
    local first = true
    -- Return 3 args (function, table, nil).
    return
    function ( t )
        if ( not first ) then
            k = next( t, k )
        else
            first = false
        end
        return k, t[k]
    end, t, nil
end

-- Begin iterating AFTER the passed key.
function lib.IteratorAfter( t, key )
    local k = key  -- part of closure
    -- Return 3 args (function, table, nil).
    local func = function ( t )
                     k = next( t, k )
                     return k, t[k]
                 end
    return func, t, nil
end

-- Find element in table (created as an array), then remove it.
-- NOP if element is absent.
function lib.ArrayRemove( t, val )
    local n = table.getn( t )
    for i = 1, n do
        if ( t[i] == val ) then
            table.remove( t, i )
            return
        end
    end
end

-- Rotate table (created as an array).
function lib.ArrayRotate( t )
    local n = table.getn( t )
    if ( n > 1 ) then
        local first = t[1]
        table.remove( t, 1 )
        table.insert( t, first )
    end
end

--------------------------------------------------------------------------------
-- Functions for parsing.
--------------------------------------------------------------------------------

-- Catenate two words, placing space if necessary.
function lib.CatWords( s1, s2 )
    if ( s1 ~= "" ) then
        return s1 .. " " .. s2
    else
        return s2
    end
end

-- Split fields of a string.
function lib.SplitFields( str, sep )
    -- This regex pattern needs a separator at end of string.
    local t
    if ( str[string.len(str)] ~= sep ) then
        local str2 = string.format( "%s%s", str, sep )
        t = { str2:match((str2:gsub("[^"..sep.."]*"..sep, "([^"..sep.."]*)"..sep))) }
    else
        t = { str:match((str:gsub("[^"..sep.."]*"..sep, "([^"..sep.."]*)"..sep))) }
    end

    -- Edge-case for "" or "*[sep]".
    if ( t[#t] == "" ) then
        t[#t] = nil
    end

    return t
end

-- Remove spaces surrounding a word.  Keeps any spaces inside the word.
function lib.RemoveSurroundingSpaces( word )
    return string.gsub( word, "^%s*(.-)%s*$", "%1" )  -- trim() from Lua book
  --local a,b,c = string.match( word, "^(%s*)(.-)(%s*)$" )  -- equivalent
  --return b
end

-- Parse comma-separated names from a string into a table.
function lib.ParseCommas( commaNames )
    -- Split at commas.
    local nameTable = lib.SplitFields( commaNames, "," )

    -- Then remove surrounding spaces.
    for key,name in KEYS_VALS(nameTable) do
        nameTable[key] = lib.RemoveSurroundingSpaces( name )
    end

    return nameTable
end
