var fs = require('fs');
var Stream = require('stream').Readable;

var Boom = require('boom');

var map = require('../map.json');

var internals = {};

exports = module.exports = {};

internals.validateMagicNumber = function (magic, reference, callback) {
  if (reference.indexOf(magic) > -1) {
    return callback();
  }
  return callback(new Error('The file type is not supported.'));
};

internals.validateBuffer = function (buffer, reference, callback) {
  var magic = buffer.slice(0, 2).toString('hex');
  internals.validateMagicNumber(magic, reference, callback);
};

internals.validateStream = function (stream, reference, callback) {
  var magic;

  stream.on('data', function (buffer) {
    magic = buffer.slice(0, 2).toString('hex');
    internals.validateMagicNumber(magic, reference, callback);
  });
};

internals.validateFile = function (file, reference, callback) {
  var stream = fs.createReadStream(file.path), magic;

  stream.on('data', function (buffer) {
    magic = buffer.slice(0, 2).toString('hex');
    internals.validateMagicNumber(magic, reference, callback);
  });
};

internals.hasValidStructure = function (obj) {
  var required = ['filename', 'path', 'headers', 'bytes'];

  return (Object.keys(obj).filter(function (key) {
    return (required.indexOf(key) > -1 );
  }).length === required.length);
};

internals.validateFieldContent = function (content, reference, callback) {
  if (content instanceof Buffer) {
    internals.validateBuffer(content, reference, callback);
  } else if (content instanceof Stream) {
    internals.validateStream(content, reference, callback);
  } else if (content instanceof Object && internals.hasValidStructure(content)) {
    internals.validateFile(content, reference, callback);
  } else {
    return callback();
  }
};

internals.allowedMagicNumbers = function (supported) {
  var allowed = [];

  for (var i = 0, len = supported.length; i < len; i++) {
    if (map.hasOwnProperty(supported[i])) {
      allowed.push(map[supported[i]]);
    }
  }

  return allowed;
};

internals.validate = function (payload, reference, callback) {
  var keys = Object.keys(payload), last = keys.length - 1, field, message;

  reference = internals.allowedMagicNumbers(reference);

  (function validateAllFields(iterator) {
    field = keys[iterator];

    internals.validateFieldContent(payload[field], reference, function (err) {
      if (err) {
        message = [err.message, ' [field = ', field, ']'].join('');
        return callback(Boom.unsupportedMediaType(message));
      }

      if (iterator === last) {
        return callback();
      }

      return validateAllFields(iterator + 1);
    });
  }(0));
};

exports.register = function (plugin, options, next) {
  plugin.ext('onPreHandler', function (request, callback) {
    if (request.method === 'post' && request.mime === 'multipart/form-data') {
      return internals.validate(request.payload, options.supported, callback);
    }

    return callback();
  });

  next();
};

exports.register.attributes = {
  name: 'hapi-magic-filter',
  version: '0.1.0'
};

@@ -393,28 +393,30 @@
 ternals.
-validateBuff
+parseMagicNumb
 er = fun
@@ -586,22 +586,22 @@
 validate
-Stream
+Buffer
  = funct
@@ -605,22 +605,22 @@
 nction (
-stream
+buffer
 , refere
@@ -642,19 +642,145 @@
 %7B%0A  
-var magic;%0A
+internals.parseMagicNumber(buffer, reference, callback);%0A%7D;%0A%0Ainternals.addStreamEventListeners = function (stream, reference, callback) %7B
 %0A  s
@@ -824,49 +824,21 @@
     
-magic = buffer.slice(0, 2).toString('hex'
+stream.pause(
 );%0A 
@@ -842,39 +842,36 @@
 ;%0A    internals.
-validat
+pars
 eMagicNumber(mag
@@ -859,37 +859,38 @@
 arseMagicNumber(
-magic
+buffer
 , reference, cal
@@ -925,20 +925,22 @@
 validate
-File
+Stream
  = funct
@@ -944,20 +944,22 @@
 nction (
-file
+stream
 , refere
@@ -981,153 +981,196 @@
 %7B%0A  
-var stream = fs.createReadStream(file.path), magic;%0A%0A  stream.on('data', function (buffer) %7B%0A    magic = buffer.slice(0, 2).toString('hex')
+internals.addStreamEventListeners(stream, reference, callback);%0A%7D;%0A%0Ainternals.validateFile = function (file, reference, callback) %7B%0A  var stream = fs.createReadStream(file.path), magic
 ;%0A  
- 
- 
 inte
@@ -1179,33 +1179,38 @@
 als.
-validateMagicNumber(magic
+addStreamEventListeners(stream
 , re
@@ -1221,38 +1221,32 @@
 nce, callback);%0A
-  %7D);%0A
 %7D;%0A%0Ainternals.ha

Fix remaining minor issues.