"use strict";

var encode = require('./encode');
var calculateEC = require('./errorcode');
var matrix = require('./matrix');

function _deepCopy(obj) {
    return JSON.parse(JSON.stringify(obj));
}

var EC_LEVELS = ['L', 'M', 'Q', 'H'];

// {{{1 Versions
var versions = [
    [], // there is no version 0
    // total number of codewords, (number of ec codewords, number of blocks) * ( L, M, Q, H )
    [26, 7, 1, 10, 1, 13, 1, 17, 1],
    [44, 10, 1, 16, 1, 22, 1, 28, 1],
    [70, 15, 1, 26, 1, 36, 2, 44, 2],
    [100, 20, 1, 36, 2, 52, 2, 64, 4],
    [134, 26, 1, 48, 2, 72, 4, 88, 4], // 5
    [172, 36, 2, 64, 4, 96, 4, 112, 4],
    [196, 40, 2, 72, 4, 108, 6, 130, 5],
    [242, 48, 2, 88, 4, 132, 6, 156, 6],
    [292, 60, 2, 110, 5, 160, 8, 192, 8],
    [346, 72, 4, 130, 5, 192, 8, 224, 8], // 10
    [404, 80, 4, 150, 5, 224, 8, 264, 11],
    [466, 96, 4, 176, 8, 260, 10, 308, 11],
    [532, 104, 4, 198, 9, 288, 12, 352, 16],
    [581, 120, 4, 216, 9, 320, 16, 384, 16],
    [655, 132, 6, 240, 10, 360, 12, 432, 18], // 15
    [733, 144, 6, 280, 10, 408, 17, 480, 16],
    [815, 168, 6, 308, 11, 448, 16, 532, 19],
    [901, 180, 6, 338, 13, 504, 18, 588, 21],
    [991, 196, 7, 364, 14, 546, 21, 650, 25],
    [1085, 224, 8, 416, 16, 600, 20, 700, 25], // 20
    [1156, 224, 8, 442, 17, 644, 23, 750, 25],
    [1258, 252, 9, 476, 17, 690, 23, 816, 34],
    [1364, 270, 9, 504, 18, 750, 25, 900, 30],
    [1474, 300, 10, 560, 20, 810, 27, 960, 32],
    [1588, 312, 12, 588, 21, 870, 29, 1050, 35], // 25
    [1706, 336, 12, 644, 23, 952, 34, 1110, 37],
    [1828, 360, 12, 700, 25, 1020, 34, 1200, 40],
    [1921, 390, 13, 728, 26, 1050, 35, 1260, 42],
    [2051, 420, 14, 784, 28, 1140, 38, 1350, 45],
    [2185, 450, 15, 812, 29, 1200, 40, 1440, 48], // 30
    [2323, 480, 16, 868, 31, 1290, 43, 1530, 51],
    [2465, 510, 17, 924, 33, 1350, 45, 1620, 54],
    [2611, 540, 18, 980, 35, 1440, 48, 1710, 57],
    [2761, 570, 19, 1036, 37, 1530, 51, 1800, 60],
    [2876, 570, 19, 1064, 38, 1590, 53, 1890, 63], // 35
    [3034, 600, 20, 1120, 40, 1680, 56, 1980, 66],
    [3196, 630, 21, 1204, 43, 1770, 59, 2100, 70],
    [3362, 660, 22, 1260, 45, 1860, 62, 2220, 74],
    [3532, 720, 24, 1316, 47, 1950, 65, 2310, 77],
    [3706, 750, 25, 1372, 49, 2040, 68, 2430, 81] // 40
];

versions = versions.map(function(v, index) {
    if (!index) return {};

    var res = {
    }
    for (var i = 1; i < 8; i += 2) {
        var length = v[0] - v[i];
        var num_template = v[i+1];
        var ec_level = EC_LEVELS[(i/2)|0];
        var level = {
            version: index,
            ec_level: ec_level,
            data_len: length,
            ec_len: v[i] / num_template,
            blocks: [],
            ec: []
        }

        for (var k = num_template, n = length; k > 0; k--) {
            var block = (n / k)|0;
            level.blocks.push(block);
            n -= block;

        }
        res[ec_level] = level;
    }
    return res;
});

// {{{1 Get version template
function getTemplate(message, ec_level) {
    var i = 1;
    var len;

    if (message.data1) {
        len = Math.ceil(message.data1.length / 8);
    } else {
        i = 10;
    }
    for (/* i */; i < 10; i++) {
        var version = versions[i][ec_level];
        if (version.data_len >= len) {
            return _deepCopy(version);
        }
    }

    if (message.data10) {
        len = Math.ceil(message.data10.length / 8);
    } else {
        i = 27;
    }
    for (/* i */; i < 27; i++) {
        var version = versions[i][ec_level];
        if (version.data_len >= len) {
            return _deepCopy(version);
        }
    }

    len = Math.ceil(message.data27.length / 8);
    for (/* i */; i < 41; i++) {
        var version = versions[i][ec_level];
        if (version.data_len >= len) {
            return _deepCopy(version);
        }
    }
    throw new Error("Too much data");
}

// {{{1 Fill template
function fillTemplate(message, template) {
    var blocks = new Buffer(template.data_len);
    blocks.fill(0);

    if (template.version < 10) {
        message = message.data1;
    } else if (template.version < 27) {
        message = message.data10;
    } else {
        message = message.data27;
    }

    var len = message.length;

    for (var i = 0; i < len; i += 8) {
        var b = 0;
        for (var j = 0; j < 8; j++) {
            b = (b << 1) | (message[i + j] ? 1 : 0);
        }
        blocks[i / 8] = b;
    }

    var pad = 236;
    for (var i = Math.ceil((len + 4) / 8); i < blocks.length; i++) {
        blocks[i] = pad;
        pad = (pad == 236) ? 17 : 236;
    }

    var offset = 0;
    template.blocks = template.blocks.map(function(n) {
        var b = blocks.slice(offset, offset + n);
        offset += n;
        template.ec.push(calculateEC(b, template.ec_len));
        return b;
    });

    return template;
}

// {{{1 All-in-one
function QR(text, ec_level, parse_url) {
    ec_level = EC_LEVELS.indexOf(ec_level) > -1 ? ec_level : 'M';
    var message = encode(text, parse_url);
    var data = fillTemplate(message, getTemplate(message, ec_level));
    return matrix.getMatrix(data);
}

// {{{1 export functions
module.exports = {
    QR: QR,
    getTemplate: getTemplate,
    fillTemplate: fillTemplate,
}