'use strict';
var isPlainObject = require('lodash/isPlainObject');
var isFunction = require('lodash/isFunction');
var _find = require('lodash/find');
var _extend = require('lodash/extend');
var _transform = require('lodash/transform');
var Inflector = require('./inflector');

module.exports = function (jsonapi, data, opts) {
  var alreadyIncluded = [];

  function isComplexType(obj) {
    return Array.isArray(obj) || isPlainObject(obj);
  }

  function getValueForRelationship(relationshipData, included) {
    if (opts && relationshipData && opts[relationshipData.type]) {
      var valueForRelationshipFct = opts[relationshipData.type]
        .valueForRelationship;

      return valueForRelationshipFct(relationshipData, included);
    } else {
      return included;
    }
  }

  function findIncluded(relationshipData, relationshipName, from) {
    return new Promise(function (resolve) {
      if (!jsonapi.included || !relationshipData) { return resolve(null); }

      var included = _find(jsonapi.included, {
        id: relationshipData.id,
        type: relationshipData.type
      });

      var includedObject = {
        to: {
          id: from.id,
          type: from.type,
        },
        from: Object.assign({}, relationshipData),
        relation: relationshipName,
      };

      // Check if the include is already processed (prevent circular
      // references).
      if (_find(alreadyIncluded, includedObject)) {
        return resolve(null);
      } else {
        alreadyIncluded.push(includedObject);
      }

      if (included) {
        return Promise
          .all([extractAttributes(included), extractRelationships(included)])
          .then(function (results) {
            var attributes = results[0];
            var relationships = results[1];
            resolve(_extend(attributes, relationships));
          });
      } else {
        return resolve(null);
      }
    });
  }

  function keyForAttribute(attribute) {
    if (isPlainObject(attribute)) {
      return _transform(attribute, function (result, value, key) {
        if (isComplexType(value)) {
          result[keyForAttribute(key)] = keyForAttribute(value);
        } else {
          result[keyForAttribute(key)] = value;
        }
      });
    } else if (Array.isArray(attribute)) {
      return attribute.map(function (attr) {
        if (isComplexType(attr)) {
          return keyForAttribute(attr);
        } else {
          return attr;
        }
      });
    } else {
      if (isFunction(opts.keyForAttribute)) {
        return opts.keyForAttribute(attribute);
      } else {
        return Inflector.caserize(attribute, opts);
      }
    }
  }

  function extractAttributes(from) {
    var dest = keyForAttribute(from.attributes || {});
    if ('id' in from) { dest[opts.id || 'id'] = from.id; }

    if (opts.typeAsAttribute) {
      if ('type' in from) { dest.type = from.type; }
    }
    if ('meta' in from) { dest.meta = keyForAttribute(from.meta || {}) }

    return dest;
  }

  function extractRelationships(from) {
    if (!from.relationships) { return; }

    var dest = {};

    return Promise
      .all(Object.keys(from.relationships).map(function (key) {
        var relationship = from.relationships[key];

        if (relationship.data === null) {
          dest[keyForAttribute(key)] = null;
        } else if (Array.isArray(relationship.data)) {
          return Promise
            .all(relationship.data.map(function (relationshipData) {
              return extractIncludes(relationshipData, key, from);
            }))
            .then(function (includes) {
              if (includes) { dest[keyForAttribute(key)] = includes; }
            });
        } else {
          return extractIncludes(relationship.data, key, from)
            .then(function (include) {
              if (include) { dest[keyForAttribute(key)] = include; }
            });
        }
      }))
      .then(function() {
        return dest;
      });
  }

  function extractIncludes(relationshipData, relationshipName, from) {
    return findIncluded(relationshipData, relationshipName, from)
      .then(function (included) {
        var valueForRelationship = getValueForRelationship(relationshipData,
          included);

        if (valueForRelationship && isFunction(valueForRelationship.then)) {
          return valueForRelationship.then(function (value) {
            return value;
          });
        } else {
          return valueForRelationship;
        }
      });
  }

  this.perform = function () {
    return Promise
      .all([extractAttributes(data), extractRelationships(data)])
      .then(function (results) {
        var attributes = results[0];
        var relationships = results[1];
        var record = _extend(attributes, relationships);

        // Links
        if (jsonapi.links) {
          record.links = jsonapi.links;
        }


        // If option is present, transform record
        if (opts && opts.transform) {
          record = opts.transform(record);
        }

        return record;
      });
  };
};

@@ -225,24 +225,130 @@
 ransform');%0A
+var _merge = require('lodash/merge');%0Avar _get = require('lodash/get');%0Avar _set = require('lodash/set');%0A
 var Inflecto
@@ -454,10 +454,10 @@
 d = 
-%5B%5D
+%7B%7D
 ;%0A%0A 
@@ -1216,26 +1216,16 @@
 var 
-includedObject = %7B
+path = %5B
 %0A   
@@ -1233,21 +1233,26 @@
     
-to: %7B
+from.type,
 %0A       
    i
@@ -1247,22 +1247,16 @@
 %0A       
-   id:
  from.id
@@ -1269,69 +1269,33 @@
     
-  type: from.type,%0A        %7D,%0A        from: Object.assign(%7B%7D,
+relationshipName,%0A       
  rel
@@ -1307,17 +1307,21 @@
 shipData
-)
+.type
 ,%0A      
@@ -1334,34 +1334,27 @@
 tion
-: relationshipName
+shipData.id
 ,%0A      
 %7D;%0A%0A
@@ -1349,18 +1349,17 @@
 ,%0A      
-%7D;
+%5D
 %0A%0A      
@@ -1454,20 +1454,19 @@
    if (_
-find
+get
 (already
@@ -1475,30 +1475,27 @@
 cluded, 
-includedObject
+path, false
 )) %7B%0A   
@@ -1544,16 +1544,23 @@
         
+_merge(
 alreadyI
@@ -1570,28 +1570,30 @@
 uded
-.push(includedObject
+, _set(%7B%7D, path, true)
 );%0A 

Store included relations in object instead of an array