var classAirTable = function(lib, settings)
{
    // Module references //

    var _lib = lib || null;
    var _modules = {};
    var _this = this;
    this.version = '1.0';
    this.toString = function(){ return _lib.toString() + ' - Table'; };
    arguments.callee.toString = function(){ return _lib.toString() + ' - Table - Constructor'; };

    // Private properties //

    /**
    * Module memory.
    * @type Object
    */
    var _memory =
    {
        tables : {}
    };

    // Public properties //

    /**
    * DOM references used by module.
    * @type Object
    */
    this.dom =
    {
        container : null,
        table : null
    };

    /**
    * Module settings.
    * @type Object
    */
    this.settings =
    {
        classNames :
        {
            collapse : 'collapse'
        },
        domIndexes :
        {
            container : 'afwTable'
        }
    };

    // Private functions //

    /**
    * Parse and fix table column values.
    * (Type casting)
    *
    * @version 1.0 2010-09-27
    * @author Mathias Petersson
    */
    function _fixTableValues(table)
    {
        var span, iRow, iRows, i, m, value, re = /^[-(]?[0-9.]+\)?$/;
        for(iRow = 0, iRows = table.rows.length; iRow < iRows; ++iRow)
        {
            if(table.rows[iRow].values)
            {
                for(i = 0, m = table.rows[iRow].values.length; i < m; ++i)
                {
                    value = table.rows[iRow].values[i].replace(/[ ,]/g, '');
                    if(re.test(value))
                    {
                        if((value.substring(0, 1) == '(' && value.substring(value.length - 1) != ')')
                        || (value.substring(0, 1) != '(' && value.substring(value.length - 1) == ')'))
                        {
                            continue;
                        }
                        if(value.substring(0, 1) == '(')
                        {
                            value = '-' + value.substring(1, value.length - 1);
                        }
                        table.rows[iRow].values[i] = parseFloat(value);
                    }
                }
            }
        }
    }

    /**
    * Initialize script.
    * @ignore
    */
    function _init(settings)
    {
        // Verify that library is loaded and linked
        if(_lib === null)
        {
            alert('Library missing for ' + _this.toString());
            return;
        }

        // Settings
        _this.settings = _lib.mergeObjects(_this.settings, settings);
    }

    /**
    * Saves DOM table in memory.
    *
    * @param {String} tableID   Table ID.
    *                           Required.
    * @param {Object} data      Object with table data.
    *                           Required.
    * @version 1.0 2010-10-22
    * @author Mathias Petersson
    */
    function _storeTable(tableID, data)
    {
        if(!_this.getTable(tableID))
        {
            _memory.tables[tableID] = data;
        }
    }

    // Public functions //

    /**
    * Adjusts table to fit inside it's container.
    *
    * @param {String} tableID   Table ID.
    *                           Optional. Default: getCurrentTableID()
    * @version 1.0 2010-09-15
    * @author Mathias Petersson
    */
    this.autoAdjust = function(tableID)
    {
        var options = _lib.isValidInputs();
        var result = false;
        if(options)
        {
            var table;
            options.tableID = (options.tableID !== undefined ? options.tableID : _this.getCurrentTableID());
            table = _this.getTable(options.tableID);
            if(table)
            {
                table.dom.style.width = '100%';
                result = true;
            }
        }
        return result;
    };
    this.autoAdjust.rules =
    {
        required : ['tableID'],
        types :
        {
            tableID : ['string']
        }
    };

    /**
    * Change active table.
    *
    * @param {String} tableID   Table ID.
    *                           Required.
    * @version 1.0 2010-10-22
    * @author Mathias Petersson
    * @return Result.
    * @type Boolean
    */
    this.changeTable = function(tableID)
    {
        var options = _lib.isValidInputs();
        var result = false;
        if(options)
        {
            var current, table;
            current = _this.getTable(_this.getCurrentTableID());
            table = _this.getTable(options.tableID);
            if(!current)
            {
                _this.dom.container.innerHTML = '';
            }
            if(current && table)
            {
                _this.collapseTable(false);
                _lib.removeElement(current.dom);
                _this.dom.table = null;
            }
            if(table)
            {
                _this.dom.container.appendChild(table.dom);
                _this.dom.table = table.dom;
                result = true;
            }
        }
        return result;
    };
    this.changeTable.rules =
    {
        required : ['tableID'],
        types :
        {
            tableID : ['string']
        }
    };

    /**
    * Set or remove a table from collapsed state.
    *
    * @param {Boolean} collapse If to collapse or not.
    *                           Optional. Default: Opposite to current state
    * @param {Mixed} classNames Classname(s) to add to table.
    *                           Optional. Default: settings.classNames.collapse
    * @version 1.0 2010-10-29
    * @author Mathias Petersson
    * @return Collapse state.
    * @type Boolean
    */
    this.collapseTable = function(collapse, classNames)
    {
        var options = _lib.isValidInputs();
        var result = false;
        if(options)
        {
            var table, i, m, collapsePrefix = 'collapse-';
            table = _this.getTable(_this.getCurrentTableID());
            if(table)
            {
                options.collapse = (options.collapse !== undefined ? options.collapse : !table.collapsed);
                options.classNames = (options.classNames !== undefined ? options.classNames : _this.settings.classNames.collapse);
                options.classNames = _lib.getCollection(options.classNames);
                for(i = 0, m = options.classNames.length; i < m; ++i)
                {
                    if(options.collapse)
                    {
                        _lib.addClass(table.dom, collapsePrefix + options.classNames[i]);
                        table.collapsed = true;
                        result = true;
                    }
                    else
                    {
                        _lib.removeClass(table.dom, collapsePrefix + options.classNames[i]);
                        table.collapsed = false;
                    }
                }
            }
        }
        return result;
    };
    this.collapseTable.rules =
    {
        required : [],
        types :
        {
            collapse : ['boolean'],
            classNames : ['string', 'array']
        }
    };

    /**
    * Fetch table from html and it's rows (and the rows cells).
    *
    * @param {String} tableID   Table ID.
    *                           Required.
    * @version 1.0 2010-10-22
    * @author Mathias Petersson
    * @return Object with various information: {dom : dom, rows : [{dom : dom, cells : [dom,dom,dom]}]}
    * @type Object
    */
    this.fetchTable = function(tableID)
    {
        var options = _lib.isValidInputs();
        var result = false;
        if(options)
        {
            var i, m, table, rows, row, cells;
            table = _lib.getDOM(options.tableID);
            if(table && table.nodeName.toUpperCase() == 'TABLE')
            {
                result = {dom : table, rows : []};
                rows = _lib.getTableRows(table);
                for(i = 0, m = rows.length; i < m; ++i)
                {
                    row = {};
                    row.dom = rows[i];
                    row.cells = _lib.getRowCells(rows[i]);
                    result.rows.push(row);
                }
            }
        }
        return result;
    };
    this.fetchTable.rules =
    {
        required : ['tableID'],
        types :
        {
            tableID : ['string']
        }
    };

    /**
    * Generates a table ID name from passed arguments.
    *
    * @version 2011-03-10
    * @author Mathias Petersson
    * @return String Lowercase value glued with - between each argument
    */
    this.generateTableIDName = function()
    {
        var i, m, s = '';
        for(i = 0, m = arguments.length; i < m; ++i)
        {
            s += (s == '') ? arguments[i] : '-' + arguments[i];
        }
        return s.toLowerCase();
    }

    /**
    * Returns current table ID.
    *
    * @version 1.0 2010-10-21
    * @author Mathias Petersson
    * @return Result.
    * @type String
    */
    this.getCurrentTableID = function()
    {
        return (_this.dom.table ? _this.dom.table.id : '');
    };

    /**
    * Get row identifier.
    *
    * @param {Object} row   Table row.
    *                       Required.
    * @version 1.0 2010-09-27
    * @author Mathias Petersson
    * @return Result.
    * @type String
    */
    this.getRowIdentifier = function(row)
    {
        var options = _lib.isValidInputs();
        if(options)
        {
            if(options.row.nodeName.toUpperCase() == 'TR')
            {
                var i, m, rowId = _lib.getCollection(options.row.className, ' ');
                for(i = 0, m = rowId.length; i < m; ++i)
                {
                    if(rowId[i].match(/^r[0-9]+$/))
                    {
                        return rowId[i];
                    }
                }
            }
        }
        return false;
    };
    this.getRowIdentifier.rules =
    {
        required : ['row'],
        types :
        {
            row : ['dom']
        }
    };

    /**
    * Get table data.
    *
    * @param {String} tableID   Table ID.
    *                           Required.
    * @version 1.0 2010-10-22
    * @author Mathias Petersson
    * @return Result.
    * @type Object
    */
    this.getTable = function(tableID)
    {
        var options = _lib.isValidInputs();
        var result = false;
        if(options)
        {
            result = _memory.tables[options.tableID];
        }
        return result;
    };
    this.getTable.rules =
    {
        required : ['tableID'],
        types :
        {
            tableID : ['string']
        }
    };

    /**
    * Initialize module instance.
    *
    * @param {Object} dom   Modules DOM elements.
    *                       Required.
    * @version 1.0 2010-10-21
    * @author Mathias Petersson
    */
    this.init = function(dom)
    {
        var options = _lib.isValidInputs();
        var errorMessage = 'Failed to initiate air_table.';
        if(options)
        {
            _this.dom.container = options.dom[_this.settings.domIndexes.container];
            if(_this.dom.container)
            {
                return;
            }
            errorMessage += '<br />- missing some DOM elements';
        }
        _lib.error(errorMessage);
    };
    this.init.rules =
    {
        required : ['dom'],
        types :
        {
            dom : ['object']
        }
    };

    /**
    * Link module pointers between modules.
    *
    * @param {Object} modules   Object containing modules.
    *                           Required.
    * @version 1.0 2010-09-21
    * @author Mathias Petersson
    */
    this.linkModules = function(modules)
    {
        var options = _lib.isValidInputs();
        if(options)
        {
            var x;
            for(x in modules)
            {
                if(modules[x].toString() != _this.toString())
                {
                    _modules[x] = modules[x];
                }
            }
        }
    };
    this.linkModules.rules =
    {
        required : ['modules'],
        types :
        {
            modules : ['object']
        }
    };

    /**
    * Parse requested table and store it in memory.
    *
    * @param {String} tableID   Table ID.
    *                           Required.
    * @version 1.0 2010-10-22
    * @author Mathias Petersson
    */
    this.parseTable = function(tableID)
    {
        var options = _lib.isValidInputs();
        if(options)
        {
            if(!_this.getTable(options.tableID))
            {
                var table = _this.fetchTable(options.tableID);
                if(table)
                {
                    _storeTable(options.tableID, table);
                }
            }
        }
    };
    this.parseTable.rules =
    {
        required : ['tableID'],
        types :
        {
            tableID : ['string']
        }
    };

    /**
    * Mark a row.
    *
    * @param {Object} row   DOM element. Table row.
    *                       Required.
    * @version 1.0 2010-10-15
    * @author Mathias Petersson
    */
    this.markRow = function(row)
    {
        var options = _lib.isValidInputs();
        if(options)
        {
            if(options.row.nodeName.toUpperCase() == 'TR')
            {
                // setProp + backgroundColor
            }
        }
    };
    this.markRow.rules =
    {
        required : ['row'],
        types :
        {
            row : ['dom']
        }
    };

    /**
    * Adds various class names to table rows and cells.
    *
    * @param {Object} table                 Table.
    *                                       Required.
    * @param {Boolean} oddEven              If to add odd and even.
    *                                       Optional. Default: true
    * @param {Boolean} firstLast            If to add first and last.
    *                                       Optional. Default: true
    * @param {Boolean} rowIdentification    If to add row identification per table.
    *                                       Optional. Default: true
    * @param {Boolean} cellIdentification   If to add cell identification per row.
    *                                       Optional. Default: true
    * @param {Object} classNames            Collection of class names to use.
    *                                       Optional. Default: {odd:'odd', even:'even', first:'first', last:'last', 'cellId':'c', rowId:'r'}
    * @version 1.0 2010-10-13
    * @author Mathias Petersson
    */
    this.setClassNames = function(table, oddEven, firstLast, rowIdentification, cellIdentification, classNames)
    {
        var options = _lib.isValidInputs();
        if(options)
        {
            if(options.table.nodeName.toUpperCase() != 'TABLE')
            {
                return;
            }
            options.oddEven = (options.oddEven !== undefined ? options.oddEven : true);
            options.firstLast = (options.firstLast !== undefined ? options.firstLast : true);
            options.rowIdentification = (options.rowIdentification !== undefined ? options.rowIdentification : true);
            options.cellIdentification = (options.cellIdentification !== undefined ? options.cellIdentification : true);
            options.classNames = (options.classNames !== undefined ? options.classNames : {});
            options.classNames = _lib.mergeObjects(
            {
                odd : 'odd',
                even : 'even',
                first : 'first',
                last : 'last',
                cellId : 'c',
                rowId : 'r'
            }, options.classNames);

            var i, m, i2, m2, cells, rows;
            rows = _lib.getTableRows(options.table);
            for(i = 0, m = rows.length; i < m; ++i)
            {
                cells = _lib.getRowCells(rows[i]);
                if(options.oddEven)
                {
                    _lib.addClass(rows[i], (i % 2 ? options.classNames.even : options.classNames.odd));
                }
                if(options.firstLast && i == 0)
                {
                    _lib.addClass(rows[i], options.classNames.first);
                }
                if(options.firstLast && i + 1 == m)
                {
                    _lib.addClass(rows[i], options.classNames.last);
                }
                if(options.rowIdentification)
                {
                    _lib.addClass(rows[i], options.classNames.rowId + i);
                }
                for(i2 = 0, m2 = cells.length; i2 < m2; ++i2)
                {
                    if(options.oddEven)
                    {
                        _lib.addClass(cells[i2], (i2 % 2 ? options.classNames.even : options.classNames.odd));
                    }
                    if(options.firstLast && i2 == 0)
                    {
                        _lib.addClass(cells[i2], options.classNames.first);
                    }
                    if(options.firstLast && i2 + 1 == m2)
                    {
                        _lib.addClass(cells[i2], options.classNames.last);
                    }
                    if(options.rowIdentification)
                    {
                        _lib.addClass(cells[i2], options.classNames.cellId + i2);
                    }
                }
            }
        }
    };
    this.setClassNames.rules =
    {
        required : ['table'],
        types :
        {
            table : ['dom'],
            oddEven : ['boolean'],
            firstLast : ['boolean'],
            rowIdentification : ['boolean'],
            cellIdentification : ['boolean'],
            classNames : ['object']
        }
    };

    // Initialize script //

    _init(settings);

    return arguments.callee.toString();
};

