'use strict';

var plantumlExecutor = require('./plantuml-executor');
var fs = require('fs');
var os = require('os');
var stream = require('stream');
var util = require('util');
var path = require('path');
var plantumlEncoder = require('plantuml-encoder');

var ENCODE = '-encodeurl';
var DECODE = '-decodeurl';
var PIPE = '-pipe';
var UNICODE = '-tutxt';
var ASCII = '-ttxt';
var PNG = '';
var SVG = '-svg';
var CONFIG = '-config';

var CONFIGS = {
  classic: path.join(__dirname, '../resources/classic.puml'),
  monochrome: path.join(__dirname, '../resources/monochrome.puml'),
};

module.exports.useNailgun = plantumlExecutor.useNailgun;

function PlantumlEncodeStream() {
  stream.Transform.call(this);
  this.chunks = [];
}

util.inherits(PlantumlEncodeStream, stream.Transform);

PlantumlEncodeStream.prototype._transform = function (chunk, encoding, callback) {
  this.chunks.push(chunk);
  callback();
};

PlantumlEncodeStream.prototype._flush = function (callback) {
  var uml = Buffer.concat(this.chunks).toString();
  var encoded = plantumlEncoder.encodeSync(uml);
  this.push(new Buffer(encoded + '\n'));
  callback();
};

module.exports.encodeFile = function(path, cb) {
  var child = plantumlExecutor.exec([ENCODE, path], cb);

  return {
    out: child.stdout,
  };
};

function isPath(input) {
  try {
    fs.lstatSync(input);
    return true;
  } catch (e) {
    return false;
  }
}

function arrangeArguments(input, options, callback) {
  if (util.isFunction(input)) {
    callback = input;
    input = undefined;
  } else {
    if (util.isFunction(options)) {
      callback = options;
      options = undefined;
    }
    if (typeof input !== 'string' && !(input instanceof String)) {
      options = input;
      input = undefined;
    }
  }

  return {
    input: input,
    options: options,
    callback: callback,
  };
}

function joinOptions(argv, options) {
  var format;
  options.format = options.format || 'png';
  switch (options.format) {
    case 'ascii': {
      format = ASCII;
      break;
    }
    case 'unicode': {
      format = UNICODE;
      break;
    }
    case 'svg': {
      format = SVG;
      break;
    }
    case 'png':
    default: {
      format = PNG;
      break;
    }
  }

  var config;
  if (options.config) {
    var template = CONFIGS[options.config];
    var file = template || options.config;
    config = [CONFIG, file];
  }

  return argv.concat(format, config);
}

function generateFromStdin(child) {
  return {
    in: child.stdin,
    out: child.stdout,
  };
}

function generateFromFile(path, child) {
  var rs = fs.createReadStream(path);
  rs.pipe(child.stdin);

  return {
    out: child.stdout,
  };
}

function generateFromText(text, child) {
  text = '@startuml' + os.EOL + text + os.EOL + '@enduml';
  child.stdin.write(text);
  child.stdin.end();

  return {
    out: child.stdout,
  };
}

module.exports.generate = function(input, options, callback) {
  var args = arrangeArguments(input, options, callback);
  input = args.input;
  options = args.options;
  callback = args.callback;

  var o = joinOptions([PIPE], options);
  var child = plantumlExecutor.exec(o, callback);

  if (!input) {
    return generateFromStdin(child);
  } else {
    if (isPath(input, callback)) {
      return generateFromFile(input, child);
    } else {
      return generateFromText(input, child);
    }
  }
};

function encodeFromStdin(encodeStream) {
  return {
    in: encodeStream,
    out: encodeStream,
  };
}

function encodeFromFile(path, encodeStream) {
  var rs = fs.createReadStream(path);
  rs.pipe(encodeStream);

  return {
    out: encodeStream,
  };
}

function encodeFromText(text, encodeStream) {
  encodeStream.write(text);
  encodeStream.end();

  return {
    out: encodeStream,
  };
}

module.exports.encode = function(input, options, callback) {
  var args = arrangeArguments(input, options, callback);
  input = args.input;
  options = args.options || {};
  callback = args.callback;

  var encodeStream = new PlantumlEncodeStream();

  if (util.isFunction(callback)) {
    var chunks = [];
    encodeStream.on('data', function(chunk) { chunks.push(chunk); });
    encodeStream.on('end', function() {
      var data = Buffer.concat(chunks);
      callback(null, data.toString());
    });
  }

  if (!input) {
    return encodeFromStdin(encodeStream);
  } else {
    if (isPath(input, callback)) {
      return encodeFromFile(input, encodeStream);
    } else {
      return encodeFromText(input, encodeStream);
    }
  }
};

module.exports.decode = function(encoded, cb) {
  var child = plantumlExecutor.exec([DECODE, encoded], cb);

  return {
    out: child.stdout,
  };
};

@@ -1882,22 +1882,8 @@
 ) %7B%0A
-  var format;%0A
   op
@@ -1980,22 +1980,24 @@
     
-format = 
+argv.push(
 ASCII
+)
 ;%0A  
@@ -2045,24 +2045,26 @@
     
-format = 
+argv.push(
 UNICODE
+)
 ;%0A  
@@ -2108,20 +2108,22 @@
     
-format = 
+argv.push(
 SVG
+)
 ;%0A  
@@ -2174,28 +2174,8 @@
 : %7B%0A
-      format = PNG;%0A
     
@@ -2198,22 +2198,8 @@
  %7D%0A%0A
-  var config;%0A
   if
@@ -2313,31 +2313,46 @@
     
-config = %5BCONFIG, 
+argv.push(CONFIG);%0A    argv.push(
 file
-%5D
+)
 ;%0A  
@@ -2371,31 +2371,8 @@
 argv
-.concat(format, config)
 ;%0A%7D%0A

Fix empty and undefined execute arguments