'use strict';

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

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

var _classnames = require('classnames');

var _classnames2 = _interopRequireDefault(_classnames);

var _entries = require('lodash/entries');

var _entries2 = _interopRequireDefault(_entries);

var _JSONSchemaRenderer = require('./JSONSchemaRenderer.scss');

var _JSONSchemaRenderer2 = _interopRequireDefault(_JSONSchemaRenderer);

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

function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }

var className = 'json-schema-renderer';

var RendererProptypes = exports.RendererProptypes = {
	propertyKey: _react.PropTypes.string.isRequired,
	title: _react.PropTypes.string
};

var SchemaProptypes = {
	jsonSchema: _react.PropTypes.object.isRequired,
	uiSchema: _react.PropTypes.shape({
		'ui:order': _react.PropTypes.array
	}),
	properties: _react.PropTypes.object.isRequired
};

/**
 * UnkownTypeException
 *
 * @param {string} type - The unkown type
 * @returns {Object} An UnkownTypeException
 */
function UnkownTypeException(type) {
	this.name = 'UnkownTypeException';
	this.message = 'Unkown type: ' + type;
}

/**
 * InvalidSchemaException
 *
 * @returns {Object} An InvalidSchemaException
 */
function InvalidSchemaException() {
	this.name = 'InvalidSchemaException';
	this.message = 'Invalid Schema';
}

/**
 * HiddenRenderer renders nothing to hide specified properties
 *
 * @returns null
 */
function HiddenRenderer() {
	return null;
}

/**
 * UnkownRenderer renders nothing in case the property doesn't have a
 * coresponding schema definition
 *
 * @returns {undefined}
 */
function UnkownRenderer() {
	return null;
}

/**
 * TextRenderer renders text based properties (string and numbers)
 */
function TextRenderer(_ref) {
	var propertyKey = _ref.propertyKey,
	    title = _ref.title,
	    properties = _ref.properties;

	return _react2.default.createElement(
		'div',
		{ className: (0, _classnames2.default)('text-renderer', 'text-renderer-' + propertyKey), key: propertyKey },
		_react2.default.createElement(
			'dt',
			null,
			title || propertyKey
		),
		_react2.default.createElement(
			'dd',
			null,
			properties
		)
	);
}

TextRenderer.propTypes = _extends({}, RendererProptypes, {
	properties: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.number]).isRequired
});

/**
 * booleanRenderer renders boolean properties
 */
function BooleanRenderer(_ref2) {
	var propertyKey = _ref2.propertyKey,
	    title = _ref2.title,
	    properties = _ref2.properties;

	return _react2.default.createElement(
		'div',
		{ className: (0, _classnames2.default)('boolean-renderer', 'boolean-renderer-' + propertyKey), key: propertyKey },
		_react2.default.createElement(
			'dt',
			null,
			title || propertyKey
		),
		_react2.default.createElement(
			'dd',
			null,
			properties.toString()
		)
	);
}

BooleanRenderer.propTypes = _extends({}, RendererProptypes, {
	properties: _react.PropTypes.bool.isRequired
});

/**
 * arrayRenderer renders an array of properties
 */
function ArrayRenderer(_ref3) {
	var propertyKey = _ref3.propertyKey,
	    title = _ref3.title,
	    properties = _ref3.properties;

	return _react2.default.createElement(
		'div',
		{ className: (0, _classnames2.default)(_JSONSchemaRenderer2.default.array, 'array-renderer-' + propertyKey), key: propertyKey },
		_react2.default.createElement(
			'dt',
			null,
			title || propertyKey
		),
		properties.map(function (val, i) {
			return _react2.default.createElement(
				'dd',
				{ key: 'propertyKey-' + i },
				val
			);
		})
	);
}

ArrayRenderer.propTypes = _extends({}, RendererProptypes, {
	properties: _react.PropTypes.arrayOf(_react.PropTypes.shape(_extends({}, RendererProptypes))).isRequired
});

var registry = {
	string: TextRenderer,
	integer: TextRenderer,
	boolean: BooleanRenderer,
	array: ArrayRenderer,
	object: ObjectRenderer // eslint-disable-line no-use-before-define
};

function isHidden(uiSchema, element) {
	return uiSchema && uiSchema[element] && uiSchema[element]['ui:widget'] === 'hidden';
}

/**
 * typeResolver
 *
 * @param schema - The JSONSchema of the data being rendered
 * @param uiSchema
 * @throws {UnkownTypeException} Type must be part of the registry
 * @returns {Object} Resolved Renderer and props
 */
function typeResolver(schema, uiSchema) {
	return function resolver(e) {
		if (!schema[e[0]]) {
			return { Renderer: UnkownRenderer };
		}
		if (isHidden(uiSchema, e[0])) {
			return { Renderer: HiddenRenderer };
		}

		var type = schema[e[0]].type;
		var title = schema[e[0]].title;

		var renderer = registry[type];
		if (!renderer) {
			throw new UnkownTypeException(type);
		}

		return {
			Renderer: renderer,
			propertyKey: e[0],
			title: title,
			properties: e[1],
			schema: schema,
			uiSchema: uiSchema
		};
	};
}

/**
 * objectRenderer renders nested properties
 */
function ObjectRenderer(_ref4) {
	var propertyKey = _ref4.propertyKey,
	    title = _ref4.title,
	    properties = _ref4.properties,
	    schema = _ref4.schema,
	    _ref4$uiSchema = _ref4.uiSchema,
	    uiSchema = _ref4$uiSchema === undefined ? {} : _ref4$uiSchema;

	var flattenProperties = (0, _entries2.default)(properties);
	var elements = flattenProperties.map(typeResolver(schema[propertyKey].properties, uiSchema[propertyKey]));
	return _react2.default.createElement(
		'div',
		{ className: (0, _classnames2.default)(_JSONSchemaRenderer2.default.object, 'object-renderer-' + propertyKey), key: propertyKey },
		_react2.default.createElement(
			'h2',
			null,
			title || propertyKey
		),
		_react2.default.createElement(
			'div',
			null,
			elements.map(function (_ref5) {
				var Renderer = _ref5.Renderer,
				    rest = _objectWithoutProperties(_ref5, ['Renderer']);

				return _react2.default.createElement(Renderer, rest);
			})
		)
	);
}

ObjectRenderer.propTypes = _extends({}, RendererProptypes, {
	schema: _react.PropTypes.shape(SchemaProptypes)
});

/**
 * orderProperties sorts properties based on uiSchema ui:order array
 *
 * @param order
 * @param properties
 * @returns {Array}
 */
function orderProperties(order, properties) {
	if (!order) {
		return properties;
	}
	return properties.sort(function (a, b) {
		var aIndex = order.indexOf(a[0]);
		var bIndex = order.indexOf(b[0]);
		if (aIndex < 0) {
			return 1;
		}
		if (bIndex < 0) {
			return -1;
		}
		return aIndex - bIndex;
	});
}

/**
 * JSONSchemaRenderer renders elements based on a JSONSchema and data
 *
 * @throws {InvalidSchemaException} schema must contain a jsonSchema and
 * properties
 * @returns {string} - HTML markup for the component
 */
function JSONSchemaRenderer(_ref6) {
	var schema = _ref6.schema;

	if (!schema.jsonSchema || !schema.properties) {
		throw new InvalidSchemaException();
	}
	var properties = (0, _entries2.default)(schema.properties);
	if (schema.uiSchema) {
		properties = orderProperties(schema.uiSchema['ui:order'], properties);
	}
	var elements = properties.map(typeResolver(schema.jsonSchema.properties, schema.uiSchema));
	return _react2.default.createElement(
		'dl',
		{ className: (0, _classnames2.default)(_JSONSchemaRenderer2.default[className], 'json-schema-renderer') },
		elements.map(function (_ref7) {
			var Renderer = _ref7.Renderer,
			    rest = _objectWithoutProperties(_ref7, ['Renderer']);

			return _react2.default.createElement(Renderer, rest);
		})
	);
}

JSONSchemaRenderer.propTypes = {
	schema: _react.PropTypes.shape(_extends({}, SchemaProptypes))
};

JSONSchemaRenderer.InvalidSchemaException = InvalidSchemaException;
JSONSchemaRenderer.UnkownTypeException = UnkownTypeException;

exports.default = JSONSchemaRenderer;