### codetube
    Copyright (C) 2011 payload payload@lavabit.com
    Copyright (C) 2011 dodo dodo.the.last@gmail.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>
###

class Utf8
    encode: (strUni) ->
        # U+0080 - U+07FF => 2 bytes 110yyyyy, 10zzzzzz)
        strUtf = strUni.replace /[\u0080-\u07ff]/g, (c) ->
            cc = c.charCodeAt(0)
            String.fromCharCode( 0xc0 | cc>>6, 0x80 | cc&0x3f )
        # U+0800 - U+FFFF => 3 bytes 1110xxxx, 10yyyyyy, 10zzzzzz
        strUtf = strUtf.replace /[\u0800-\uffff]/g, (c) ->
            cc = c.charCodeAt(0)
            String.fromCharCode(0xe0|cc>>12, 0x80|cc>>6&0x3F, 0x80|cc&0x3f)
        strUtf


class Sha1
    constructor: ->
        @utf8 = new Utf8()
        @reziproce_pow2_32 = 1 / Math.pow(2, 32)
        @_K = [0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6]
        @_H = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0]

    _f: (s, x, y, z) =>
        switch s
            when 0 then (x & y) ^ (~x & z)          # Ch
            when 1 then x ^ y ^ z                   # Parity
            when 2 then (x & y) ^ (x & z) ^ (y & z) # Maj
            when 3 then x ^ y ^ z                   # Parity

    rotl: (x, n) => # rotate left (circular left shift) value x by n positions
        (x << n) | (x >>> (32 - n))

    toHex: (n) => # hexadecimal representation
        str = ""
        for i in [7..0]
            v = ( n >>> (i * 4)) & 0xf
            str += v.toString(16);
        str

    hash: (msg) =>
        msg = ""+msg if typeof msg isnt 'string'
        # convert string to UTF-8, as SHA only deals with byte-streams
        msg = @utf8.encode(msg)
        # add trailing '1' bit (+ 0's padding) to string
        msg += String.fromCharCode(0x80)
        # convert string msg into 512-bit/16-integer blocks arrays of ints
        l = msg.length / 4 + 2
        N = Math.ceil(l/16)
        M = new Array(N)
        for i in [0..(N-1)]
            M[i] = new Array(16)
            # encode 4 chars per integer, big-endian encoding
            for j in [0..15]
                a = msg.charCodeAt(i * 64 + j * 4    ) << 24
                b = msg.charCodeAt(i * 64 + j * 4 + 1) << 16
                c = msg.charCodeAt(i * 64 + j * 4 + 2) << 8
                d = msg.charCodeAt(i * 64 + j * 4 + 3)
                M[i][j] = a | b | c | d
        # add length (in bits) into final pair of 32-bit integers (big-endian)
        M[N-1][14] = Math.floor((msg.length - 1) * 8 * @reziproce_pow2_32)
        M[N-1][15] = ((msg.length - 1) * 8) & 0xffffffff
        # set initial hash value
        H = @_H[0..]
        # hash computation
        W = new Array(80)
        for i in [0..(N-1)]
            # prepare message schedule 'W'
            for t in [0..15]
                W[t] = M[i][t]
            for t in [16..79]
                W[t] = @rotl(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1)
            # initialise five working variables a, b, c, d, e with previous hash value
            [a, b, c, d, e] = H
            # main loop
            for t in [0..79]
                # seq for blocks of @f functions and @_K constants
                s = Math.floor(t * 0.05)
                T = (@rotl(a,5) + @_f(s,b,c,d) + e + @_K[s] + W[t]) & 0xffffffff
                [a, b, c, d, e] = [T, a, @rotl(b, 30), c, d]
            # compute the new intermediate hash value (addition modulo 2^32)
            H = ( ((H[h]+[a, b, c, d, e][h]) & 0xffffffff) for h in [0..4] )
        H = ( @toHex(H[h]) for h in [0..4] )
        H[0] + H[1] + H[2] + H[3] + H[4]


# exports

module.exports = new Sha1()