#!/usr/bin/env node

var program = require('commander');
var compressjs = require('../');
var fs = require('fs');

program
    .version(compressjs.version)
    .usage('-d|-z [infile] [outfile]')
    .option('-d, --decompress',
            'Decompress stdin to stdout')
    .option('-z, --compress',
            'Compress stdin to stdout')
    .option('-b, --block <n>',
            'Extract a single block, starting at <n> bits.', -1)
    .option('-t <compressor>', 'Select compressor type')
    .option('-1', 'Fastest/largest compression')
    .option('-2')
    .option('-3')
    .option('-4')
    .option('-5')
    .option('-6')
    .option('-7')
    .option('-8')
    .option('-9', 'Slowest/smallest compression');
program.on('--help', function() {
    console.log('  If <infile> is omitted, reads from stdin.');
    console.log('  If <outfile> is omitted, writes to stdout.');
});
program.parse(process.argv);

if (!program.decompress) { program.compress = true; }

if (program.decompress && program.compress) {
    console.error('Must specify either -d or -z.');
    return 1;
}

if (program.compress && (+program.block) >= 0) {
    console.error('--block can only be used with decompression');
    return 1;
}

var level = undefined;
for (var l=1; l<=9; l++) {
    if (program[''+l]) {
        if (level) {
            console.error("Can't specify both -"+level+" and -"+l);
            return;
        }
        level = l;
    }
}
if (level && program.decompress) {
    console.error('Compression level has no effect when decompressing.');
    return;
}
if (!level) { level=7; /* default */ }

var makeInStream = function(in_fd) {
    var stream = new compressjs.Stream();
    var stat = fs.fstatSync(in_fd);
    if (stat.size) {
        stream.size = stat.size;
    }
    stream.buffer = new Buffer(4096);
    stream.filePos = null;
    stream.pos = 0;
    stream.end = 0;
    stream._fillBuffer = function() {
        this.end = fs.readSync(in_fd, this.buffer, 0, this.buffer.length,
                               this.filePos);
        this.pos = 0;
        if (this.filePos !== null && this.end > 0) {
            this.filePos += this.end;
        }
    };
    stream.readByte = function() {
        if (this.pos >= this.end) { this._fillBuffer(); }
        if (this.pos < this.end) {
            return this.buffer[this.pos++];
        }
        return -1;
    };
    stream.read = function(buffer, bufOffset, length) {
        if (this.pos >= this.end) { this._fillBuffer(); }
        var bytesRead = 0;
        while (bytesRead < length && this.pos < this.end) {
            buffer[bufOffset++] = this.buffer[this.pos++];
            bytesRead++;
        }
        return bytesRead;
    };
    stream.seek = function(seek_pos) {
        this.filePos = seek_pos;
        this.pos = this.end = 0;
    };
    stream.eof = function() {
        if (this.pos >= this.end) { this._fillBuffer(); }
        return (this.pos >= this.end);
    };
    stream.buffer.fill(0);
    return stream;
};

var makeOutStream = function(out_fd) {
    var stream = new compressjs.Stream();
    stream.buffer = new Buffer(4096);
    stream.pos = 0;
    stream.flush = function() {
        fs.writeSync(out_fd, this.buffer, 0, this.pos);
        this.pos = 0;
    };
    stream.writeByte = function(_byte) {
        if (this.pos >= this.buffer.length) { this.flush(); }
        this.buffer[this.pos++] = _byte;
    };
    stream.buffer.fill(0);
    return stream;
};

var in_fd = 0, close_in = function(){};
var out_fd = 1, close_out = function(){};
if (program.args.length > 0) {
    in_fd = fs.openSync(program.args.shift(), 'r');
    close_in = function() { fs.closeSync(in_fd); };
}
if (program.args.length > 0) {
    out_fd = fs.openSync(program.args.shift(), 'w');
    close_out = function() { fs.closeSync(out_fd); };
}

var inStream = makeInStream(in_fd);
var outStream= makeOutStream(out_fd);

var cmp = compressjs.Lzp3;
if (program.T) {
    switch (program.T.toLowerCase()) {
    // models and coders
    case 'defsum':  cmp = compressjs.DefSumModel; break;
    case 'fenwick': cmp = compressjs.FenwickModel; break;
    case 'mtf':     cmp = compressjs.MTFModel; break;
    case 'context1':cmp = compressjs.Context1Model; break;
    case 'no':      cmp = compressjs.NoModel; break;
    case 'huff':    /* fall through */
    case 'huffman': cmp = compressjs.Huffman; break;
    // compression methods
    case 'bwtc':    cmp = compressjs.BWTC; break;
    case 'bzip':    /* fall through */
    case 'bzip2':   cmp = compressjs.Bzip2; break;
    case 'dmc':     cmp = compressjs.Dmc; break;
    case 'lzjb':    cmp = compressjs.Lzjb; break;
    case 'lzjbr':   cmp = compressjs.LzjbR; break;
    case 'lzp3':    cmp = compressjs.Lzp3; break;
    case 'ppm':     cmp = compressjs.PPM; break;
    case 'simple':  cmp = compressjs.Simple; break;
    default:
        console.error('Unknown compressor:', program.T);
        return 1;
    }
}

if (program.decompress) {
    if (program.block >= 0) {
      cmp.decompressBlock(inStream, program.block, outStream);
    } else {
      cmp.decompressFile(inStream, outStream);
    }
    outStream.flush();
    close_in();
    close_out();
    return 0;
}
if (program.compress) {
    cmp.compressFile(inStream, outStream, level);
    outStream.flush();
    close_in();
    close_out();
    return 0;
}
return 1;
