text: "use strict";
  /**
   * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
   *
   * @author Alexander Rose <alexander.rose@weirdbyte.de>
   * @author David Sehnal <david.sehnal@gmail.com>
   */
  Object.defineProperty(exports, "__esModule", { value: true });
  exports.ParamDefinition = void 0;
  var tslib_1 = require("tslib");
  var index_1 = require("./index");
  var linear_algebra_1 = require("../mol-math/linear-algebra");
  var object_1 = require("./object");
  var string_1 = require("./string");
  var lists_1 = require("./color/lists");
  var assets_1 = require("./assets");
  var ParamDefinition;
  (function (ParamDefinition) {
      ParamDefinition.Essential = { isEssential: true };
      function setInfo(param, info) {
          if (!info)
              return param;
          if (info.label)
              param.label = info.label;
          if (info.description)
              param.description = info.description;
          if (info.legend)
              param.legend = info.legend;
          if (info.fieldLabels)
              param.fieldLabels = info.fieldLabels;
          if (info.isHidden)
              param.isHidden = info.isHidden;
          if (info.shortLabel)
              param.shortLabel = info.shortLabel;
          if (info.twoColumns)
              param.twoColumns = info.twoColumns;
          if (info.isEssential)
              param.isEssential = info.isEssential;
          if (info.category)
              param.category = info.category;
          if (info.hideIf)
              param.hideIf = info.hideIf;
          if (info.help)
              param.help = info.help;
          return param;
      }
      function Optional(p) {
          var ret = (0, tslib_1.__assign)({}, p);
          ret.isOptional = true;
          return ret;
      }
      ParamDefinition.Optional = Optional;
      function Value(defaultValue, info) {
          return setInfo({ type: 'value', defaultValue: defaultValue }, info);
      }
      ParamDefinition.Value = Value;
      function Select(defaultValue, options, info) {
          return setInfo({ type: 'select', defaultValue: checkDefaultKey(defaultValue, options), options: options, cycle: info === null || info === void 0 ? void 0 : info.cycle }, info);
      }
      ParamDefinition.Select = Select;
      function MultiSelect(defaultValue, options, info) {
          // TODO: check if default value is a subset of options?
          var ret = setInfo({ type: 'multi-select', defaultValue: defaultValue, options: options }, info);
          if (info === null || info === void 0 ? void 0 : info.emptyValue)
              ret.emptyValue = info.emptyValue;
          return ret;
      }
      ParamDefinition.MultiSelect = MultiSelect;
      function Boolean(defaultValue, info) {
          return setInfo({ type: 'boolean', defaultValue: defaultValue }, info);
      }
      ParamDefinition.Boolean = Boolean;
      function Text(defaultValue, info) {
          if (defaultValue === void 0) { defaultValue = ''; }
          return setInfo({ type: 'text', defaultValue: defaultValue }, info);
      }
      ParamDefinition.Text = Text;
      function Color(defaultValue, info) {
          var ret = setInfo({ type: 'color', defaultValue: defaultValue }, info);
          if (info === null || info === void 0 ? void 0 : info.isExpanded)
              ret.isExpanded = info.isExpanded;
          return ret;
      }
      ParamDefinition.Color = Color;
      function ColorList(defaultValue, info) {
          var def;
          if (typeof defaultValue === 'string') {
              var colors = (0, lists_1.getColorListFromName)(defaultValue);
              def = { kind: colors.type !== 'qualitative' ? 'interpolate' : 'set', colors: colors.list };
          }
          else {
              def = defaultValue;
          }
          return setInfo({ type: 'color-list', presetKind: (info === null || info === void 0 ? void 0 : info.presetKind) || 'all', defaultValue: def, offsets: !!(info === null || info === void 0 ? void 0 : info.offsets) }, info);
      }
      ParamDefinition.ColorList = ColorList;
      function Vec3(defaultValue, range, info) {
          return setInfo(setRange({ type: 'vec3', defaultValue: defaultValue }, range), info);
      }
      ParamDefinition.Vec3 = Vec3;
      function Mat4(defaultValue, info) {
          return setInfo({ type: 'mat4', defaultValue: defaultValue }, info);
      }
      ParamDefinition.Mat4 = Mat4;
      function Url(url, info) {
          var defaultValue = typeof url === 'string' ? assets_1.Asset.Url(url) : assets_1.Asset.Url(url.url, { body: url.body });
          var ret = setInfo({ type: 'url', defaultValue: defaultValue }, info);
          return ret;
      }
      ParamDefinition.Url = Url;
      function File(info) {
          var ret = setInfo({ type: 'file', defaultValue: null }, info);
          if (info === null || info === void 0 ? void 0 : info.accept)
              ret.accept = info.accept;
          return ret;
      }
      ParamDefinition.File = File;
      function FileList(info) {
          var ret = setInfo({ type: 'file-list', defaultValue: null }, info);
          if (info === null || info === void 0 ? void 0 : info.accept)
              ret.accept = info.accept;
          return ret;
      }
      ParamDefinition.FileList = FileList;
      function setRange(p, range) {
          if (!range)
              return p;
          if (typeof range.min !== 'undefined')
              p.min = range.min;
          if (typeof range.max !== 'undefined')
              p.max = range.max;
          if (typeof range.step !== 'undefined')
              p.step = range.step;
          return p;
      }
      function Numeric(defaultValue, range, info) {
          var ret = setInfo(setRange({ type: 'number', defaultValue: defaultValue }, range), info);
          if (info === null || info === void 0 ? void 0 : info.immediateUpdate)
              ret.immediateUpdate = true;
          return ret;
      }
      ParamDefinition.Numeric = Numeric;
      function Interval(defaultValue, range, info) {
          return setInfo(setRange({ type: 'interval', defaultValue: defaultValue }, range), info);
      }
      ParamDefinition.Interval = Interval;
      function LineGraph(defaultValue, info) {
          return setInfo({ type: 'line-graph', defaultValue: defaultValue }, info);
      }
      ParamDefinition.LineGraph = LineGraph;
      function Group(params, info) {
          var ret = setInfo({ type: 'group', defaultValue: (info === null || info === void 0 ? void 0 : info.customDefault) || getDefaultValues(params), params: params }, info);
          if (info === null || info === void 0 ? void 0 : info.presets)
              ret.presets = info.presets;
          if (info === null || info === void 0 ? void 0 : info.isExpanded)
              ret.isExpanded = info.isExpanded;
          if (info === null || info === void 0 ? void 0 : info.isFlat)
              ret.isFlat = info.isFlat;
          if (info === null || info === void 0 ? void 0 : info.pivot)
              ret.pivot = info.pivot;
          return ret;
      }
      ParamDefinition.Group = Group;
      function EmptyGroup(info) {
          return Group({}, info);
      }
      ParamDefinition.EmptyGroup = EmptyGroup;
      function Mapped(defaultKey, names, map, info) {
          var name = checkDefaultKey(defaultKey, names);
          return setInfo({
              type: 'mapped',
              defaultValue: { name: name, params: map(name).defaultValue },
              select: Select(name, names, info),
              map: map
          }, info);
      }
      ParamDefinition.Mapped = Mapped;
      function MappedStatic(defaultKey, map, info) {
          var options = (info === null || info === void 0 ? void 0 : info.options)
              ? info.options
              : Object.keys(map).map(function (k) { return [k, map[k].label || (0, string_1.stringToWords)(k)]; });
          var name = checkDefaultKey(defaultKey, options);
          return setInfo({
              type: 'mapped',
              defaultValue: { name: name, params: map[name].defaultValue },
              select: Select(name, options, info),
              map: function (key) { return map[key]; }
          }, info);
      }
      ParamDefinition.MappedStatic = MappedStatic;
      function ObjectList(element, getLabel, info) {
          return setInfo({ type: 'object-list', element: element, getLabel: getLabel, ctor: _defaultObjectListCtor, defaultValue: (info === null || info === void 0 ? void 0 : info.defaultValue) || [] }, info);
      }
      ParamDefinition.ObjectList = ObjectList;
      function _defaultObjectListCtor() { return getDefaultValues(this.element); }
      function unsetGetValue() {
          throw new Error('getValue not set. Fix runtime.');
      }
      function ValueRef(getOptions, resolveRef, info) {
          var _a;
          return setInfo({ type: 'value-ref', defaultValue: { ref: (_a = info === null || info === void 0 ? void 0 : info.defaultRef) !== null && _a !== void 0 ? _a : '', getValue: unsetGetValue }, getOptions: getOptions, resolveRef: resolveRef }, info);
      }
      ParamDefinition.ValueRef = ValueRef;
      function DataRef(info) {
          var _a;
          return setInfo({ type: 'data-ref', defaultValue: { ref: (_a = info === null || info === void 0 ? void 0 : info.defaultRef) !== null && _a !== void 0 ? _a : '', getValue: unsetGetValue } }, info);
      }
      ParamDefinition.DataRef = DataRef;
      function Converted(fromValue, toValue, converted) {
          return { type: 'converted', defaultValue: toValue(converted.defaultValue), converted: converted, fromValue: fromValue, toValue: toValue };
      }
      ParamDefinition.Converted = Converted;
      function Conditioned(defaultValue, conditionParams, conditionForValue, conditionedValue, info) {
          var options = Object.keys(conditionParams).map(function (k) { return [k, k]; });
          return setInfo({ type: 'conditioned', select: Select(conditionForValue(defaultValue), options, info), defaultValue: defaultValue, conditionParams: conditionParams, conditionForValue: conditionForValue, conditionedValue: conditionedValue }, info);
      }
      ParamDefinition.Conditioned = Conditioned;
      function Script(defaultValue, info) {
          return setInfo({ type: 'script', defaultValue: defaultValue }, info);
      }
      ParamDefinition.Script = Script;
      function For(params) {
          return 0;
      }
      ParamDefinition.For = For;
      function getDefaultValues(params) {
          var d = {};
          for (var _a = 0, _b = Object.keys(params); _a < _b.length; _a++) {
              var k = _b[_a];
              if (params[k].isOptional)
                  continue;
              d[k] = params[k].defaultValue;
          }
          return d;
      }
      ParamDefinition.getDefaultValues = getDefaultValues;
      function _resolveRef(resolve, ref) {
          return function () { return resolve(ref); };
      }
      function resolveRefValue(p, value, getData) {
          if (!value)
              return;
          if (p.type === 'value-ref') {
              var v = value;
              if (!v.ref)
                  v.getValue = function () { throw new Error('Unset ref in ValueRef value.'); };
              else
                  v.getValue = _resolveRef(p.resolveRef, v.ref);
          }
          else if (p.type === 'data-ref') {
              var v = value;
              if (!v.ref)
                  v.getValue = function () { throw new Error('Unset ref in ValueRef value.'); };
              else
                  v.getValue = _resolveRef(getData, v.ref);
          }
          else if (p.type === 'group') {
              resolveRefs(p.params, value, getData);
          }
          else if (p.type === 'mapped') {
              var v = value;
              var param = p.map(v.name);
              resolveRefValue(param, v.params, getData);
          }
          else if (p.type === 'object-list') {
              if (!hasValueRef(p.element))
                  return;
              for (var _a = 0, value_1 = value; _a < value_1.length; _a++) {
                  var e = value_1[_a];
                  resolveRefs(p.element, e, getData);
              }
          }
      }
      function hasParamValueRef(p) {
          if (p.type === 'value-ref' || p.type === 'data-ref') {
              return true;
          }
          else if (p.type === 'group') {
              if (hasValueRef(p.params))
                  return true;
          }
          else if (p.type === 'mapped') {
              for (var _a = 0, _b = p.select.options; _a < _b.length; _a++) {
                  var o = _b[_a][0];
                  if (hasParamValueRef(p.map(o)))
                      return true;
              }
          }
          else if (p.type === 'object-list') {
              return hasValueRef(p.element);
          }
          return false;
      }
      function hasValueRef(params) {
          for (var _a = 0, _b = Object.keys(params); _a < _b.length; _a++) {
              var n = _b[_a];
              if (hasParamValueRef(params[n]))
                  return true;
          }
          return false;
      }
      function resolveRefs(params, values, getData) {
          for (var _a = 0, _b = Object.keys(params); _a < _b.length; _a++) {
              var n = _b[_a];
              resolveRefValue(params[n], values === null || values === void 0 ? void 0 : values[n], getData);
          }
      }
      ParamDefinition.resolveRefs = resolveRefs;
      function setDefaultValues(params, defaultValues) {
          for (var _a = 0, _b = Object.keys(params); _a < _b.length; _a++) {
              var k = _b[_a];
              if (params[k].isOptional)
                  continue;
              params[k].defaultValue = defaultValues[k];
          }
      }
      ParamDefinition.setDefaultValues = setDefaultValues;
      function clone(params) {
          return (0, object_1.deepClone)(params);
      }
      ParamDefinition.clone = clone;
      function validate(params, values) {
          // TODO
          return void 0;
      }
      ParamDefinition.validate = validate;
      function areEqual(params, a, b) {
          if (a === b)
              return true;
          if (typeof a !== 'object' || typeof b !== 'object')
              return false;
          for (var _a = 0, _b = Object.keys(params); _a < _b.length; _a++) {
              var k = _b[_a];
              if (!isParamEqual(params[k], a[k], b[k]))
                  return false;
          }
          return true;
      }
      ParamDefinition.areEqual = areEqual;
      function isParamEqual(p, a, b) {
          if (a === b)
              return true;
          if (p.type === 'group') {
              return areEqual(p.params, a, b);
          }
          else if (p.type === 'mapped') {
              var u = a, v = b;
              if (u.name !== v.name)
                  return false;
              var map = p.map(u.name);
              return isParamEqual(map, u.params, v.params);
          }
          else if (p.type === 'multi-select') {
              var u = a, v = b;
              if (u.length !== v.length)
                  return false;
              if (u.length < 10) {
                  for (var i = 0, _i = u.length; i < _i; i++) {
                      if (u[i] === v[i])
                          continue;
                      if (v.indexOf(u[i]) < 0)
                          return false;
                  }
              }
              else {
                  // TODO: should the value of multiselect be a set?
                  var vSet = new Set(v);
                  for (var i = 0, _i = u.length; i < _i; i++) {
                      if (u[i] === v[i])
                          continue;
                      if (!vSet.has(u[i]))
                          return false;
                  }
              }
              return true;
          }
          else if (p.type === 'interval') {
              return a[0] === b[0] && a[1] === b[1];
          }
          else if (p.type === 'line-graph') {
              var u = a, v = b;
              if (u.length !== v.length)
                  return false;
              for (var i = 0, _i = u.length; i < _i; i++) {
                  if (!linear_algebra_1.Vec2.areEqual(u[i], v[i]))
                      return false;
              }
              return true;
          }
          else if (p.type === 'vec3') {
              return linear_algebra_1.Vec3.equals(a, b);
          }
          else if (p.type === 'mat4') {
              return linear_algebra_1.Mat4.areEqual(a, b, linear_algebra_1.EPSILON);
          }
          else if (p.type === 'script') {
              var u = a, v = b;
              return u.language === v.language && u.expression === v.expression;
          }
          else if (p.type === 'object-list') {
              var u = a, v = b;
              var l = u.length;
              if (l !== v.length)
                  return false;
              for (var i = 0; i < l; i++) {
                  if (!areEqual(p.element, u[i], v[i]))
                      return false;
              }
              return true;
          }
          else if (typeof a === 'object' && typeof b === 'object') {
              return (0, index_1.shallowEqualObjects)(a, b);
          }
          // a === b was checked at the top.
          return false;
      }
      ParamDefinition.isParamEqual = isParamEqual;
      function merge(params, a, b) {
          if (a === undefined)
              return (0, tslib_1.__assign)({}, b);
          if (b === undefined)
              return (0, tslib_1.__assign)({}, a);
          var o = Object.create(null);
          for (var _a = 0, _b = Object.keys(params); _a < _b.length; _a++) {
              var k = _b[_a];
              o[k] = mergeParam(params[k], a[k], b[k]);
          }
          return o;
      }
      ParamDefinition.merge = merge;
      function mergeParam(p, a, b) {
          if (a === undefined)
              return typeof b === 'object' && !Array.isArray(b) ? (0, tslib_1.__assign)({}, b) : b;
          if (b === undefined)
              return typeof a === 'object' && !Array.isArray(a) ? (0, tslib_1.__assign)({}, a) : a;
          if (p.type === 'group') {
              return merge(p.params, a, b);
          }
          else if (p.type === 'mapped') {
              var u = a, v = b;
              if (u.name !== v.name)
                  return (0, tslib_1.__assign)({}, v);
              var map = p.map(v.name);
              return {
                  name: v.name,
                  params: mergeParam(map, u.params, v.params)
              };
          }
          else if (p.type === 'value') {
              return b;
          }
          else if (typeof a === 'object' && typeof b === 'object') {
              if (Array.isArray(b)) {
                  return b;
              }
              return (0, tslib_1.__assign)((0, tslib_1.__assign)({}, a), b);
          }
          else {
              return b;
          }
      }
      ParamDefinition.mergeParam = mergeParam;
      function selectHasOption(p, v) {
          for (var _a = 0, _b = p.options; _a < _b.length; _a++) {
              var o = _b[_a];
              if (o[0] === v)
                  return true;
          }
          return false;
      }
      function normalizeParam(p, value, defaultIfUndefined) {
          if (value === void 0 || value === null) {
              return defaultIfUndefined ? p.defaultValue : void 0;
          }
          // TODO: is this a good idea and will work well?
          // if (typeof p.defaultValue !== typeof value) {
          //     return p.defaultValue;
          // }
          if (p.type === 'value') {
              return value;
          }
          else if (p.type === 'group') {
              var ret = Object.create(null);
              for (var _a = 0, _b = Object.keys(p.params); _a < _b.length; _a++) {
                  var key = _b[_a];
                  var param = p.params[key];
                  if (value[key] === void 0) {
                      if (defaultIfUndefined)
                          ret[key] = param.defaultValue;
                  }
                  else {
                      ret[key] = normalizeParam(param, value[key], defaultIfUndefined);
                  }
              }
              return ret;
          }
          else if (p.type === 'mapped') {
              var v = value;
              if (typeof v.name !== 'string') {
                  return p.defaultValue;
              }
              if (typeof v.params === 'undefined') {
                  return defaultIfUndefined ? p.defaultValue : void 0;
              }
              if (!selectHasOption(p.select, v.name)) {
                  return p.defaultValue;
              }
              var param = p.map(v.name);
              return {
                  name: v.name,
                  params: normalizeParam(param, v.params, defaultIfUndefined)
              };
          }
          else if (p.type === 'select') {
              if (!selectHasOption(p, value))
                  return p.defaultValue;
              return value;
          }
          else if (p.type === 'multi-select') {
              if (!Array.isArray(value))
                  return p.defaultValue;
              var ret = value.filter(function (v) { return selectHasOption(this, v); }, p);
              if (value.length > 0 && ret.length === 0)
                  return p.defaultValue;
              return ret;
          }
          else if (p.type === 'object-list') {
              if (!Array.isArray(value))
                  return p.defaultValue;
              return value.map(function (v) { return normalizeParams(p.element, v, defaultIfUndefined ? 'all' : 'skip'); });
          }
          // TODO: validate/normalize all param types "properly"??
          return value;
      }
      function normalizeParams(p, value, defaultIfUndefined) {
          if (typeof value !== 'object' || value === null) {
              return defaultIfUndefined ? getDefaultValues(p) : value;
          }
          var ret = Object.create(null);
          for (var _a = 0, _b = Object.keys(p); _a < _b.length; _a++) {
              var key = _b[_a];
              var param = p[key];
              if (value[key] === void 0) {
                  if (defaultIfUndefined === 'all')
                      ret[key] = param.defaultValue;
              }
              else {
                  ret[key] = normalizeParam(param, value[key], defaultIfUndefined !== 'skip');
              }
          }
          return ret;
      }
      ParamDefinition.normalizeParams = normalizeParams;
      /**
       * Map an object to a list of [K, string][] to be used as options, stringToWords for key used by default (or identity of null).
       *
       * if options is { [string]: string } and mapping is not provided, use the Value.
       */
      function objectToOptions(options, f) {
          var ret = [];
          for (var _a = 0, _b = Object.keys(options); _a < _b.length; _a++) {
              var k = _b[_a];
              if (!f) {
                  if (typeof options[k] === 'string')
                      ret.push([k, options[k]]);
                  else
                      ret.push([k, f === null ? k : (0, string_1.stringToWords)(k)]);
              }
              else {
                  var o = f(k, options[k]);
                  ret.push(typeof o === 'string' ? [k, o] : [k, o[0], o[1]]);
              }
          }
          return ret;
      }
      ParamDefinition.objectToOptions = objectToOptions;
      /**
       * Map array of options using stringToWords by default (or identity of null).
       */
      function arrayToOptions(xs, f) {
          var ret = [];
          for (var _a = 0, xs_1 = xs; _a < xs_1.length; _a++) {
              var x = xs_1[_a];
              if (!f) {
                  ret.push([x, f === null ? x : (0, string_1.stringToWords)(x)]);
              }
              else {
                  ret.push([x, f(x)]);
              }
          }
          return ret;
      }
      ParamDefinition.arrayToOptions = arrayToOptions;
      function optionLabel(param, value) {
          for (var _a = 0, _b = param.options; _a < _b.length; _a++) {
              var o = _b[_a];
              if (o[0] === value)
                  return o[1];
          }
          return '';
      }
      ParamDefinition.optionLabel = optionLabel;
      function checkDefaultKey(k, options) {
          for (var _a = 0, options_1 = options; _a < options_1.length; _a++) {
              var o = options_1[_a];
              if (o[0] === k)
                  return k;
          }
          return options.length > 0 ? options[0][0] : void 0;
      }
  })(ParamDefinition = exports.ParamDefinition || (exports.ParamDefinition = {}));
  //# sourceMappingURL=param-definition.js.map