/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an 'AS IS' basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Bespin.
 *
 * The Initial Developer of the Original Code is
 * Mozilla.
 * Portions created by the Initial Developer are Copyright (C) 2009
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Bespin Team (bespin@mozilla.com)
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

var SC = require('sproutcore/runtime').SC;
var catalog = require('bespin:plugins').catalog;
var console = require('bespin:console').console;
var Trace = require('bespin:util/stacktrace').Trace;

var environment = require('canon:environment');
var keyboard = require('canon:keyboard');
var Request = require('canon:request').Request;

var hint = require('command_line:hint');
var typehint = require('command_line:typehint');
var Input = require('command_line:input').Input;

/**
 * Command line controller.
 */
exports.cliController = SC.Object.create({
    /**
     * @property{CliInputView}
     * The current command line view.
     */
    view: null,

    /**
     * A string containing the current contents of the command line
     */
    input: null,

    /**
     * A set of hints which could help the user complete their typing
     */
    hints: null,

    /**
     * Is the input in a state where it could possibly work?
     */
    error: false,

    init: function() {
        this.hints = [];
    },

    /**
     * Called by the UI to execute a command. Assumes that #input is bound to
     * the CLI input text field.
     */
    exec: function() {
        this.executeCommand(this.get('input'));
    },

    /**
     * We need to re-parse the CLI whenever the input changes
     */
    _inputChanged: function() {
        var hints = this.get('hints');
        hints.propertyWillChange('[]');
        hints.length = 0;

        var input = Input.create({
            typed: this.get('input'),
            env: environment.global,
            flags: keyboard.buildFlags(environment.global, { })
        });
        var results = input.parse();
        results.hints.forEach(function(hint) {
            hints.pushObject(hint);
        }.bind(this));

        hints.propertyDidChange('[]');
    }.observes('input'),

    /**
     * Execute a command manually without using the UI
     * @param typed {String} The command to turn into an Instruction and execute
     */
    executeCommand: function(typed) {
        console.log('executeCommand "' + typed + '"');
        var hints = this.get('hints');

        if (!typed || typed === '') {
            return;
        }

        var input = Input.create({
            typed: typed,
            env: environment.global,
            flags: keyboard.buildFlags(environment.global, { })
        });
        var results = input.parse();

        var self = this;

        var loadError = function(ex) {
            var trace = new Trace(ex, true);
            console.group('Error executing: ' + typed);
            console.error(ex);
            trace.log(3);
            console.groupEnd();

            self.set('input', '');
        };

        results.argsPromise.then(function(args) {
            input._commandExt.load().then(function(command) {
                self.set('input', '');

                // Check the function pointed to in the meta-data exists
                if (!command) {
                    hints.pushObject(hint.Hint.create({
                        level: hint.Level.Error,
                        element: 'Command not found.'
                    }));
                    return;
                }

                var request = Request.create({
                    command: command,
                    commandExt: input._commandExt,
                    typed: typed,
                    args: args
                });

                try {
                    command(environment.global, args, request);
                } catch (ex) {
                    var trace = new Trace(ex, true);
                    console.group('Error executing command \'' + typed + '\'');
                    console.error(ex);
                    trace.log(3);
                    console.groupEnd();

                    // TODO: Better UI
                    hints.pushObject(hint.Hint.create({
                        level: hint.Level.Error,
                        element: ex.toString()
                    }));

                    request.doneWithError(ex);
                }
            }, loadError);
        }, loadError);
    },

    /**
     * Replaces the contents of the command line with the given text as a
     * prompt, positions the insertion point at the end, and focuses the
     * command line.
     */
    prompt: function(text) {
        var view = this.get('view');
        view.focus();
        view.replaceSelection(text);
    }
});
