text: "use strict";
  var fs = require("fs"),
  	path = require("path"),
  	exec = require("child_process").exec;
  	
  var noop = function () {}; 
  
  var sysFsPathOld = "/sys/devices/virtual/gpio", // pre 3.18.x kernel
  	sysFsPathNew = "/sys/class/gpio", // post 3.18.x kernel
  	sysFsPath;
  
  var rev = fs.readFileSync("/proc/cpuinfo").toString().split("\n").filter(function(line) {
  	return line.indexOf("Revision") == 0;
  })[0].split(":")[1].trim();
  
  // tests the device tree directory to determine the actual gpio path
  if (fs.existsSync(sysFsPathNew)) {
  	sysFsPath = sysFsPathNew;
  } else {
  	sysFsPath = sysFsPathOld; // fallback for old kernels
  }
  
  rev = parseInt(rev, 16) < 3 ? 1 : 2; // http://elinux.org/RPi_HardwareHistory#Board_Revision_History
  
  var pinMapping = {
  	"3": 0,
  	"5": 1,
  	"7": 4,
  	"8": 14,
  	"10": 15,
  	"11": 17,
  	"12": 18,
  	"13": 21,
  	"15": 22,
  	"16": 23,
  	"18": 24,
  	"19": 10,
  	"21": 9,
  	"22": 25,
  	"23": 11,
  	"24": 8,
  	"26": 7,
  
  	// Model A+ and Model B+ pins
  	"29": 5,
  	"31": 6,
  	"32": 12,
  	"33": 13,
  	"35": 19,
  	"36": 16,
  	"37": 26,
  	"38": 20,
  	"40": 21
  };
  
  if (rev == 2) {
  	pinMapping["3"] = 2;
  	pinMapping["5"] = 3;
  	pinMapping["13"] = 27;
  }
  
  function isNumber(number) {
  	return !isNaN(parseInt(number, 10));
  }
  
  function handleExecResponse(method, pinNumber, callback) {
  	return function(err, stdout, stderr) {
  		if (err) {
  			console.error("Error when trying to", method, "pin", pinNumber);
  			console.error(stderr);
  			callback(err);
  		} else {
  			callback();
  		}
  	}
  }
  
  function sanitizePinNumber(pinNumber) {
  	if (!isNumber(pinNumber) || !isNumber(pinMapping[pinNumber])) {
  		throw new Error("Pin number isn't valid");
  	}
  
  	return parseInt(pinNumber, 10);
  }
  
  function sanitizeDirection(direction) {
  	direction = (direction || "").toLowerCase().trim();
  	if (direction === "in" || direction === "input") {
  		return "in";
  	} else if (direction === "out" || direction === "output" || !direction) {
  		return "out";
  	} else {
  		throw new Error("Direction must be 'input' or 'output'");
  	}
  }
  
  function sanitizeOptions(options) {
  	var sanitized = {};
  
  	options.split(" ").forEach(function(token) {
  		if (token == "in" || token == "input") {
  			sanitized.direction = "in";
  		}
  
  		if (token == "pullup" || token == "up") {
  			sanitized.pull = "pullup";
  		}
  
  		if (token == "pulldown" || token == "down") {
  			sanitized.pull = "pulldown";
  		}
  	});
  
  	if (!sanitized.direction) {
  		sanitized.direction = "out";
  	}
  
  	if (!sanitized.pull) {
  		sanitized.pull = "";
  	}
  
  	return sanitized;
  }
  
  var gpio = {
  	rev: rev,
  
  	open: function(pinNumber, options, callback) {
  		pinNumber = sanitizePinNumber(pinNumber);
  
  		if (!callback && typeof options === "function") {
  			callback = options;
  			options = "out";
  		}
  
  		options = sanitizeOptions(options);
  		
  		if (!fs.existsSync(sysFsPath + "/gpio" + pinMapping[pinNumber]))
  		{
  			fs.writeFile(sysFsPath + "/export" , pinMapping[pinNumber].toString(), "utf8", function(err)
  			{
  				if (err) return callback(err);
  				gpio.setDirection(pinNumber, options.direction, (callback || noop));
  			});
  		}
  		else
  		{
  			gpio.setDirection(pinNumber, options.direction, (callback || noop));
  		}
  	},
  
  	setDirection: function(pinNumber, direction, callback) {
  		pinNumber = sanitizePinNumber(pinNumber);
  		direction = sanitizeDirection(direction);
  
  		fs.writeFile(sysFsPath + "/gpio" + pinMapping[pinNumber] + "/direction", direction, "utf8", (callback || noop));
  	},
  
  	getDirection: function(pinNumber, callback) {
  		pinNumber = sanitizePinNumber(pinNumber);
  		callback = callback || noop;
  
  		fs.readFile(sysFsPath + "/gpio" + pinMapping[pinNumber] + "/direction", "utf8", function(err, direction) {
  			if (err) return callback(err);
  			callback(null, sanitizeDirection(direction.trim()));
  		});
  	},
  
  	close: function(pinNumber, callback) {
  		pinNumber = sanitizePinNumber(pinNumber);
  	},
  
  	read: function(pinNumber, callback) {
  		pinNumber = sanitizePinNumber(pinNumber);
  
  		fs.readFile(sysFsPath + "/gpio" + pinMapping[pinNumber] + "/value", function(err, data) {
  			if (err) return (callback || noop)(err);
  
  			(callback || noop)(null, parseInt(data, 10));
  		});
  	},
  
  	write: function(pinNumber, value, callback) {
  		pinNumber = sanitizePinNumber(pinNumber);
  
  		value = !!value ? "1" : "0";
  
  		fs.writeFile(sysFsPath + "/gpio" + pinMapping[pinNumber] + "/value", value, "utf8", (callback || noop));
  	}
  };
  
  gpio.export = gpio.open;
  gpio.unexport = gpio.close;
  
  module.exports = gpio;
  