define('jls/security/MessageDigest', [
  'jls/lang/Class',
  'jls/lang/Exception',
  'jls/lang/ByteBuffer',
  'jls/io/BufferChannel'
], function (
  Class,
  Exception,
  ByteBuffer,
  BufferChannel
) {

/**
 * @namespace Provides security classes for the jls language.
 * @see jls.security.MessageDigest
 * @name jls.security
 */

var MessageDigest;
/**
 * @class This class represents a cryptographic hash function.
 * The message digests are MD5(not implemented) and SHA1.
 * @name jls.security.MessageDigest
 */
MessageDigest = Class.create( /** @lends jls.security.MessageDigest.prototype */
{
    /**
     * Creates a MessageDigest.
     * 
     * @private
     */
    initialize : function(algorithm) {
        this._algorithm = algorithm;
        this._buffer = ByteBuffer.allocate(1024);
    },
    /**
     * Returns the algorithm name.
     *
     * @returns {String} The algorithm name.
     */
    getAlgorithm : function() {
        return this._algorithm;
    },
    /**
     * Completes the digest and returns a buffer.
     *
     * @returns {jls.lang.ByteBuffer} The digested buffer.
     */
    digest : function() {
        throw new Exception('Not implemented');
    },
    /**
     * Resets the digest.
     *
     */
    reset : function() {
        this._buffer.clear();
    },
    _checkRemaining : function(length) {
        if (this._buffer.remaining() >= length) {
            return;
        }
        var capacity = this._buffer.capacity() * 2;
        for (; length > capacity - this._buffer.capacity(); capacity = capacity * 2);
        var tmp = ByteBuffer.allocate(capacity);
        this._buffer.flip();
        tmp.putBuffer(this._buffer);
        this._buffer = tmp;
    },
    /**
     * Updates the digest with a specified buffer.
     *
     * @param {jls.lang.ByteBuffer} buffer The buffer to update.
     */
    update : function(buffer) {
        this._checkRemaining(buffer.remaining());
        this._buffer.putBuffer(buffer, buffer.remaining());
    },
    /**
     * Updates the digest with a specified byte.
     *
     * @param {Number} b The byte to update.
     */
    updateByte : function(b) {
        this._checkRemaining(1);
        this._buffer.putByte(b);
    },
    /**
     * Updates the digest with a specified string.
     *
     * @param {String} buffer The string to update.
     */
    updateString : function(s, csn) {
        this._checkRemaining(s.length * 2);
        this._buffer.putString(s, csn ? csn : 'UTF-8');
    }
});

Object.extend(MessageDigest, /** @lends jls.security.MessageDigest */
{
	_algorithmMapping : {
		'SHA1' : 'jls/security/Sha1'
	},
	_availableMessageDigests : {},
    /**
     * Returns available algorithms.
     * 
     * @returns {Array} The available algorithms.
     */
	availableAlgorithms : function() {
        return Object.keys(MessageDigest._availableMessageDigests);
    },
	addMessageDigest : function(mdc) {
    	if ((typeof mdc == 'undefined') || (mdc == null)) {
			throw new Exception('Invalid message digest argument');
    	}
        var md = new mdc();
    	if (! (md instanceof MessageDigest)) {
			throw new Exception('Invalid message digest argument (not an instance of MessageDigest)');
    	}
		MessageDigest._availableMessageDigests[md.getAlgorithm()] = mdc;
    },
    /**
     * Tells if the specified algorithm is supported.
     * 
     * @param {String} algorithm The algorithm name.
     * @returns {Boolean} true if the specified algorithm is supported.
     */
    isSupported : function(algorithm) {
    	if (typeof algorithm != 'string') {
			throw new Exception('Invalid algorithm name');
    	}
    	if (algorithm in MessageDigest._availableMessageDigests) {
			return true;
    	}
    	if (algorithm in MessageDigest._algorithmMapping) {
            var a = require(MessageDigest._algorithmMapping[algorithm]);
			return algorithm in MessageDigest._availableMessageDigests;
    	}
		return false;
    },
    /**
     * Returns the specified message digest.
     * 
     * @param {String} algorithm The message digest algorithm name.
     * @returns {jls.security.MessageDigest} The message digest.
     */
	getInstance : function(algorithm) {
		if (! MessageDigest.isSupported(algorithm)) {
			throw new Exception('Unsupported algorithm "' + algorithm + '"');
		}
        return new MessageDigest._availableMessageDigests[algorithm]();
    }
});

return MessageDigest;
});
