import {NumberField, SchemaField, StringField, TypedObjectField} from "@common/data/fields.mjs";
import VFXReferenceField from "./vfx-reference-field.mjs";

/**
 * A subclass of VFXReferenceField which is used to target an entire object.
 * This allows applying deltas to multiple properties of that object.
 * @template {object} [ValueType=any]
 * @extends VFXReferenceField<ValueType>
 *
 * @example
 * ```js
 * const dimensions = new VFXReferenceObjectField(new SchemaField({
 *   width: new NumberField(),
 *   height: new NumberField()
 * }));
 * const unresolvedValue = {reference: "target", deltas: {width: 1, height: -1}};
 * const references = {target: tokenDocument}; // Suppose tokenDocument width=4 and tokenDocument height=4
 * const resolvedObject = dimensions.resolve(unresolvedValue, references); // {width: 5, height: 3}
 * ```
 */
export default class VFXReferenceObjectField extends VFXReferenceField {
  constructor(schema, options, context) {
    super(schema, {
      required: schema.options.required,
      nullable: schema.options.nullable,
      initial: schema.options.initial,
      ...options
    }, context);
  }

  /* ---------------------------------------- */

  /** @override */
  static referenceField = new SchemaField({
    reference: new StringField({ required: true, nullable: false }),
    property: new StringField({ required: false, nullable: true }),
    deltas: new TypedObjectField(new NumberField({nullable: false, required: true}))
  }, { required: true, nullable: false });

  /* ---------------------------------------- */

  /** @override */
  resolve(value, references) {
    if ( !this.constructor.isReference(value) ) return this.valueField.clean(value);
    let target = references[value.reference];
    if ( value.property ) target = foundry.utils.getProperty(target, value.property);
    const result = {};
    for ( const [k, field] of Object.entries(this.valueField.fields) ) {
      let v = target[k];
      if ( Number.isNumeric(value.deltas?.[k]) && Number.isNumeric(v) ) v += value.deltas[k];
      result[k] = field.clean(v);
    }
    return result;
  }
}
