"use strict";

// Crypto module import.
const crypto = require('crypto');

// Hash generation string.
const itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';

// To 64 bit version.
function to64(index, count) {
    let result = '';

    while (--count >= 0) { // Result char count.
        result += itoa64[index & 63]; // Get corresponding char.
        index = index >> 6; // Move to next one.
    }

    return result;
}

// Returns salt.
function getSalt(inputSalt) {
    let salt = '';

    if (inputSalt) {
        // Remove $apr1$ token and extract salt.
        salt = inputSalt.split('$')[2];
    } else {
        while(salt.length < 8) { // Random 8 chars.
            let rchIndex = Math.floor((Math.random() * 64));
            salt += itoa64[rchIndex];
        }
    }

    return salt;
}

// Returns password.
function getPassword(final) {
    // Encrypted pass.
    let epass = '';

    epass += to64((final.charCodeAt(0) << 16) | (final.charCodeAt(6) << 8) | final.charCodeAt(12), 4);
    epass += to64((final.charCodeAt(1) << 16) | (final.charCodeAt(7) << 8) | final.charCodeAt(13), 4);
    epass += to64((final.charCodeAt(2) << 16) | (final.charCodeAt(8) << 8) | final.charCodeAt(14), 4);
    epass += to64((final.charCodeAt(3) << 16) | (final.charCodeAt(9) << 8) | final.charCodeAt(15), 4);
    epass += to64((final.charCodeAt(4) << 16) | (final.charCodeAt(10) << 8) | final.charCodeAt(5), 4);
    epass += to64(final.charCodeAt(11), 2);

    return epass;
}

// Exporting old style.
module.exports = (password, salt) => {
    let magic = '';
    if (salt && salt.split('$')[1] === '1') {
        magic = '$1$';
    } else {
        magic = '$apr1$';
    }

    salt = getSalt(salt);

    let ctx = password + magic + salt;
    let final = crypto.createHash('md5').update(password + salt + password, 'ascii').digest('binary');

    for (let pl = password.length; pl > 0; pl -= 16) {
        ctx += final.substr(0, (pl > 16) ? 16 : pl);
    }

    for (let i = password.length; i; i >>= 1) {
        if (i % 2) {
            ctx += String.fromCharCode(0);
        } else {
            ctx += password.charAt(0);
        }
    }

    final = crypto.createHash('md5').update(ctx, 'ascii').digest('binary');

    // 1000 loop.
    for (let i = 0; i < 1000; ++i) {
        // Weird stuff.
        let ctxl = '';

        if (i % 2) {
            ctxl += password;
        } else {
            ctxl += final.substr(0, 16);
        }

        if (i % 3) {
            ctxl += salt;
        }

        if (i % 7) {
            ctxl += password;
        }

        if (i % 2) {
            ctxl += final.substr(0, 16);
        } else {
            ctxl += password;
        }

        // Final assignment after each loop.
        final = crypto.createHash('md5').update(ctxl, 'ascii').digest('binary');
    }

    return magic + salt + '$' + getPassword(final);
};
