'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _block = require('../models/block');

var _block2 = _interopRequireDefault(_block);

var _character = require('../models/character');

var _character2 = _interopRequireDefault(_character);

var _document = require('../models/document');

var _document2 = _interopRequireDefault(_document);

var _inline = require('../models/inline');

var _inline2 = _interopRequireDefault(_inline);

var _mark = require('../models/mark');

var _mark2 = _interopRequireDefault(_mark);

var _state = require('../models/state');

var _state2 = _interopRequireDefault(_state);

var _text = require('../models/text');

var _text2 = _interopRequireDefault(_text);

var _groupByMarks = require('../utils/group-by-marks');

var _groupByMarks2 = _interopRequireDefault(_groupByMarks);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

/**
 * Serialize a `state`.
 *
 * @param {State} state
 * @return {Object} object
 */

function serialize(state) {
  return serializeNode(state.document);
}

/**
 * Serialize a `node`.
 *
 * @param {Node} node
 * @return {Object} object
 */

function serializeNode(node) {
  switch (node.kind) {
    case 'document':
      {
        return {
          nodes: node.nodes.toArray().map(function (child) {
            return serializeNode(child);
          })
        };
      }
    case 'text':
      {
        return {
          kind: node.kind,
          ranges: serializeCharacters(node.characters)
        };
      }
    case 'block':
    case 'inline':
      {
        var obj = {};
        obj.kind = node.kind;
        obj.type = node.type;
        obj.nodes = node.nodes.toArray().map(function (child) {
          return serializeNode(child);
        });
        if (node.isVoid) obj.isVoid = node.isVoid;
        if (node.data.size) obj.data = node.data.toJSON();
        return obj;
      }
    default:
      {
        throw new Error('Unknown node kind "' + node.kind + '".');
      }
  }
}

/**
 * Serialize a list of `characters`.
 *
 * @param {List} characters
 * @return {Array}
 */

function serializeCharacters(characters) {
  return (0, _groupByMarks2.default)(characters).toArray().map(function (range) {
    var obj = {};
    obj.text = range.text;
    if (range.marks.size) obj.marks = range.marks.toArray().map(serializeMark);
    return obj;
  });
}

/**
 * Serialize a `mark`.
 *
 * @param {Mark} mark
 * @return {Object} Object
 */

function serializeMark(mark) {
  var obj = {};
  obj.type = mark.type;
  if (mark.data.size) obj.data = mark.data.toJSON();
  return obj;
}

/**
 * Deserialize a state JSON `object`.
 *
 * @param {Object} object
 * @return {State} state
 */

function deserialize(object) {
  return _state2.default.create({
    document: _document2.default.create({
      nodes: _block2.default.createList(object.nodes.map(deserializeNode))
    })
  });
}

/**
 * Deserialize a node JSON `object`.
 *
 * @param {Object} object
 * @return {Node} node
 */

function deserializeNode(object) {
  switch (object.kind) {
    case 'block':
      {
        return _block2.default.create({
          type: object.type,
          data: object.data,
          isVoid: object.isVoid,
          nodes: _block2.default.createList(object.nodes.map(deserializeNode))
        });
      }
    case 'inline':
      {
        return _inline2.default.create({
          type: object.type,
          data: object.data,
          isVoid: object.isVoid,
          nodes: _inline2.default.createList(object.nodes.map(deserializeNode))
        });
      }
    case 'text':
      {
        return _text2.default.create({
          characters: deserializeRanges(object.ranges)
        });
      }
    default:
      {
        throw new Error('Unknown node kind "' + object.kind + '".');
      }
  }
}

/**
 * Deserialize a JSON `array` of ranges.
 *
 * @param {Array} array
 * @return {List} characters
 */

function deserializeRanges(array) {
  return array.reduce(function (characters, object) {
    var marks = object.marks || [];
    var chars = object.text.split('').map(function (char) {
      return _character2.default.create({
        text: char,
        marks: _mark2.default.createSet(marks.map(deserializeMark))
      });
    });

    return characters.push.apply(characters, _toConsumableArray(chars));
  }, _character2.default.createList());
}

/**
 * Deserialize a mark JSON `object`.
 *
 * @param {Object} object
 * @return {Mark} mark
 */

function deserializeMark(object) {
  return _mark2.default.create(object);
}

/**
 * Export.
 */

exports.default = {
  serialize: serialize,
  serializeCharacters: serializeCharacters,
  serializeMark: serializeMark,
  serializeNode: serializeNode,
  deserialize: deserialize,
  deserializeNode: deserializeNode,
  deserializeRanges: deserializeRanges
};