import { AttachmentPointType, isAttachmentPointType } from "./specs";

type SvgSymbolMetadataBooleans = {
  /**
   * If true, this indicates that the symbol should always have
   * a symbol nested within its nesting area(s).
   */
  always_nest?: boolean;

  /**
   * If true, this indicates that the symbol should always
   * be nested inside another symbol's nesting area.
   */
  always_be_nested?: boolean;

  /**
   * If true, this indicates that the symbol should never
   * be nested inside another symbol's nesting area.
   */
  never_be_nested?: boolean;

  /**
   * If true, this indicates that any symbols nested on this
   * symbol’s nesting area should have their colors inverted.
   */
  invert_nested?: boolean;

  /**
   * In the mandala, the normal rotation direction for animation is counterclockwise.
   * This changes the rotation direction to clockwise.
   */
  rotate_clockwise?: boolean;

  /**
   * If true, this indicates that we should never horizontally flip the
   * orientation of a symbol when attaching it.  Otherwise, we will flip
   * the symbol horizontally if it's facing left, and flip it horizontally
   * again if it's facing down.
   */
  never_flip_attachments?: boolean;
};

const METADATA_BOOLEANS: Set<keyof SvgSymbolMetadataBooleans> = new Set([
  "always_nest",
  "always_be_nested",
  "never_be_nested",
  "invert_nested",
  "rotate_clockwise",
  "never_flip_attachments",
]);

function isSvgSymbolMetadataBoolean(
  key: string
): key is keyof SvgSymbolMetadataBooleans {
  return METADATA_BOOLEANS.has(key as any);
}

export type SvgSymbolMetadata = SvgSymbolMetadataBooleans & {
  /**
   * If defined, this indicates the kinds of attachment points
   * that this symbol can attach to.  If not defined, it will
   * be able to attach to any symbol.
   */
  attach_to?: AttachmentPointType[];

  /**
   * Setting this to a positive integer (whole number) will multiply the
   * likelihood that this symbol will be chosen from a random selection of symbols
   * by the given amount when this symbol is used in a creature. For example,
   * setting it to 2 will make it twice as likely to be chosen, setting it to 5
   * will make it five times more likely, and so on.
   */
  creature_frequency_multiplier?: number;
};

export function validateSvgSymbolMetadata(obj: any): {
  metadata: SvgSymbolMetadata;
  unknownProperties: string[];
} {
  const metadata: SvgSymbolMetadata = {};
  const unknownProperties: string[] = [];
  for (let key in obj) {
    const value: unknown = obj[key];
    if (isSvgSymbolMetadataBoolean(key)) {
      if (typeof value !== "boolean") {
        throw new Error(
          `Expected "${key}" to be a boolean, but it is a ${typeof value}!`
        );
      }
      metadata[key] = value;
    } else if (key === "attach_to") {
      metadata.attach_to = validateAttachTo(obj[key]);
    } else if (key === "creature_frequency_multiplier") {
      metadata.creature_frequency_multiplier = validateFrequencyMultiplier(
        obj[key]
      );
    } else {
      unknownProperties.push(key);
    }
  }
  return { metadata, unknownProperties };
}

const MIN_FREQUENCY_MULTIPLIER = 1;

export function validateFrequencyMultiplier(
  value: unknown
): number | undefined {
  if (typeof value === "number") {
    if (value < MIN_FREQUENCY_MULTIPLIER) {
      console.log(
        `Frequency multiplier is less than minimum of ${MIN_FREQUENCY_MULTIPLIER}.`
      );
      return MIN_FREQUENCY_MULTIPLIER;
    }
    return Math.floor(value);
  }
  console.log(`Frequency multiplier "${value}" is not a number.`);
  return undefined;
}

export function validateAttachTo(value: unknown): AttachmentPointType[] {
  if (!Array.isArray(value)) {
    throw new Error(
      `Expected "attach_to" to be an array, but it is a ${typeof value}!`
    );
  }

  const result: AttachmentPointType[] = [];

  for (let item of value) {
    if (isAttachmentPointType(item)) {
      result.push(item);
    } else {
      console.log(
        `Item '${item}' in "attach_to" is not a valid attachment point.`
      );
    }
  }

  return result;
}
