/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

'use strict';

const vm = require('vm');
const utils = require('../utils');
const types = require('../types');

/**
 * @ignore
 */
class ResultMapper {
  /**
   * Gets a generated function to adapt the row to a document.
   * @param {ModelMappingInfo} info
   * @param {ResultSet} rs
   * @returns {Function}
   */
  static getSelectAdapter(info, rs) {
    const columns = rs.columns;
    if (!columns) {
      throw new Error('Expected ROWS result obtained VOID');
    }

    let scriptText = '(function rowAdapter(row, info) {\n' +
      '  const item = info.newInstance();\n';

    for (const c of columns) {
      scriptText += `  item['${info.getPropertyName(c.name)}'] = `;

      if (!info.getToModelFn(c.name)) {
        scriptText += `row['${c.name}'];\n`;
      } else {
        scriptText += `info.getToModelFn('${c.name}')(row['${c.name}']);\n`;
      }
    }

    scriptText += '  return item;\n})';

    const script = new vm.Script(scriptText, { filename: 'gen-result-mapper.js'});
    return script.runInThisContext();
  }

  /**
   * Gets a function used to adapt VOID results or conditional updates.
   * @param {ResultSet} rs
   * @returns {Function}
   */
  static getMutationAdapter(rs) {
    if (rs.columns === null) {
      // VOID result
      return utils.noop;
    }

    if (
      rs.columns.length === 1 && rs.columns[0].name === '[applied]' &&
      rs.columns[0].type.code === types.dataTypes.boolean) {
      return utils.noop;
    }

    return ResultMapper._getConditionalRowAdapter(rs);
  }

  static _getConditionalRowAdapter(rs) {
    return (function conditionalRowAdapter(row, info) {
      const item = info.newInstance();

      // Skip the first column ("[applied]")
      for (let i = 1; i < rs.columns.length; i++) {
        const c = rs.columns[i];
        item[info.getPropertyName(c.name)] = row[c.name];
      }

      return item;
    });
  }

  /**
   * @param {ModelMappingInfo} info
   * @param {ResultSet} rs
   * @returns {{canCache: Boolean, fn: Function}}
   */
  static getCustomQueryAdapter(info, rs) {
    if (rs.columns === null || rs.columns.length === 0) {
      // VOID result
      return { canCache: true, fn: utils.noop };
    }

    if (rs.columns[0].name === '[applied]' && rs.columns[0].type.code === types.dataTypes.boolean) {
      // Conditional update results adapter functions should not be cached
      return { canCache: false, fn: ResultMapper._getConditionalRowAdapter(rs) };
    }

    return { canCache: true, fn: ResultMapper.getSelectAdapter(info, rs) };
  }
}

module.exports = ResultMapper;