text: /*
   * Copyright 2015 IBM Corp.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  module.exports = function(kernelP) {
    return (function() {
  
      var Utils = require('../utils.js');
  
      var gKernelP = kernelP;
  
      /*
       * NOTE: the following have not been implemented as they do not make sense for JavaScript
       *
       * <K,V> java.util.Map<K,V>    getJavaMap(int i)
       * Returns the value at position i of array type as a Map.
       * <T> java.util.List<T>   getList(int i)
       * Returns the value at position i of array type as List.
       * <K,V> scala.collection.Map<K,V> getMap(int i)
       * Returns the value at position i of map type as a Scala Map.
       * <T> scala.collection.Seq<T> getSeq(int i)
       * Returns the value at position i of array type as a Scala Seq.
       * <T> scala.collection.immutable.Map<java.lang.String,T>  getValuesMap(scala.collection.Seq<java.lang.String> fieldNames)
       * Returns a Map(name -> value) for the requested fieldNames
       * scala.collection.Seq<java.lang.Object>  toSeq()
       * Return a Scala Seq representing the row.
       *
       * NOTE: the following are being ignored as they also don't make sense for JavaScript (see ./types/DataTypes.js)
       *
       * byte getByte(int i)
       * decimal getDecimal(int i)
       * long getLong(int i)
       * short getShort(int i)
       */
  
  // Local resolve functions to parse results of various types
      function _resolveBool(result, resolve, reject) {
        // parse stringified result here
        resolve(JSON.parse(result));
      }
  
      function _resolveFloat(result, resolve, reject) {
        resolve(parseFloat(result));
      }
  
      function _resolveInt(result, resolve, reject) {
        resolve(parseInt(result));
      }
  
      function _resolveObj(result, resolve, reject) {
        // have to parse if number or bool
        resolve(isFinite(result) ? new Number(result).valueOf() : isBool(result) ? JSON.parse(result) : result);
      }
  
      function isBool (val) {
        return val === 'true' || val === 'false';
      }
  
      function deepEquals(obj1, obj2) {
        var isEqual = false;
  
        if (typeof(obj1) === typeof(obj2)) {
          if (Array.isArray(obj1)) {
            if (obj1.length == obj2.length) {
              isEqual = obj1.some(function(val, index) {
                return val === obj2[index];
              });
            }
          } else if (obj1 !== null && typeof(obj1) === 'object') {
            var keys1 = [], keys2 = [], values1 = [], values2 = [];
  
            for (var key in obj1) {
              keys1.push(key);
              values1.push(obj1[key]);
            }
  
            for (var key in obj2) {
              keys2.push(key);
              values2.push(obj2[key]);
            }
  
            if (deepEquals(keys1, keys2) && deepEquals(values1, values2)) {
              isEqual = true;
            }
          } else {
            isEqual = (obj1 === obj2);
          }
        }
  
        return isEqual;
      }
  
      function areTwoRowsEqual(row1, row2) {
        var isEqual = row1 instanceof Row && row2 instanceof Row && deepEquals(row1._schema, row2._schema) && deepEquals(row1._values, row2._values);
  
        return isEqual;
      }
  
      function getByIndex(row, index) {
        var obj = {value: null, schema: null};
  
        if (row._values) {
          if (index < row._values.length) {
            obj.value = row._values[index];
            obj.schema = row._schema.fields[index];
          } else {
            throw new Error('Index ' + index + ' is out of bounds');
          }
        }
  
        return obj;
      }
  
      /**
       * @constructor
       * @memberof module:eclairjs/sql
       * @classdesc Represents one row of output from a relational operator. Allows both generic access by ordinal, which will incur boxing overhead for primitives, as well as native primitive access.
       * It is invalid to use the native primitive interface to retrieve a value that is null, instead a user must check isNullAt before attempting to retrieve a value that might be null.
       * To create a new Row, use RowFactory.create()
       */
      function Row() {
        if (arguments && arguments.length == 2 && arguments[0] instanceof Promise && arguments[1] instanceof Promise) {
          this.kernelP = arguments[0];
          this.refIdP = arguments[1];
        } else {
          // local copy of data
          this._values = arguments[0];
          this._schema = arguments[1];
          this._eclairLocal = true;
        }
      }
  
      /**
       * Returns true if there are any NULL values in this row.
       * @returns {boolean}
       */
      Row.prototype.anyNull = function() {
        var result = false;
  
        if (this._values) {
          result = this._values.some(function(item) {
            return item === null;
          });
        }
  
        return result;
      };
  
      /**
       * Returns the value at position index.
       * @param index
       * @returns {object}
       */
      Row.prototype.apply = function(index) {
        return getByIndex(this, index).value;
      };
  
      /**
       * Make a copy of the current Row object
       * @returns {module:eclairjs/sql.Row}
       */
      Row.prototype.copy = function() {
        return new Row(this._values, this._schema);
      };
  
      /**
       * compares object obj to this Row object
       * @param {object} obj
       * @returns {boolean}
       */
      Row.prototype.equals = function(obj) {
        return areTwoRowsEqual(this, obj);
      };
  
      Row.prototype._fieldIndex = function(name) {
        var index = -1;
  
        if (this._schema) {
          this._schema.fields.some(function(field, i) {
            if (field.name === name) {
              index = i;
              return true;
            } else {
              return false;
            }
          });
        }
  
        if (index >= 0) {
          return index;
        } else {
          throw new Error('field "'+name+'" does not exist');
        }
      };
  
      /**
       * Returns the index of a given field name.
       * @param {string} name
       * @returns {integer}
       */
      Row.prototype.fieldIndex = function(name) {
        if (this._schema) {
          return this._fieldIndex(name);
        } else {
          throw new Error('fieldIndex on a Row without a schema is undefined');
        }
      };
  
      /**
       * Returns the value at position index.
       * @param {integer} index
       * @returns {object}
       */
      Row.prototype.get = function(index) {
        return getByIndex(this, index).value;
      };
  
      /**
       * Returns the value at position index as a primitive boolean.
       * @param {integer} index
       * @returns {boolean}
       */
      Row.prototype.getBoolean = function(index) {
        var item = getByIndex(this, index);
  
        if (item.schema.dataType === 'boolean') {
          return item.value;
        } else {
          throw new Error('the type for index '+index+' is not a Boolean');
        }
      };
  
      /**
       * Returns the value at position idex as a primitive byte.
       * @param {integer} index
       * @returns {byte}
       * @ignore
       */
      /* Not applicable to JavaScript
       Row.prototype.getByte = function(index) {
  
       throw {name:'NotImplementedException', message:'The method is not implemented for JavaScript'};
       };
       */
  
      /**
       * Returns the value at position index of type as Date.
       * @param {integer} index
       * @returns {Date}
       */
      Row.prototype.getDate = function(index) {
        var item = getByIndex(this, index);
  
        if (item.schema.dataType === 'date') {
          return new Date(item.value);
        } else {
          throw new Error('the type for index '+index+' is not a Date');
        }
      };
  
      /**
       * Returns the value at position index of type as decimal.
       * @param {integer} index
       * @returns {Promise.<decimal>}
       * @ignore
       */
      /* Not applicable to JavaScript
       Row.prototype.getDecimal = function(index) {
  
       throw {name:'NotImplementedException', message:'The method is not implemented for JavaScript'};
       };
       */
  
      /**
       * Returns the value at position index of type as double.
       * @param {integer} index
       * @returns {double}
       */
      Row.prototype.getDouble = function(index) {
        var item = getByIndex(this, index);
  
        if (item.schema.dataType === 'double') {
          return item.value;
        } else {
          throw new Error('the type for index '+index+' is not a Double');
        }
      };
  
      /**
       * Returns the value at position index of type as float.
       * @param {integer} index
       * @returns {float}
       */
      Row.prototype.getFloat = function(index) {
        var item = getByIndex(this, index);
  
        if (item.schema.dataType === 'double') {
          return item.value;
        } else {
          throw new Error('the type for index '+index+' is not a Float');
        }
      };
  
      /**
       * Returns the value at position index of type as integer.
       * @param {integer} index
       * @returns {integer}
       */
      Row.prototype.getInt = function(index) {
        var item = getByIndex(this, index);
  
        if (item.schema.dataType === 'integer') {
          return item.value;
        } else {
          throw new Error('the type for index '+index+' is not a Integer');
        }
      };
  
      /**
       * Returns the value at position index of type as long.
       * @param {integer} index
       * @returns {long}
       * @ignore
       */
      Row.prototype.getLong = function(index) {
        var item = getByIndex(this, index);
  
        if (item.schema.dataType === 'long') {
          return item.value;
        } else {
          throw new Error('the type for index '+index+' is not a Long');
        }
      };
  
      /**
       * Returns the value at position index of type as short.
       * @param {integer} index
       * @returns {short}
       * @ignore
       */
      /* Not applicable to JavaScript
       Row.prototype.getShort = function(index) {
  
       throw {name:'NotImplementedException', message:'The method is not implemented for JavaScript'};
       };
       */
  
      /**
       * Returns the value at position index of type as String.
       * @param {integer} index
       * @returns {String}
       */
      Row.prototype.getString = function(index) {
        var item = getByIndex(this, index);
  
        if (item.schema.dataType === 'string') {
          return item.value;
        } else {
          throw new Error('the type for index '+index+' is not a String');
        }
      };
  
      /**
       * Returns the value at position index of struct type as a Row object.
       * @param {integer} index
       * @returns {module:eclairjs/sql.Row}
       */
      Row.prototype.getStruct = function(index) {
        throw "not implemented by ElairJS";
        /*
         var args = {
         target: this,
         method: 'getStruct',
         args: Utils.wrapArguments(arguments),
         returnType: Row
         };
  
         return Utils.generate(args);
         */
      };
  
      /**
       * Returns the value at position index of date type as Date.
       * @param {integer} index
       * @returns {Date}
       */
      Row.prototype.getTimestamp = function(index) {
        var item = getByIndex(this, index);
  
        if (item.schema.dataType === 'timestamp') {
          return new Date(item.value);
        } else {
          throw new Error('the type for index '+index+' is not a Timestamp');
        }
      };
  
      /**
       * Checks whether the value at position index is null.
       * @param {integer} index
       * @returns {boolean}
       */
      Row.prototype.isNullAt = function(index) {
        return (getByIndex(this, index).value === null);
      };
  
      /**
       * Number of elements in the Row.
       * @returns {Promise.<integer>}
       */
      Row.prototype.length = function() {
        return this._values.length;
      };
  
      /**
       * Displays all elements of this traversable or iterator in a string using start, end, and separator strings.
       * @param {string} [separator]
       * @param {string} [start] start will be ignored if end parameter is not specified
       * @param {string} [end] Required if start specified
       * @returns {Promise.<string>}
       */
      Row.prototype.mkString = function() {
        var str = '';
        var args = arguments;
  
        if (args.length == 3) {
          str += args[0];
        }
  
        this._values.forEach(function (value, i, values) {
          str += value;
  
          if (i < values.length - 1) {
            if (args.length == 1) {
              str += args[0];
            } else if (args.length == 3) {
              str += args[1];
            }
          }
        });
  
        if (args.length == 3) {
          str += arguments[2];
        }
  
        return str;
      };
  
      /**
       * Schema for the row.
       * @returns {module:eclairjs/sql/types.StructType}
       */
      Row.prototype.schema = function() {
        var StructType = require('./types/StructType.js')(gKernelP);
  
        var StructField = require('./types/StructField.js')(gKernelP);
        var Metadata = require('./types/Metadata.js')(gKernelP);
        var DataTypes = require('./types/DataTypes.js')(gKernelP);
  
        var fields = [];
  
        this._schema.fields.forEach(function (field) {
          var dt;
  
          // TODO: make this easier, perhaps a method in datatypes?
          switch (field.dataType) {
            case 'boolean':
              dt = DataTypes.BooleanType;
              break;
  
            case 'date':
              dt = DataTypes.DateType;
              break;
  
            case 'double':
              dt = DataTypes.DoubleType;
              break;
  
            case 'float':
              dt = DataTypes.FloatType;
              break;
  
            case 'integer':
              dt = DataTypes.IntegerType;
              break;
  
            case 'timestamp':
              dt = DataTypes.TimestampType;
              break;
  
            case 'string':
              dt = DataTypes.StringType;
              break;
          }
  
          fields.push(new StructField(field.name, dt, field.nullable, Metadata.empty()));
        });
  
        return new StructType(fields);
      };
  
      /**
       * Number of elements in the Row.
       * @returns {integer}
       */
      Row.prototype.size = function() {
        return this._values.length;
      };
  
      Row.prototype.toJSON = function() {
        if (this._schema) {
          var result = {};
          var myThis = this;
          this._schema.fields.forEach(function(field, i) {
            result[field.name] = myThis._values[i];
          }.bind(this));
  
          return result;
        } else {
          return this._values;
        }
      };
  
      Row.prototype._generateRemote = function() {
        var RowFactory = require('./RowFactory')(gKernelP);
  
        return RowFactory.createRemote(this._values);
      };
  
      Row.moduleLocation = '/sql/Row';
  
      return Row;
    })();
  };