// o---------------------------------------------------------------------------------o
    // | This file is part of the RGraph package - you can learn more at:                |
    // |                                                                                 |
    // |                       https://www.rgraph.net/license.html                       |
    // |                                                                                 |
    // | RGraph is dual-licensed under the Open Source GPL license. That means that it's |
    // | free to use and there are no restrictions on what you can use RGraph for!       |
    // | If the GPL license does not suit you however, then there's an inexpensive       |
    // | commercial license option available. See the URL above for more details.        |
    // o---------------------------------------------------------------------------------o

    //
    // Initialise the various objects
    //
    RGraph = window.RGraph || {isrgraph:true,isRGraph: true,rgraph:true};

// Module pattern
(function (win, doc, undefined)
{
    RGraph.Sheets = function ()
    {
        var args = RGraph.Sheets.getArgs(arguments, 'oauth,key,worksheet,callback');

        // Allow three args to be given as well as four
        if (arguments.length === 1) {
            // Nothing to do here
            
        } else if (arguments.length === 3) {
            args.oauth     = args.oauth;
            args.key       = args.key;
            args.callback  = args.worksheet; // Need to set this because the order of the args is wrong
            args.worksheet = 'Sheet1';

        } else if (arguments.length === 4) {
            args.worksheet = args.worksheet;
        }

        var worksheet = args.worksheet,
            callback  = args.callback,
            letters   = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
            key       = args.key,
            oauth     = args.oauth;

        var url = ('https://sheets.googleapis.com/v4/spreadsheets/[KEY]/values/[WORKSHEET]?alt=json&key=[OAUTH_KEY]')
                   .replace(/\[KEY\]/, args.key)
                   .replace(/\[WORKSHEET\]/, encodeURIComponent(args.worksheet))
                   .replace(/\[OAUTH_KEY\]/, args.oauth)






        
        

        //
        // Loads the spreadsheet
        //
        this.load = function(url, userCallback)
        {
            var obj = this;
            
            RGraph.Sheets.AJAX.getJSON(url, function (json)
            {
                var grid  = json.values;
                var rows  = grid.length;
                var cells = 0;

                //
                // Determine the longest row
                //
                for (var i=0; i<grid.length; ++i) {
                    cells = Math.max(cells, grid[i].length);
                }
                
                //
                // Now that the max row length has been determined go through
                // the grid and pad out each row
                //
                for (var i=0; i<grid.length; ++i) {
                    for (var j=grid[i].length; j<cells; ++j) {
                        grid[i][j] = '';
                    }
                }
                
                // Convert numerics strings to numbers
                // eg "234"  =>  234
                for (var i=0; i<grid.length; ++i) {
                    for (var j=0; j<cells; ++j) {
                        if (grid[i][j].match(/^[0-9]+$/)) {
                            grid[i][j] = parseInt(grid[i][j]);
                        } else if (grid[i][j].match(/^[0-9.]+$/)) {
                            grid[i][j] = parseFloat(grid[i][j]);
                        }
                    }
                }

                //
                // Save the grid on the object
                //
                obj.data = grid;



                //
                // Call the users callback function. Users can access the raw data if
                // they want with the obj.data property or by using the accessor
                // methods (better)
                //
                userCallback(obj);
            });
        };








        //
        // Fetches a row of data and returns it
        //
        // @param id    number The numeric index of the column to fetch (starts at 1)
        // @param start number The index to start fetching/returning at. The first
        //                     character is 1
        // @param opt        An option object containing options
        //
        this.row = function (index, start)
        {
            var opt = {}, row;
            
            // Does the row exist?
            if (!this.data[index - 1]) {
                return [];
            }
            
            // Default for start is 1
            start = start || 1;

            //
            // Parse the .trim argument
            //

            if (arguments && typeof arguments[2] === 'object' && typeof arguments[2].trim === 'boolean') {
                opt.trim = arguments[2].trim;
            } else {
                opt.trim = true;
            }

            row = this.data[index - 1].slice(start - 1);

            // Trim the array if required
            if (opt.trim) {
                row = RGraph.Sheets.arrayRTrim(row);
            }

            return row;
        };








        //
        // Fetches a column of data and returns it
        //
        // @param id    number The letter that corresponds to the column
        // @param start number The index to start fetching/returning at. The first
        //                     character is 1
        // @param opt        An option object containing options
        //
        this.col = function (index, start)
        {
            var opt = {},
                col = [];
          
            // Default for start is 1
            start = start || 1;

            if (arguments && typeof arguments[2] === 'object' && typeof arguments[2].trim === 'boolean') {
                opt.trim = arguments[2].trim;
            } else {
                opt.trim = true;
            }
            
            for (var i=0; i<this.data.length; ++i) {
                col.push(this.data[i][index - 1]);
            }

            // Trim the array if required
            if (opt.trim) {
                col = RGraph.Sheets.arrayRTrim(col);
            }

            // Now account for the start index

            col = col.slice(start - 1);

            return col;
        };








        //
        // Returns the index (zero index) of the given letters
        //
        this.getIndexOfLetters = function (l)
        {
            var parts = l.split('');

            if (parts.length === 1) {
                return letters.indexOf(l) + 1;
            } else if (parts.length === 2){
                var idx = ((letters.indexOf(parts[0]) + 1) * 26) + (letters.indexOf(parts[1]) + 1);
                return idx;
            }
        };








        //
        // The get method makes retrieving cells very straightforward,
        // for example: obj.get('B1');
        //
        // @param str string The cells(s) to fetch
        // @param     string Optional set of options that are passed
        //                   to the relevant row/col function
        //
        this.get = function (str)
        {
            // Uppercase letters please!
            str = str.toUpperCase();

            //
            // Handle the style of .get('C') or .get('AA'
            //
            if (str.match(/^\s*([a-z]+)\s*$/i)) {
            
                str = RegExp.$1;
            
                if (str.length === 1) {
                    var index = letters.indexOf(str) + 1;
                    return this.col(index, 1, arguments[1]);
                
                } else if (str.length === 2) {
                    var index = ((letters.indexOf(str[0]) + 1) * 26) + letters.indexOf(str[1]) + 1;
                    return this.col(index, 1, arguments[1]);
                }
            }
            
            
            
            
            //
            // Handle the style of .get('2');
            //(fetching a whole row
            //
            if (str.match(/^\s*[0-9]+\s*$/i)) {
                return this.row(str, null, arguments[1]);
            }
            
            
            
            
            //
            // Handle the style of .get('E2');
            //(fetching a single cell)
            //
            if (str.match(/^\s*([a-z]{1,2})([0-9]+)\s*$/i)) {
                
                var letter = RegExp.$1,
                    number = RegExp.$2,
                    col    = this.get(letter, {trim: false});

                
                return col[number - 1];
            }
            
            
            
            
            //
            // Handle the style of .get('B2:E2');
            //(fetching the B2 cell to the E2 cell)
            //
            if (str.match(/^\s*([a-z]{1,2})([0-9]+)\s*:\s*([a-z]{1,2})([0-9]+)\s*$/i)) {

                var letter1 = RegExp.$1,
                    letter2 = RegExp.$3,
                    number1 = parseInt(RegExp.$2),
                    number2 = parseInt(RegExp.$4)


                // A column
                if (letter1 === letter2) {
                    var cells = [],
                        index = this.getIndexOfLetters(letter1),
                        col   = this.col(index, null, {trim: false});

                    for (var i=(number1 - 1); i<=(number2 - 1); ++i) {
                        cells.push(col[i]);
                    }

                // A row
                } else if (number1 === number2) {

                    var cells = [],
                        row   = this.row(number1, null, {trim: false}),
                        index1 = this.getIndexOfLetters(letter1),
                        index2 = this.getIndexOfLetters(letter2)

                    for (var i=(index1 - 1); i<=(index2 - 1); ++i) {
                        cells.push(row[i]);
                    }
                
                // A matrix
                } else if (letter1 !== letter2 && number1 !== number2) {

                    var cells  = [],
                        index1 = this.getIndexOfLetters(letter1),
                        index2 = this.getIndexOfLetters(letter2),
                        row    = [];

                    for (var i=number1; i<=number2; ++i) {
                        row = this.row(i).slice(index1 - 1, index2); // Don't need to subtract 1 here as we want to be inclusive
                        cells.push(row);
                    }
                }

                // Trim the results
                if (arguments[1] && arguments[1].trim === false) {
                    // Nada
                } else {
                    cells = RGraph.Sheets.arrayRTrim(cells);
                }

                return cells;
               
            }
        };








        //
        // Load the data
        //
        this.load(url, args.callback);
    };








    // This function trims an array of empty values. Its here so that the Google
    // Sheets code can be used without the RGraph.common.core file

    //
    // An arrayRtrim function that removes the empty elements off
    // both ends
    //
    // @param  array arr The array to trim.
    // @return array     The array
    //
    RGraph.Sheets.arrayRTrim = function (arr)
    {
        var out = [], content = false;

        // Trim the end
        for (var i=(arr.length - 1); i>=0; i--) {
            if (arr[i] || content) {
                out.push(arr[i]);
                content = true;
            }
        }
        
        arr = out.reverse();
        
        return out;
    };








    //
    // This function allows both object based arguments to functions
    // and also regular arguments as well.
    //
    // You can call it from inside a function like this:
    //
    // args = RGraph.Sheets.getArgs(arguments, 'object,id,foo,bar');
    //
    // So you're passing it the arguments object and a comma seperated list of names
    // for the arguments.
    //
    // @param array args   The arguments object that you get when inside a function
    // @param string names A comma seperated list of desired names for the arguments
    //                     eg: 'object,color,size'
    //
    RGraph.Sheets.getArgs = function (args, names)
    {
        var ret   = {};
        var count = 0;
        names     = names.trim().split(/ *, */);

        if (   args
            && args[0]
            && args.length === 1
            && typeof args[0][names[0]] !== 'undefined') {
            
            for (var i=0; i<names.length; ++i) {
                if (typeof args[0][names[i]] === 'undefined') {
                    args[0][names[i]] = null;
                }
            }

            return args[0];
        } else {
            for (var i in names) {
                ret[names[i]] = typeof args[count] === 'undefined' ? null : args[count];
                
                count += 1;
            }
        }

        return ret;
    };








    //
    // Makes an AJAX call. It calls the given callback (a function) when ready
    // 
    // @param  args object An object consisting of:
    //                      o url
    //                      o callback
    // OR
    //
    // @param string   url      The URL to retrieve
    // @param function callback A function that is called when the response is ready,
    //                          there's an example below called "myCallback".
    //
    RGraph.Sheets.AJAX = function ()
    {
        var args = RGraph.Sheets.getArgs(arguments, 'url,callback');

        // Mozilla, Safari, ...
        if (window.XMLHttpRequest) {
            var httpRequest = new XMLHttpRequest();
            

        // MSIE
        } else if (window.ActiveXObject) {
            var httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
        }

        httpRequest.onreadystatechange = function ()
        {
            if (this.readyState == 4 && this.status == 200) {
                this.__user_callback__ = args.callback;
                this.__user_callback__(this.responseText);
            }
        }

        httpRequest.open('GET', args.url, true);
        
        // Set a Cache-Control header
        if (httpRequest && httpRequest.setRequestHeader) {
            httpRequest.setRequestHeader('Cache-Control', 'no-cache');
        }
        
        httpRequest.send();
    };








    //
    // Uses the above function but calls the call back passing JSON (ie a JavaScript object ) as its argument
    // 
    // @param  args object An object consisting of:
    //                      o url
    //                      o callback
    // OR
    //
    // @param url string The URL to fetch
    // @param callback function Your callback function (which is passed the JSON object as an argument)
    //
    RGraph.Sheets.AJAX.getJSON = function ()
    {
        var args = RGraph.Sheets.getArgs(arguments, 'url,callback');

        RGraph.Sheets.AJAX(args.url, function ()
        {
            var json = eval('(' + this.responseText + ')');

            args.callback(json);
        });
    };








// End module pattern
})(window, document);