/*
 * BSD 3-Clause License
 *
 * Copyright (c) 2017, MapCreator
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 *  Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

import {AbstractClassError} from '../../errors/AbstractError';
import PaginatedResourceListing from '../../PaginatedResourceListing';
import ResourceBase from './ResourceBase';

/**
 * Base of all resource items that support Crud operations
 * @abstract
 */
export default class CrudBase extends ResourceBase {
  /**
   * @param {Maps4News} api - Api instance
   * @param {Object<String, *>} data - Item data
   */
  constructor(api, data = {}) {
    super(api, data);

    if (this.constructor === CrudBase) {
      throw new AbstractClassError();
    }
  }

  /**
   * Build data for create operation
   * @returns {Object<String, *>} - Create data
   * @protected
   */
  _buildCreateData() {
    this._updateProperties();

    const out = {};
    const keys = [].concat(
      Object.keys(this._properties),
      Object.keys(this._baseProperties),
    ).filter((item, pos, self) => self.indexOf(item) === pos);

    for (const key of keys) {
      out[key] = this._properties[key] || this._baseProperties[key];
    }

    delete out.id;
    return out;
  }

  /**
   * Macro for resource listing
   * @param {ResourceBase} Target - Target object
   * @param {?String} url - Target url, if null it will guess
   * @param {Number} [page=1] - Page number
   * @param {Number} [perPage= this.api.defaults.perPage] - Amount of items per page
   * @returns {Promise} - Resolves with {@link PaginatedResourceListing} instance and rejects with {@link ApiError}
   * @protected
   * @deprecated
   */
  _listResource(Target, url = null, page = 1, perPage = this.api.defaults.perPage) {
    if (!url) {
      const resource = (new Target(this.api)).resourceName.replace(/s+$/, '');

      url = `${this.url}/${resource}s`;
    }

    const resolver = new PaginatedResourceListing(this.api, url, Target);

    return resolver.getPage(page, perPage);
  }

  /**
   * Save item. This will create a new item if `id` is unset
   * @returns {Promise} - Resolves with {@link CrudBase} instance and rejects with {@link ApiError}
   */
  save() {
    return !this.id ? this._create() : this._update();
  }

  /**
   * Store new item
   * @returns {Promise} - Resolves with {@link CrudBase} instance and rejects with {@link ApiError}
   * @private
   */
  _create() {
    return new Promise((resolve, reject) => {
      this.api
        .request(this.baseUrl, 'POST', this._buildCreateData())
        .catch(reject)
        .then(data => {
          this._properties = {};
          this._baseProperties = data;

          resolve(this);
        });
    });
  }

  /**
   * Update existing item
   * @returns {Promise} - Resolves with {@link CrudBase} instance and rejects with {@link ApiError}
   * @private
   */
  _update() {
    this._updateProperties();

    return new Promise((resolve, reject) => {
      this.api
        .request(this.url, 'PATCH', this._properties)
        .catch(reject)
        .then(() => resolve(this));
    });
  }

  /**
   * Delete item
   * @returns {Promise} - Resolves with an empty {@link Object} and rejects with {@link ApiError}
   */
  delete() {
    return this.api.request(this.url, 'DELETE');
  }

  /**
   * Restore item
   * @returns {Promise} - Resolves with {@link CrudBase} instance and rejects with {@link ApiError}
   */
  restore() {
    return new Promise((resolve, reject) => {
      this.api.request(this.url, 'PUT')
        .catch(reject)
        .then(data => resolve(new this(this.api, data)));
    });
  }
}

@@ -2656,794 +2656,8 @@
  %7D%0A%0A
-  /**%0A   * Macro for resource listing%0A   * @param %7BResourceBase%7D Target - Target object%0A   * @param %7B?String%7D url - Target url, if null it will guess%0A   * @param %7BNumber%7D %5Bpage=1%5D - Page number%0A   * @param %7BNumber%7D %5BperPage= this.api.defaults.perPage%5D - Amount of items per page%0A   * @returns %7BPromise%7D - Resolves with %7B@link PaginatedResourceListing%7D instance and rejects with %7B@link ApiError%7D%0A   * @protected%0A   * @deprecated%0A   */%0A  _listResource(Target, url = null, page = 1, perPage = this.api.defaults.perPage) %7B%0A    if (!url) %7B%0A      const resource = (new Target(this.api)).resourceName.replace(/s+$/, '');%0A%0A      url = %60$%7Bthis.url%7D/$%7Bresource%7Ds%60;%0A    %7D%0A%0A    const resolver = new PaginatedResourceListing(this.api, url, Target);%0A%0A    return resolver.getPage(page, perPage);%0A  %7D%0A%0A
   /*

Remove deprecated CrudBase::_listResource