//
// fast-image-size - Simple stand alone module to just extract the image size from image file without using special image libraries.
//
// Please refer to README.md for this module's documentations.
//
// NOTE:
// - Before changing this code please refer to the 'hacking the code section' on README.md.
//
// Copyright (c) 2013 Ziv Barber;
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// 'Software'), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

var fast_image_size_info = require('./package.json');
var fs = require('fs');

module.exports = exports = function ( file_path, callback ) {
	// Internal options:
	if ( file_path == '@version@' ) {
		return fast_image_size_info.version;
	} // Endif.

	function getJpgSize( buffer_data, retInfo ) {
	  // Skip 5 chars, they are for signature
	  buffer_data = buffer_data.slice( 4 );

	  var i, next;
	  while (buffer_data.length) {
	    // read length of the next block
	    i = buffer_data.readUInt16BE( 0 );

	    // 0xFFC0 is baseline(SOF)
	    // 0xFFC2 is progressive(SOF2)
	    next = buffer_data[ i + 1 ];
	    if (next === 0xC0 || next === 0xC2) {
	      return {
	        'height' : buffer_data.readUInt16BE( i + 5 ),
	        'width' : buffer_data.readUInt16BE( i + 7 )
	      };
	    }

	    // move to the next block
	    buffer_data = buffer_data.slice( i + 2 );
	  }
	}

	function parseHeaderData ( buffer_data, callback_data ) {
		var retInfo = {};

		// Detect GIF:
		if ( buffer_data[0] == 0x47 && buffer_data[1] == 0x49 && buffer_data[2] == 0x46 ) {
			retInfo.type = 'gif';
			retInfo.width = (buffer_data[7] * 256) + buffer_data[6];
			retInfo.height = (buffer_data[9] * 256) + buffer_data[8];

		// Detect JPEG:
		} else if ( buffer_data[0] == 0xFF && buffer_data[1] == 0xD8 && buffer_data[2] == 0xFF && buffer_data[3] == 0xE0 ) {
		  retInfo.type = 'jpeg';
		  var size = getJpgSize( buffer_data, retInfo );
		  retInfo.width = size.width;
		  retInfo.height = size.height;

		// Detect PNG:
		} else if ( buffer_data[0] == 137 && buffer_data[1] == 80 && buffer_data[2] == 78 && buffer_data[3] == 71 && buffer_data[4] == 13 && buffer_data[5] == 10 && buffer_data[6] == 26 && buffer_data[7] == 10 ) {
			retInfo.type = 'png';

			if ( buffer_data[12] == 0x49 && buffer_data[13] == 0x48 && buffer_data[14] == 0x44 && buffer_data[15] == 0x52 ) {
				retInfo.width = (buffer_data[16] * 256 * 256 * 256) + (buffer_data[17] * 256 * 256) + (buffer_data[18] * 256) + buffer_data[19];
				retInfo.height = (buffer_data[20] * 256 * 256 * 256) + (buffer_data[21] * 256 * 256) + (buffer_data[22] * 256) + buffer_data[23];
			} // Endif.

		// Detect BMP:
		} else if ( buffer_data[0] == 0x42 && buffer_data[1] == 0x4D ) {
			retInfo.type = 'bmp';
			retInfo.width = (buffer_data[21] * 256 * 256 * 256) + (buffer_data[20] * 256 * 256) + (buffer_data[19] * 256) + buffer_data[18];
			retInfo.height = (buffer_data[25] * 256 * 256 * 256) + (buffer_data[24] * 256 * 256) + (buffer_data[23] * 256) + buffer_data[22];
		} // Endif.

		retInfo.image = file_path;
		if ( !retInfo.type ) {
			retInfo.type = 'unknown';
		} // Endif.

		if ( callback_data ) {
			callback_data ( retInfo );
		} // Endif.

		return retInfo;
	};

	// Async mode:
	if ( callback ) {
	  fs.exists ( file_path, function( exists ) {
	    if ( exists ) {
	      fs.open ( file_path, "r", function ( error, fd ) {
	        var bufferSize = fs.fstatSync( fd ).size;
	        var buffer = new Buffer( bufferSize );
	        fs.read ( fd, buffer, 0, bufferSize, null, function ( error, bytesRead, buffer ) {
	          fs.close ( fd );
	          parseHeaderData ( buffer, callback );
	        });
	      });
			} else {
				throw 'Error: Invalid file name.';
			} // Endif.
		});

	// Sync mode:
	} else {
		var fd = fs.openSync ( file_path, "r" );
		var bufferSize = fs.fstatSync( fd ).size;
		var buffer = new Buffer( bufferSize );
		var bytesRead = fs.readSync ( fd, buffer, 0, bufferSize, 0 );
		fs.closeSync ( fd );
		return parseHeaderData ( buffer, null );
	} // Endif.
};

