import{Evented as e}from"@arcgis/core/core/Evented.js";import t from"safe-stable-stringify";import{filter as r,isPlainObject as i,map as s,objectKeys as o}from"../utilities/object.js";const n=new WeakSet;export class SerializableBase extends e{_assignPropertiesPromises=[];constructor(e){if(super(),!n.has(this.constructor))throw new Error(`Class "${this.constructor.name}" must be marked as "@serializable" since it extends ${SerializableBase.name} (make sure it is the _last_ decorator on the class).`)}toJSON(e="all"){return filterJSONProperties(this._getSerializableProperties(),this,e)}assignProperties(e){const t=a(this._getSerializableProperties(),this,e,!0);this._assignPropertiesPromises.push(t),(async()=>{try{await t}finally{this._assignPropertiesPromises.splice(this._assignPropertiesPromises.indexOf(t),1)}})()}async assignPropertiesAsync(e){await this.finishAssignProperties();const t=assignPropertiesAsync(this._getSerializableProperties(),this,e);this._assignPropertiesPromises.push(t);try{await t}finally{this._assignPropertiesPromises.splice(this._assignPropertiesPromises.indexOf(t),1)}}async finishAssignProperties(){await Promise.all(this._assignPropertiesPromises)}getSerializableProperties(e="all"){return getSerializableProperties(this._getSerializableProperties(),e)}getDefaults(){return getDefaults(this._getSerializableProperties())}getDefault(e){return this.getDefaults()[e]}_getSerializableProperties(){return{}}_toPropertyDef(e){return toPropertyDef(e)}}export function serializable(e){if(!isSerializable(e.prototype))throw new Error(`Class "${e.name}" is marked as @serializable but does not implement 'Serializable'.`);class t extends e{constructor(e,...r){super(e,...r),this.constructor===t&&this.assignProperties({...this.getDefaults(),...e})}}return n.add(t),t}export function shouldExcludeDefaults(e){return"initial"===e||"project"===e}export function filterJSONProperties(e,r,i){let s=i;"initial"!==s&&"initial-with-defaults"!==s&&"preferences"!==s&&"project"!==s&&(s="all");const n={},a=getSerializableProperties(e,s);for(const e of o(a)){const i=a[e],o=l(e,i,r,s);if(shouldExcludeDefaults(s)){let r=!1;r=i.hasOwnProperty("default")?t(o)===t(i.default):i.isDefault?i.isDefault(o,s):isDefault(o),r||(n[e]=o)}else n[e]=o}if(!shouldExcludeDefaults(s)&&"preferences"!==s||!isDefault(n))return n}export function assignProperties(e,t,r){r&&a(e,t,r,!0)}export function assignPropertiesAsync(e,t,r){return a(e,t,r)}function a(e,t,r,s=!1){if(!r)return Promise.resolve();const o=[];return Object.keys(e).forEach((n=>{const a=e[n];if(r.hasOwnProperty(n)&&(Array.isArray(e[n])||!1!==a.deserialize))try{const e=r[n]??void 0;if(a?.deserialize){const t=a.deserialize(e);t&&o.push(t)}else"object"!=typeof t[n]||!t[n]||!i(e)&&void 0!==e||s||"function"!=typeof t[n].assignPropertiesAsync?"object"==typeof t[n]&&t[n]&&(i(e)||void 0===e)&&"function"==typeof t[n].assignProperties?t[n].assignProperties(e):t[n]=e:o.push(t[n].assignPropertiesAsync(e))}catch(e){if(s)throw e;o.push(Promise.reject(e))}})),Promise.all(o).then()}export function getDefaults(e){const t={},r=o(e).filter((t=>"object"==typeof e[t]));for(const i of r){const r=e[i];r.hasOwnProperty("default")&&(t[i]=r.default)}return t}export function getDefault(e,t){return getDefaults(e)[t]}export function isDefault(e){if(null==e)return!0;if(e.constructor===Object){if(!Object.keys(e).length)return!0}return!(!Array.isArray(e)||e.length)}function l(e,t,r,i){const s=t?.serialize?t.serialize(i):r[e];if(s&&"function"==typeof s.toItemUri)return s.toItemUri();if(s&&"function"==typeof s.toJSON)return s.toJSON(i);if(Array.isArray(s))return s.map(((e,t)=>l(t,void 0,s,i)));if(s&&"function"==typeof s.toArray){const e=s.toArray();return e.map(((t,r)=>l(r,void 0,e,i)))}if(s&&"object"==typeof s){const e={};for(const t of Object.keys(s))e[t]=l(t,void 0,s,i);return e}return t?.serialize?s:r[e]}export function toPropertyDef(e){const t={serializeModes:[]};return"string"==typeof e?{...t,serializeModes:[e]}:Array.isArray(e)?{...t,serializeModes:e}:{...t,...e}}export function isSerializable(e){return["assignProperties","getDefaults","getSerializableProperties","toJSON"].every((t=>"function"==typeof e[t]))}export function getSerializableProperties(e,t="all"){const i=s(e,(e=>toPropertyDef(e))),o=r(i,(e=>function(e,t){if(!1===t.serialize)return!1;const r="initial-with-defaults"===e?"initial":e;return"all"===e||t.serializeModes?.includes(r)}(t,e)));return o}