/*
*copyright Ryan Day 2012
*
* Licensed under the MIT license:
*   http://www.opensource.org/licenses/mit-license.php
*
* this is the main server side application file for node-qrcode. 
* these exports use serverside canvas api methods for file IO and buffers
*
*/

var QRCodeLib = require('./lib/qrcode-draw')
, terminalRender = require('./lib/termialrender.js')
, svgRender = require('./lib/svgrender')
, Canvas = require('canvas')
, fs = require('fs');


var QRCodeDraw = QRCodeLib.QRCodeDraw,
  QRCode = QRCodeLib.QRCode;

//EXPORTS

//
// breaking change to 0.1 this used to be an instance. now it returns the constructor.
//
exports.QRCodeDraw = QRCodeDraw;

//
// export error correct levels.
//
exports.errorCorrectLevels = QRCodeLib.QRErrorCorrectLevel;

//
// export original canvas to be used with draw method, esp. Canvas.Image
//
exports.canvas=Canvas;

/*
* provide an api to return the max characters allowed for given dimensions, and miniumum error correction level
* the qr code library will always use the maximum error correction level for the given numbar of chars constrained by size
*/
exports.getMaxChars = function(minErrorCorrectionLevel,width,moduleScale){
	//TODO THIS NEEDS TO WORK
  console.log('this doesnt work yet. comming soon =)');
};

var parseOptions = function(options) {
  var textKeys = {'minimum':"L",'medium':"M",'high':"Q",'max':"H"}
	if(options.errorCorrectLevel) {
    var ec = options.errorCorrectLevel;  
    if(textKeys[ec]){
      options.errorCorrectLevel = textKeys[ec];
    }
  }
  return options;
};

// returns Canvas Object with qr code drawn on it
/*
* String text, optional Object options, Function callback
*/
var draw = exports.draw = function(text,options,cb){

	var args = Array.prototype.slice.call(arguments);
	cb = args.pop();
	if(typeof cb != 'function') {
		throw new TypeError('last argument must be a function');
	}
	
	text = args.shift();
	options = args.shift()||{};
    options=parseOptions(options);

	//NOTE the width and height are determined from within the qr code lib and are not configurable from the outside yet
  
	var drawInstance = new QRCodeDraw();
	drawInstance.draw(new Canvas(200,200),text,options,function(error,canvas,qrWidth){
		cb(error,canvas,qrWidth)
	});
};

//returns data uri for drawn qrcode png
exports.toDataURL = exports.toDataURI = function(text,options,cb){

  if(typeof options == 'function') {
    cb = options;
    options = {};
  }

  draw(text,options,function(error,canvas){
    if(error) {
      cb(error);
    } else {
      canvas.toDataURL(cb);
    }
  });
}

//synchronous PNGStream
exports.toPNGStream = function (text, WSpath, options,cb) {

  if(typeof options == 'function'){
    cb = options;
    options = {};
  }

  var out = fs.createWriteStream(WSpath);

  draw(text, options, function (error,canvas) {
    if(error) {
      cb(error,'');
    } else {
      stream = canvas.createPNGStream();
    }

    stream.pipe(out);

    stream.on('end', function () {
      cb(error,'');
    });

    stream.pipe(out);
    
  });

  return out;
}

//returns bytes written to file 
exports.save = function(path,text,options,cb){

  if(typeof options == 'function'){
    cb = options;
    options = {};
  }

  var fileExt = path.slice((path.lastIndexOf(".") - 1 >>> 0) + 2).toLowerCase();

  if (fileExt === 'svg') {
    saveSvg(path,text,options,cb);
  } else {
    savePng(path,text,options,cb);
  }
};

function savePng(path,text,options,cb){
	draw(text, options, function(error,canvas){

		var fd,buf,fdAndBuf = function(){
			fs.write(fd, buf, 0, buf.length, 0, function(fsErr, written){
				fs.close(fd);
				if(cb) cb(fsErr, written);
			});
		};

		//run non dependent async calls at the same time ish
		canvas.toBuffer(function(canvasErr, _buf){
			if(canvasErr) return cb(canvasErr);
			buf = _buf
			if(fd) fdAndBuf();
		});

		fs.open(path, 'w', 0666, function(fsErr,_fd){
			if(fsErr) return cb(fsErr);
			fd = _fd
			if(buf) fdAndBuf();
		});

	});
}

function saveSvg(path,text,options,cb){
  exports.drawSvg(text,function(error,code){
    if (!error) {
      fs.writeFile(path, code, function(fsErr) {
        return cb(fsErr, fsErr ? null : code);
      });
    }
  });
}


//
//this returns an array of points that have either a 0 or 1 value representing 0 for light and 1 for dark
//these values include points in the white edge of the qrcode because that edge is actually part of the spec  
//
exports.drawBitArray = function(text,options,cb){

  if(typeof options == 'function'){
    cb = options;
    options = {};
  }
  options = parseOptions(options);

  var drawInstance = new QRCodeDraw();
  drawInstance.drawBitArray(text,options,function(error,bits,width){
    cb(error,bits,width);
  });
}

//
// draw qr in your terminal!
//
exports.drawText = function(text,options,cb){

  if(typeof options == 'function'){
    cb = options;
    options = {};
  }

  var drawInstance = new QRCodeDraw();
  drawInstance.drawBitArray(text,function(error,bits,width){
    if (!error) {
      var code = terminalRender.renderBits(bits,width);
      cb(error,code);
    } else {
      cb(error,null);
    }
  });
}

exports.drawSvg = function(text,options,cb){
  if(typeof options == 'function'){
    cb = options;
    options = {};
  }

  var drawInstance = new QRCodeDraw();
  drawInstance.drawBitArray(text,function(error,bits,width){
    if (!error) {
      var code = svgRender.renderBits(bits,width,options);
      cb(error,code);
    } else {
      cb(error,null);
    }
  });
}