/* (c) Copyright 2015-2016 Kevin Kelley <kelleyk@kelleyk.net>. */


var AST2100IDCT;


// N.B.: These values assume CONST_BITS == 8
var FIX_1_082392200 = 277;  // fix(1.082392200)
var FIX_1_414213562 = 362;  // fix(1.414213562)
var FIX_1_847759065 = 473;  // fix(1.847759065)
var FIX_2_613125930 = 669;  // fix(2.613125930)


var MAXJSAMPLE = 255;  // largest value of a sample; 8-bit, so 2**8-1
var CONST_BITS = 8;
var PASS1_BITS = 0;

// This is like clamp() except that it also adds the MAXJSAMPLE/2 offset.
var range_limit = function (x) {
    x += 128;  // XXX: This is baked into the range limit table in the ATEN stuff and in libjpeg; see commpent above prepare_range_limit_table() in jdmaster.c.
    return Math.max(0, Math.min(255, x));
};

// Convert float to fixed-point.
var fix = function (x) {
    return ~~(x * (1 << CONST_BITS) + 0.5);
};

var fixed_dequant = function (scaled_quant_table, buf, i) {
    // N.B.: The data in buf is unscaled; the data in scaled_quant_table has been scaled by 1<<16.
    return fixed_mul(scaled_quant_table[i], buf[i]);
};

var descale = function (x, n) {
    // console.log([x, x>>n, (x>>n)+128]);
    return x >> n;
};

// if not accurate rounding mode...
var idescale = descale;
// else use better implementation

// @KK: This isn't a libjpeg parameter; it just means that ATEN downshifts all the way back to "normal ints" at the end of pass 1.
var END_PASS1_DESCALE_BITS = CONST_BITS;

var fixed_mul = function (a, b) {
    return descale(a * b, CONST_BITS);
};


(function () {
    "use strict";

    AST2100IDCT = {

        // Uses the 16/16 integer representation that ATEN and libjpeg's jidctfst.c ("fast, not-so-accurate integer
        // IDCT") do.  Note that, for performance, this routine *also* incorporates the dequantization step, which is
        // why it takes scaled_quant_table as an argument.  (This argument does not actually contain "just" a scaled
        // quant table; some constants have been pre-multiplied into it.  See the function that loads quant tables for
        // more details.)
        idct_fixed_aan: function (scaled_quant_table, buf, dstBuf) {

            // ATEN rounds things off early, at a cost to precision: the int32 values in 'workspace' are not scaled at all.
            var workspace = new Int32Array(64);
    
            for (var x = 0; x < 8; ++x)
                AST2100IDCT._aan_idct_col(scaled_quant_table, buf, workspace, x);
                
            for (var y = 0; y < 8; ++y)
                AST2100IDCT._aan_idct_row(scaled_quant_table, dstBuf, workspace, y);
                
            return dstBuf;
        },

        // Columns; aka "Pass 1".
        _aan_idct_col: function(scaled_quant_table, buf, workspace, x) {

            var dequant = function (idx) { return fixed_dequant(scaled_quant_table, buf, idx); };
            var mul = fixed_mul;

            var y;

            var all_ac_zero = true;
            for (y = 1; y < 8; ++y) {
                if (buf[8 * y + x] != 0) {
                    all_ac_zero = false;
                    break;
                }
            }
            if (all_ac_zero) {
                var raw_dcval = buf[8 * 0 + x];
                var quant_val = scaled_quant_table[8 * 0 + x];
                var dcval = idescale(dequant(8 * 0 + x), END_PASS1_DESCALE_BITS);  // in total, >> 16
                for (y = 0; y < 8; ++y)
                    workspace[8 * y + x] = dcval;
                return;
            }
    
            // Even part.
            var tmp0 = dequant(8 * 0 + x);
            var tmp1 = dequant(8 * 2 + x);
            var tmp2 = dequant(8 * 4 + x);
            var tmp3 = dequant(8 * 6 + x);
            
            var tmp10 = tmp0 + tmp2;  // Phase 3
            var tmp11 = tmp0 - tmp2;

            var tmp13 = tmp1 + tmp3;  // Phases 5-3
            var tmp12 = mul((tmp1 - tmp3), FIX_1_414213562) - tmp13;  // 2 * c4

            tmp0 = tmp10 + tmp13;
            tmp3 = tmp10 - tmp13;
            tmp1 = tmp11 + tmp12;
            tmp2 = tmp11 - tmp12;

            // Odd part.
            var tmp4 = dequant(8 * 1 + x);
            var tmp5 = dequant(8 * 3 + x);
            var tmp6 = dequant(8 * 5 + x);
            var tmp7 = dequant(8 * 7 + x);
            
            var z13 = tmp6 + tmp5;  // Phase 6
            var z10 = tmp6 - tmp5;
            var z11 = tmp4 + tmp7;
            var z12 = tmp4 - tmp7;

            tmp7 = z11 + z13;  // Phase 5
            tmp11 = mul((z11 - z13), FIX_1_414213562);  // 2 * c4
    
            var z5 = mul((z10 + z12), FIX_1_847759065);  // 2 * c2
            tmp10 = mul(FIX_1_082392200, z12) - z5;  // 2 * (c2-c6)
            tmp12 = mul(-FIX_2_613125930, z10) + z5;

            tmp6 = tmp12 - tmp7;
            tmp5 = tmp11 - tmp6;
            tmp4 = tmp10 + tmp5;

            workspace[x + 8 * 0] = idescale(tmp0 + tmp7, END_PASS1_DESCALE_BITS);
            workspace[x + 8 * 7] = idescale(tmp0 - tmp7, END_PASS1_DESCALE_BITS);
            workspace[x + 8 * 1] = idescale(tmp1 + tmp6, END_PASS1_DESCALE_BITS);
            workspace[x + 8 * 6] = idescale(tmp1 - tmp6, END_PASS1_DESCALE_BITS);
            workspace[x + 8 * 2] = idescale(tmp2 + tmp5, END_PASS1_DESCALE_BITS);
            workspace[x + 8 * 5] = idescale(tmp2 - tmp5, END_PASS1_DESCALE_BITS);
            workspace[x + 8 * 4] = idescale(tmp3 + tmp4, END_PASS1_DESCALE_BITS);
            workspace[x + 8 * 3] = idescale(tmp3 - tmp4, END_PASS1_DESCALE_BITS);
            
        },
        
        // Rows; aka "Pass 2".
        _aan_idct_row: function(scaled_quant_table, buf, workspace, y) {

            var wsptr = function (x) { return workspace[8 * y + x]; };
            var mul = fixed_mul;

            // Even part.
            var tmp10 = wsptr(0) + wsptr(4);
            var tmp11 = wsptr(0) - wsptr(4);
            
            var tmp13 = wsptr(2) + wsptr(6);
            var tmp12 = mul((wsptr(2) - wsptr(6)), FIX_1_414213562) - tmp13;

            var tmp0 = tmp10 + tmp13;
            var tmp3 = tmp10 - tmp13;
            var tmp1 = tmp11 + tmp12;
            var tmp2 = tmp11 - tmp12;

            // Odd part.
            var z13 = wsptr(5) + wsptr(3);
            var z10 = wsptr(5) - wsptr(3);
            var z11 = wsptr(1) + wsptr(7);
            var z12 = wsptr(1) - wsptr(7);

            var tmp7 = z11 + z13;
            tmp11 = mul((z11 - z13), FIX_1_414213562);

            var z5 = mul((z10 + z12), FIX_1_847759065);  // 2 * c2
            tmp10 = mul(FIX_1_082392200, z12) - z5;  // 2 * (c2-c6)
            tmp12 = mul(-FIX_2_613125930, z10) + z5;

            var tmp6 = tmp12 - tmp7;
            var tmp5 = tmp11 - tmp6;
            var tmp4 = tmp10 + tmp5;

            var set_out = function (x, val) {
                // Shift right by PASS1_BITS bits to convert back to a normal int, and then by another 3 to divide by 8.
                val = idescale(val, PASS1_BITS + 3);                
                val = range_limit(val);  // This also applies the +128 offset.
                buf[y * 8 + x] = val;
            };
    
            // Final output stage: scale down by a factor of 8 and range-limit
            set_out(0, tmp0 + tmp7);
            set_out(7, tmp0 - tmp7);
            set_out(1, tmp1 + tmp6);
            set_out(6, tmp0 - tmp6);
            set_out(2, tmp2 + tmp5);
            set_out(5, tmp2 - tmp5);
            set_out(4, tmp3 + tmp4);
            set_out(3, tmp3 - tmp4);
        }
        
    };

})();
