// ----------------------------------------------------------------------------
//
// pg-patcher - A Postgres patch helper for node-postgres.
//
// Copyright (c) 2013, Andrew Chilton. All Rights Reserved.
//
// License: MIT - http://chilts.mit-license.org/2013/
//
// ----------------------------------------------------------------------------

// core
var fs = require('fs');

// npm
var xtend = require('xtend');
var async = require('async');

// ----------------------------------------------------------------------------

var defaults = {
    dir    : 'db',
    prefix : 'patch',
};

module.exports = function pgpatcher(client, level, opts, callback) {
    // optional 'opts'
    if ( typeof opts === 'function' ) {
        callback = opts;
        opts = {};
    }

    opts = xtend({}, defaults, opts);

    var forwardPatch = {};
    var reversePatch = {};
    var currentPatchLevel;

    async.series(
        [
            readPatchDir,
            getCurrentPatch,
            // checkAllPatchFilesExist,
            begin,
            nextPatch,
            writeCurrentLevel,
            commit,
        ],
        function(err) {
            if (err) {
                return callback(err);
            }
            callback(null, currentPatchLevel);
        }
    );

    function readPatchDir(done) {
        fs.readdir(opts.dir, function(err, files) {
            files.forEach(function(filename) {
                if ( filename.match(/~$/) ) {
                    return;
                }

                var parts = filename.split(/[-\.]/);
                var from = +parts[1];
                var to   = +parts[2];

                var patch = {
                    from     : from,
                    to       : to,
                    filename : filename,
                };

                if ( to > from ) {
                    forwardPatch[to] = patch;
                }
                else {
                    reversePatch[to] = patch;
                }
            });
            done();
        });
        
    }

    function begin(done) {
        console.log('Beginning transaction ...');
        client.query("BEGIN", done);
    }

    function commit(done) {
        console.log('Commiting transaction ...');
        client.query("COMMIT", done);
    }

    function getCurrentPatch(done) {
        console.log('Getting current patch ...');
        client.query("SELECT value FROM property WHERE key = 'patch'", function(err, res) {
            if (err) {
                if ( '' + err === 'error: relation "property" does not exist' ) {
                    console.log('Property table does not exist, patch level zero!');
                    currentPatchLevel = 0;
                    return done();
                }
                return done(err);
            }

            currentPatchLevel = +res.rows[0].value;
            console.log('* current patch is ' + currentPatchLevel);

            done();
        });
    }

    function nextPatch(done) {
        console.log('Patching to next level ...');

        var tryLevel;
        var patch;
        if ( level > currentPatchLevel ) {
            tryLevel = currentPatchLevel + 1;
            patch = forwardPatch[tryLevel];
            console.log(' * trying forward patch to ' + tryLevel);
        }
        else if ( level < currentPatchLevel ) {
            tryLevel = currentPatchLevel - 1;
            patch = reversePatch[tryLevel];
            console.log(' * trying reverse patch to ' + tryLevel);
        }
        else {
            // same, nothing to do
            console.log(' * nothing to patch');
            return process.nextTick(done);
        }

        // make the filename
        filename = opts.dir + '/' + patch.filename;

        // read the patch file
        fs.readFile(filename, { encoding : 'utf8' }, function(err, sql) {
            client.query(sql, function(err, res) {
                if (err) return done(err);

                // update the current patch level state
                currentPatchLevel = tryLevel;

                nextPatch(done);
            });
        });
    }

    function writeCurrentLevel(done) {
        console.log('Writing current patch level to database ...');
        // only write the if the patch level is greater than zero
        if ( currentPatchLevel > 0 ) {
            client.query("UPDATE property SET value = $1 WHERE key = 'patch'", [ currentPatchLevel ], done);
        }
        else {
            process.nextTick(done);
        }
    }
};

// ----------------------------------------------------------------------------

@@ -579,16 +579,52 @@
 patch',%0A
+    logger : function() %7B%7D, // noop%0A
 %7D;%0A%0Amodu
@@ -839,16 +839,46 @@
 , opts);
+%0A    var logger = opts.logger;
 %0A%0A    va
@@ -2148,27 +2148,22 @@
         
-console.
 log
+ger
 ('Beginn
@@ -2265,27 +2265,22 @@
         
-console.
 log
+ger
 ('Commit
@@ -2392,27 +2392,22 @@
         
-console.
 log
+ger
 ('Gettin
@@ -2646,27 +2646,22 @@
         
-console.
 log
+ger
 ('Proper
@@ -2919,24 +2919,17 @@
     
-console.log('* c
+logger('C
 urre
@@ -3043,27 +3043,22 @@
         
-console.
 log
+ger
 ('Patchi
@@ -3268,33 +3268,25 @@
         
-console.log(' * t
+logger('T
 rying fo
@@ -3292,35 +3292,40 @@
 orward patch to 
-' +
+%25s ...',
  tryLevel);%0A    
@@ -3484,25 +3484,17 @@
     
-console.log(' * t
+logger('T
 ryin
@@ -3512,19 +3512,24 @@
 atch to 
-' +
+%25s ...',
  tryLeve
@@ -3608,40 +3608,34 @@
     
-console.log(' * nothing to patch
+logger('No patching needed
 ');%0A
@@ -4184,76 +4184,8 @@
 ) %7B%0A
-        console.log('Writing current patch level to database ...');%0A
     
@@ -4281,24 +4281,111 @@
 vel %3E 0 ) %7B%0A
+          logger('Writing current patch level %25s to database ...', currentPatchLevel);%0A
             
@@ -4483,32 +4483,32 @@
 one);%0A        %7D%0A
-
         else %7B%0A 
@@ -4498,32 +4498,109 @@
 %0A        else %7B%0A
+            logger(%22Patch level 0 - no need to write this to the database%22);%0A
             proc

Switch to a logger provided by the user