// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.

define(function(requirejs) {
    "use strict";

    var $ = requirejs('jquery');
    var utils = requirejs('base/js/utils');

    var Contents = function(options) {
        /**
         * Constructor
         *
         * Preliminary documentation for the REST API is at 
         * https://github.com/ipython/ipython/wiki/IPEP-27%3A-Contents-Service
         *
         * A contents handles passing file operations
         * to the back-end.  This includes checkpointing
         * with the normal file operations.
         *
         * Parameters:
         *  options: dictionary
         *      Dictionary of keyword arguments.
         *          base_url: string
         */
        this.base_url = options.base_url;
    };

    /** Error type */
    Contents.DIRECTORY_NOT_EMPTY_ERROR = 'DirectoryNotEmptyError';

    Contents.DirectoryNotEmptyError = function() {
        // Constructor
        //
        // An error representing the result of attempting to delete a non-empty
        // directory.
        this.message = 'A directory must be empty before being deleted.';
    };
    
    Contents.DirectoryNotEmptyError.prototype = Object.create(Error.prototype);
    Contents.DirectoryNotEmptyError.prototype.name =
        Contents.DIRECTORY_NOT_EMPTY_ERROR;


    Contents.prototype.api_url = function() {
        var url_parts = [
            this.base_url, 'api/contents',
            utils.url_join_encode.apply(null, arguments),
        ];
        return utils.url_path_join.apply(null, url_parts);
    };

    /**
     * Creates a basic error handler that wraps a jqXHR error as an Error.
     *
     * Takes a callback that accepts an Error, and returns a callback that can
     * be passed directly to $.ajax, which will wrap the error from jQuery
     * as an Error, and pass that to the original callback.
     *
     * @method create_basic_error_handler
     * @param{Function} callback
     * @return{Function}
     */
    Contents.prototype.create_basic_error_handler = function(callback) {
        if (!callback) {
            return utils.log_ajax_error;
        }
        return function(xhr, status, error) {
            callback(utils.wrap_ajax_error(xhr, status, error));
        };
    };

    /**
     * File Functions (including notebook operations)
     */

    /**
     * Get a file.
     *
     * @method get
     * @param {String} path
     * @param {Object} options
     *    type : 'notebook', 'file', or 'directory'
     *    format: 'text' or 'base64'; only relevant for type: 'file'
     *    content: true or false; // whether to include the content
     */
    Contents.prototype.get = function (path, options) {
        /**
         * We do the call with settings so we can set cache to false.
         */
        var settings = {
            processData : false,
            cache : false,
            type : "GET",
            dataType : "json",
        };
        var url = this.api_url(path);
        var params = {};
        if (options.type) { params.type = options.type; }
        if (options.format) { params.format = options.format; }
        if (options.content === false) { params.content = '0'; }
        return utils.promising_ajax(url + '?' + $.param(params), settings);
    };


    /**
     * Creates a new untitled file or directory in the specified directory path.
     *
     * @method new
     * @param {String} path: the directory in which to create the new file/directory
     * @param {Object} options:
     *      ext: file extension to use
     *      type: model type to create ('notebook', 'file', or 'directory')
     */
    Contents.prototype.new_untitled = function(path, options) {
        var data = JSON.stringify({
          ext: options.ext,
          type: options.type
        });

        var settings = {
            processData : false,
            type : "POST",
            data: data,
            contentType: 'application/json',
            dataType : "json",
        };
        return utils.promising_ajax(this.api_url(path), settings);
    };

    Contents.prototype.delete = function(path) {
        var settings = {
            processData : false,
            type : "DELETE",
            dataType : "json",
        };
        var url = this.api_url(path);
        return utils.promising_ajax(url, settings).catch(
            // Translate certain errors to more specific ones.
            function(error) {
                // TODO: update IPEP27 to specify errors more precisely, so
                // that error types can be detected here with certainty.
                if (error.xhr.status === 400) {
                    throw new Contents.DirectoryNotEmptyError();
                }
                throw error;
            }
        );
    };

    Contents.prototype.rename = function(path, new_path) {
        var data = {path: new_path};
        var settings = {
            processData : false,
            type : "PATCH",
            data : JSON.stringify(data),
            dataType: "json",
            contentType: 'application/json',
        };
        var url = this.api_url(path);
        return utils.promising_ajax(url, settings);
    };

    Contents.prototype.trust = function(path) {
        var settings = {
            processData : false,
            type : "POST",
            contentType: 'application/json',
        };
        var url = this.api_url(path, "trust");
        return utils.promising_ajax(url, settings);
    }

    Contents.prototype.save = function(path, model) {
        /**
         * We do the call with settings so we can set cache to false.
         */
        var settings = {
            processData : false,
            type : "PUT",
            dataType: "json",
            data : JSON.stringify(model),
            contentType: 'application/json',
        };
        var url = this.api_url(path);
        return utils.promising_ajax(url, settings);
    };
    
    Contents.prototype.copy = function(from_file, to_dir) {
        /**
         * Copy a file into a given directory via POST
         * The server will select the name of the copied file
         */
        var url = this.api_url(to_dir);
        
        var settings = {
            processData : false,
            type: "POST",
            data: JSON.stringify({copy_from: from_file}),
            contentType: 'application/json',
            dataType : "json",
        };
        return utils.promising_ajax(url, settings);
    };

    /**
     * Checkpointing Functions
     */

    Contents.prototype.create_checkpoint = function(path) {
        var url = this.api_url(path, 'checkpoints');
        var settings = {
            type : "POST",
            contentType: false,  // no data
            dataType : "json",
        };
        return utils.promising_ajax(url, settings);
    };

    Contents.prototype.list_checkpoints = function(path) {
        var url = this.api_url(path, 'checkpoints');
        var settings = {
            type : "GET",
            cache: false,
            dataType: "json",
        };
        return utils.promising_ajax(url, settings);
    };

    Contents.prototype.restore_checkpoint = function(path, checkpoint_id) {
        var url = this.api_url(path, 'checkpoints', checkpoint_id);
        var settings = {
            type : "POST",
            contentType: false,  // no data
        };
        return utils.promising_ajax(url, settings);
    };

    Contents.prototype.delete_checkpoint = function(path, checkpoint_id) {
        var url = this.api_url(path, 'checkpoints', checkpoint_id);
        var settings = {
            type : "DELETE",
        };
        return utils.promising_ajax(url, settings);
    };

    /**
     * File management functions
     */

    /**
     * List notebooks and directories at a given path
     *
     * On success, load_callback is called with an array of dictionaries
     * representing individual files or directories.  Each dictionary has
     * the keys:
     *     type: "notebook" or "directory"
     *     created: created date
     *     last_modified: last modified dat
     * @method list_notebooks
     * @param {String} path The path to list notebooks in
     */
    Contents.prototype.list_contents = function(path) {
        return this.get(path, {type: 'directory'});
    };

    return {'Contents': Contents};
});
