import Document from "../abstract/document.mjs";
import {mergeObject} from "../utils/helpers.mjs";
import * as CONST from "../constants.mjs";
import * as fields from "../data/fields.mjs";
import {TextureData} from "../data/data.mjs";

/**
 * @import {TileData} from "./_types.mjs";
 */

/**
 * The Tile Document.
 * Defines the DataSchema and common behaviors for a Tile which are shared between both client and server.
 * @extends {Document<TileData>}
 * @mixes TileData
 * @category Documents
 */
export default class BaseTile extends Document {

  /* -------------------------------------------- */
  /*  Model Configuration                         */
  /* -------------------------------------------- */

  /** @inheritdoc */
  static metadata = Object.freeze(mergeObject(super.metadata, {
    name: "Tile",
    collection: "tiles",
    label: "DOCUMENT.Tile",
    labelPlural: "DOCUMENT.Tiles",
    schemaVersion: "14.355"
  }, {inplace: false}));

  /** @inheritdoc */
  static defineSchema() {
    const occlusionModes = Object.entries(CONST.OCCLUSION_MODES).reduce((modes, [mode, flag]) => {
      if ( flag !== CONST.OCCLUSION_MODES.NONE ) modes[flag] = `TILE.OcclusionMode${mode.titleCase()}`;
      return modes;
    }, {});
    return {
      _id: new fields.DocumentIdField(),
      name: new fields.StringField({textSearch: true}),
      texture: new TextureData({}, {categories: ["TEXTURE"], initial: {anchorX: 0.5, anchorY: 0.5, alphaThreshold: 0.75}}),
      width: new fields.NumberField({required: true, min: 0, integer: true, nullable: false}),
      height: new fields.NumberField({required: true, min: 0, integer: true, nullable: false}),
      x: new fields.NumberField({required: true, integer: true, nullable: false, initial: 0}),
      y: new fields.NumberField({required: true, integer: true, nullable: false, initial: 0}),
      elevation: new fields.NumberField({required: true, nullable: false, initial: 0}),
      levels: new fields.SceneLevelsSetField(),
      sort: new fields.NumberField({required: true, integer: true, nullable: false, initial: 0}),
      rotation: new fields.AngleField(),
      alpha: new fields.AlphaField(),
      hidden: new fields.BooleanField(),
      locked: new fields.BooleanField(),
      restrictions: new fields.SchemaField({
        light: new fields.BooleanField(),
        weather: new fields.BooleanField()
      }),
      occlusion: new fields.SchemaField({
        modes: new fields.SetField(new fields.NumberField({choices: occlusionModes,
          validationError: "must be a value in CONST.OCCLUSION_MODES"})),
        alpha: new fields.AlphaField({initial: 0})
      }),
      video: new fields.SchemaField({
        loop: new fields.BooleanField({initial: true}),
        autoplay: new fields.BooleanField({initial: true}),
        volume: new fields.AlphaField({initial: 0, step: 0.01})
      }),
      flags: new fields.DocumentFlagsField()
    };
  }

  /** @override */
  static LOCALIZATION_PREFIXES = ["DOCUMENT", "TILE"];

  /* -------------------------------------------- */
  /*  Document Methods                            */
  /* -------------------------------------------- */

  /** @override */
  getUserLevel(user) {
    user ||= game.user;
    if ( this.pack ) return this.compendium.getUserLevel(user);
    if ( user.isGM ) return CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER;
    return CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE;
  }

  /* -------------------------------------------- */
  /*  Deprecations and Compatibility              */
  /* -------------------------------------------- */

  /** @inheritDoc */
  static migrateData(data, options) {
    /** @deprecated since v14 */
    this._addDataFieldMigration(data, "occlusion.mode", "occlusion.modes",
      data => data.occlusion.mode > 0 ? [data.occlusion.mode] : []);
    return super.migrateData(data, options);
  }

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

  /** @inheritDoc */
  static shimData(data, options) {
    /** @deprecated since v14 */
    this._addDataFieldShim(data, "occlusion.mode", "occlusion.modes", {since: 14, until: 16, once: true,
      value: data.occlusion?.modes?.at(0) ?? CONST.OCCLUSION_MODES.NONE});
    return super.shimData(data);
  }
}
