/*
 * Licensed to Cloudkick, Inc ('Cloudkick') under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * Cloudkick licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var sys = require('sys');
var fs = require('fs');
var path = require('path');
var http = require('util/http');
var crypto = require('crypto');
var pumpfile = require('util/http_pumpfile');
var client_config = require('util/config');
var dotfiles = require('util/client_dotfiles');
var misc = require('util/misc');
var spinner = require('util/spinner');
var manifest = require('manifest/index');
var MANIFEST_FILENAME = require('manifest/constants').MANIFEST_FILENAME;
var async = require('extern/async');

/** Configuration options for upload subcommand */
exports.config = {
  short_description: 'Upload an application bundle.',
  long_description: 'Upload an application bundle to a remote server.',
  required_arguments: [],
  optional_arguments: [['version', 'The bundle version to upload, defaults to the newest available']],
  uses_global_options: ['remote']
};

/**
 * Handler for uploading a bundle.
 * @param {Object} args Command line arguments.
 * @param {String} args.version Application version number to create.
 */
exports.handle_command = function(args)
{
  var manifest_path = path.join(process.cwd(), MANIFEST_FILENAME);
  var bundledir = path.join(dotfiles.dot_cast_project_path(process.cwd()), 'tmp');
  var version = args.version;
  var bundlename;
  var fpath;
  var size = 0;

  async.series([
    // Validate the manifest and get the bundle name
    function(callback) {
      manifest.validate_manifest(manifest_path, function(err, app_manifest) {
        if (!err) {
          bundlename = misc.get_valid_bundle_name(app_manifest.name);
        }
        return callback(err);
      });
    },

    function(callback) {
      // If no version was specified then look up the most recently modified bundle
      if (!version) {
        fs.readdir(bundledir, function(err, files) {
          var msg;

          if (err) {
            msg = 'Unable to read ' + bundledir + ', have you created a bundle yet?';
            return callback(new Error(msg));
          }

          var newestfile;
          var newestmtime;

          async.forEach(files, function(file, callback) {
            fs.stat(path.join(bundledir, file), function(err, stats) {
              if (!err && stats.isFile() && (!newestmtime || stats.mtime > newestmtime)) {
                newestmtime = stats.mtime;
                newestfile = file;
                size = stats.size;
              }
              return callback();
            });
          },
          function(err) {
            if (!err && !newestfile) {
              msg = 'No bundles found in ' + bundledir + ' have you created one yet?';
              err = new Error(msg);
            }
            else {
              fpath = path.join(bundledir, newestfile);
            }
            return callback(err);
          });
        });
      }

      // If a version was specified, make sure it exists
      else {
        var full_bundlename = misc.get_full_bundle_name(bundlename, version);
        fpath = path.join(bundledir, full_bundlename + '.tar.gz');
        fs.stat(fpath, function(err, stats) {
          if (err || !stats.isFile()) {
            err = new Error('Specified version does not exist.');
          }
          else {
            size = stats.size;
          }
          return callback(err);
        });
      }
    },

    function(callback) {
      var remotepath = path.join('/', 'bundles', bundlename, path.basename(fpath));
      var pbar = spinner.percentbar('Uploading ' + path.basename(fpath), size);
      var completed = false;

      pbar.start();

      http.client(args.remote, function(err, client) {
        client.on('error', function() {
          completed = true;
          return callback(new Error('A connection error occurred'));
        });

        var bytes = 0;

        var request = client.request('PUT', remotepath, {
          'host': 'localhost',
          'content-length': size,
          'transfer-encoding': 'chunked',
          'trailer': 'x-content-sha1'
        });

        pumpfile.pumpfileout(fpath, request,
        function(bytes) {
          if (!completed) {
            pbar.tick(bytes);
          }
        },
        function(err, sha1) {
          if (completed) {
            return;
          }

          completed = true;

          if (err) {
            return callback(err);
          }

          pbar.end();
          request.addTrailers({'x-content-sha1': sha1.digest('base64')});
          request.end();

          request.on('response', function(response) {
            if (response.statusCode === 204) {
              return callback();
            }
            var chunks = [];

            response.on('data', function(data) {
              chunks.push(data);
            });

            response.on('end', function() {
              try {
                var err = JSON.parse(chunks.join(''));
                return callback(new Error(err.message));
              }
              catch (err2) {
                return callback(new Error('Unexpected response from agent'));
              }
            });
          });

          sys.puts('Waiting for response...');
        });
      });
    }
  ],
  function(err) {
    if (err) {
      sys.puts('Error: ' + err.message);
    }
    else {
      sys.puts('Upload Successful');
    }
  });
};

@@ -4585,23 +4585,23 @@
     var 
-request
+headers
  = clien
@@ -4606,78 +4606,33 @@
 ent.
-request('PUT', remotepath, %7B%0A          'host': 'localhost',%0A          
+headers;%0A        headers%5B
 'con
@@ -4647,15 +4647,17 @@
 gth'
-:
+%5D =
  size
-,
+;
 %0A   
@@ -4657,26 +4657,32 @@
 ze;%0A        
-  
+headers%5B
 'transfer-en
@@ -4688,17 +4688,19 @@
 ncoding'
-:
+%5D =
  'chunke
@@ -4701,17 +4701,17 @@
 chunked'
-,
+;
 %0A       
@@ -4711,18 +4711,24 @@
         
-  
+headers%5B
 'trailer
@@ -4728,17 +4728,19 @@
 trailer'
-:
+%5D =
  'x-cont
@@ -4748,16 +4748,18 @@
 nt-sha1'
+;%0A
 %0A       
@@ -4751,33 +4751,87 @@
 sha1';%0A%0A        
-%7D
+var request = client.request('PUT', remotepath, headers
 );%0A%0A        pump

Fix some weird hardcoded stuff in 'bundles upload'