#!/usr/bin/env node

var options = argv = require('optimist').argv,
    _ = require('underscore'),
    Step = require('step'),
    sys = require('sys'),
    spawn = require('child_process').spawn,
    MBTiles = require('..'),
    utils = require('..').utils,
    mbtiles,
    table,
    total;

// @TODO: support specifying a table to be processed, e.g. for processing
// grids or grid data.

// Show help.
if (argv.help || argv._.length < 2) {
    console.warn('Usage: mbpipe [COMMAND] [FILE]');
    console.warn('  Pipe each tile to stdin of the specified command and');
    console.warn('  write stdout into the mbtiles file.');
    console.warn('Examples:');
    console.warn('  mbpipe "pngquant 32" world.mbtiles');
    process.exit();
}

// Grab args.
var filename = argv._[1],
    command = {
        command: argv._[0].split(' ')[0],
        args: argv._[0].split(' ').slice(1)
    };

Step(
    function() { mbtiles = new MBTiles(filename, this); },
    function(err) {
        if (err) throw err;
        Step(
            function() { mbtiles._db.run('PRAGMA synchronous=0', this); },
            function() { mbtiles._db.run('PRAGMA locking_mode=EXCLUSIVE', this); },
            function() { mbtiles._db.run('PRAGMA journal_mode=TRUNCATE', this); },
            this
        );
    },
    function(err) {
        if (err) throw err;
        mbtiles._exists('tiles', function(err, exists) {
            if (exists) table = 'tiles';
            this();
        }.bind(this));
    },
    function(err) {
        if (err) throw err;
        mbtiles._exists('images', function(err, exists) {
            if (exists) table = 'images';
            this();
        }.bind(this));
    },
    function(err) {
        if (err) throw err;
        if (!table) throw new Error('No usable image table found.');
        mbtiles._db.get('SELECT COUNT(tile_data) AS total FROM ' + table, function(err, row) {
            total = row.total;
            this();
        }.bind(this));
    },
    function(err) {
        if (err) throw err;
        if (!total) throw new Error('No tiles found');

        var printed = 0;
        var done = this;
        var doit = function(table, limit, offset) {
            process.nextTick(function() {
                var query = table === 'images'
                    ? mbtiles._db.prepare('SELECT tile_data AS tile_data, tile_id AS id FROM images ORDER BY tile_id ASC LIMIT ? OFFSET ?')
                    : mbtiles._db.prepare('SELECT tile_data AS tile_data, zoom_level AS z, tile_column AS x, tile_row AS y FROM tiles ORDER BY z, x, y LIMIT ? OFFSET ?');
                query.all(limit, offset, function(err, rows) {
                    Step(
                        function() {
                            var group = this.group();
                            var exec = function(row, callback) {
                                row.piped = new Buffer(0);
                                var child = spawn(command.command, command.args);
                                var stream = function(chunk) {
                                    var joined = (new Buffer(row.piped.length + chunk.length));
                                    row.piped.copy(joined, 0, 0);
                                    chunk.copy(joined, row.piped.length, 0);
                                    row.piped = joined;
                                };
                                child.stdin.write(row.tile_data);
                                child.stdin.end();
                                child.stdout.on('data', stream);
                                child.stderr.on('data', stream);
                                child.on('exit', function(code) {
                                    if (code) return callback(null, null);
                                    if (table === 'images') {
                                        mbtiles._db.run(
                                            'INSERT OR REPLACE INTO images (tile_id, tile_data) VALUES(?, ?)',
                                            row.id,
                                            row.piped,
                                            callback
                                        );
                                    } else {
                                        mbtiles._db.run(
                                            'INSERT OR REPLACE INTO tiles (zoom_level, tile_column, tile_row, tile_data) VALUES(?, ?, ?, ?)',
                                            row.z,
                                            row.x,
                                            row.y,
                                            row.piped,
                                            callback
                                        );
                                    }
                                });
                            };
                            for (var i = 0; i < rows.length; i++) {
                                exec(rows[i], group());
                            }
                        },
                        function(err) {
                            var progress = Math.floor(offset / total * 40);
                            if (progress > printed) {
                                sys.print((new Array(progress - printed + 1)).join('#'));
                                printed = progress;
                            }
                            if (rows.length === limit) {
                                doit(table, limit, offset + limit);
                            } else {
                                mbtiles._db.run('VACUUM', function() {
                                    sys.print('\n');
                                    console.warn('Pipe complete.');
                                    done();
                                });
                            }
                        }
                    );
                });
                query.finalize();
            });
        };
        console.warn('00 -------------- 50 -------------- 100');
        doit(table, 255, 0);
    }
);

