export const TreeViewDecorationStyles = {
  CAPTION_HIGHLIGHT_CLASS: 'mana-caption-highlight',
  CAPTION_PREFIX_CLASS: 'mana-caption-prefix',
  CAPTION_SUFFIX_CLASS: 'mana-caption-suffix',
  ICON_WRAPPER_CLASS: 'mana-icon-wrapper',
  DECORATOR_SIZE_CLASS: 'mana-decorator-size',
  DECORATOR_SIDEBAR_SIZE_CLASS: 'mana-decorator-sidebar-size',
  TOP_RIGHT_CLASS: 'mana-top-right',
  BOTTOM_RIGHT_CLASS: 'mana-bottom-right',
  BOTTOM_RIGHT_SIDEBAR_CLASS: 'mana-bottom-right-sidebar',
  BOTTOM_LEFT_CLASS: 'mana-bottom-left',
  TOP_LEFT_CLASS: 'mana-top-left',
};
/**
 * Enumeration for the quadrant to overlay the image on.
 */
export enum IconOverlayPosition {
  /**
   * Overlays the top right quarter of the original image.
   */
  TOP_RIGHT,
  /**
   * Overlays the bottom right of the original image.
   */
  BOTTOM_RIGHT,
  /**
   * Overlays the bottom left segment of the original image.
   */
  BOTTOM_LEFT,
  /**
   * Occupies the top left quarter of the original icon.
   */
  TOP_LEFT,
}

export namespace IconOverlayPosition {
  /**
   * Returns with the CSS class style for the enum.
   */
  export function getStyle(position: IconOverlayPosition, inSideBar?: boolean): string {
    switch (position) {
      case IconOverlayPosition.TOP_RIGHT:
        return TreeViewDecorationStyles.TOP_RIGHT_CLASS;
      case IconOverlayPosition.BOTTOM_RIGHT:
        return inSideBar
          ? TreeViewDecorationStyles.BOTTOM_RIGHT_SIDEBAR_CLASS
          : TreeViewDecorationStyles.BOTTOM_RIGHT_CLASS;
      case IconOverlayPosition.BOTTOM_LEFT:
        return TreeViewDecorationStyles.BOTTOM_LEFT_CLASS;
      case IconOverlayPosition.TOP_LEFT:
        return TreeViewDecorationStyles.TOP_LEFT_CLASS;
      default:
        return '';
    }
  }
}

export namespace CaptionHighlight {
  /**
   * A pair of offset and length that has to be highlighted as a range.
   */
  export type Range = {
    /**
     * Zero based offset of the highlighted region.
     */
    readonly offset: number;
    /**
     * The length of the highlighted region.
     */
    readonly length: number;
  };
  export namespace Range {
    /**
     * `true` if the `arg` is contained in the range. The ranges are closed ranges, hence the check is inclusive.
     */
    export function contains(arg: number, range: Range): boolean {
      return arg >= range.offset && arg <= range.offset + range.length;
    }
  }
  /**
   * The result of a caption splitting based on the highlighting information.
   */
  export type Fragment = {
    /**
     * The text data of the fragment.
     */
    readonly data: string;
    /**
     * Has to be highlighted if defined.
     */
    readonly highlight?: true;
  };
  /**
   * Splits the `caption` argument based on the ranges from the `highlight` argument.
   */
  export function split(caption: string, highlight: CaptionHighlight): Fragment[] {
    const result: Fragment[] = [];
    const ranges = highlight.ranges.slice();
    const containerOf = (index: number) =>
      ranges.findIndex((range) => Range.contains(index, range));
    let data = '';
    for (let i = 0; i < caption.length; i += 1) {
      const containerIndex = containerOf(i);
      if (containerIndex === -1) {
        data += caption[i];
      } else {
        if (data.length > 0) {
          result.push({ data });
        }
        const { length } = ranges.splice(containerIndex, 1).shift()!;
        result.push({ data: caption.substr(i, length), highlight: true });
        data = '';
        i = i + length - 1;
      }
    }
    if (data.length > 0) {
      result.push({ data });
    }
    if (ranges.length !== 0) {
      throw new Error(
        'Error occurred when splitting the caption. There was a mismatch between the caption and the corresponding highlighting ranges.',
      );
    }
    return result;
  }
}
/**
 * The caption highlighting with the highlighted ranges and an optional background color.
 */
export type CaptionHighlight = {
  /**
   * The ranges to highlight in the caption.
   */
  readonly ranges: CaptionHighlight.Range[];
  /**
   * The optional color of the text data that is being highlighted. Falls back to the default `mark` color values defined under a widget segment class.
   */
  readonly color?: TreeViewDecoration.Color;
  /**
   * The optional background color of the text data that is being highlighted.
   */
  readonly backgroundColor?: TreeViewDecoration.Color;
};
/**
 * Namespace for the decoration data and the styling refinements for the decorated widgets.
 */
export namespace TreeViewDecoration {
  /**
   * For the sake of simplicity, we have merged the `font-style`, `font-weight`, and the `text-decoration` together.
   */
  export type FontStyle =
    | 'normal'
    | 'bold'
    | 'italic'
    | 'oblique'
    | 'underline'
    | 'line-through';
  /**
   * A string that could be:
   *
   *  - one of the browser colors, (E.g.: `blue`, `red`, `magenta`),
   *  - the case insensitive hexadecimal color code, (for instance, `#ee82ee`, `#20B2AA`, `#f09` ), or
   *  - either the `rgb()` or the `rgba()` functions.
   *
   * For more details, see: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value.
   *
   * Note, it is highly recommended to use one of the predefined colors of Mana, so the desired color will
   * look nice with both the `light` and the `dark` theme too.
   */
  export type Color = string;
  /**
   * Encapsulates styling information of the font.
   */
  export type FontData = {
    /**
     * Zero to any font style.
     */
    readonly style?: FontStyle | FontStyle[];
    /**
     * The color of the font.
     */
    readonly color?: Color;
  };
  /**
   * Arbitrary information that has to be shown either before or after the caption as a prefix or a suffix.
   */
  export type CaptionAffix = {
    /**
     * The text content of the prefix or the suffix.
     */
    readonly data: string;
    /**
     * Font data for customizing the prefix of the suffix.
     */
    readonly fontData?: FontData;
  };
  export type BaseTailDecoration = {
    /**
     * Optional tooltip for the tail decoration.
     */
    readonly tooltip?: string;
  };
  /**
   * Unlike caption suffixes, tail decorations appears right-aligned after the caption and the caption suffixes (is any).
   */
  export type TailDecoration = {
    /**
     * The text content of the tail decoration.
     */
    readonly data: string;
    /**
     * Font data for customizing the content.
     */
    readonly fontData?: FontData;
  } & BaseTailDecoration;
  export type TailDecorationIcon = {
    /**
     * This should be the name of the Font Awesome icon with out the `fa fa-` prefix, just the name, for instance `paw`.
     * For the existing icons, see here: https://fontawesome.com/v4.7.0/icons/.
     */
    readonly icon: string;
    /**
     * The color of the icon.
     */
    readonly color?: Color;
  } & BaseTailDecoration;
  export type TailDecorationIconClass = {
    /**
     * This should be the entire Font Awesome class array, for instance ['fa', 'fa-paw']
     * For the existing icons, see here: https://fontawesome.com/v4.7.0/icons/.
     */
    readonly iconClass: string[];
    /**
     * The color of the icon.
     */
    readonly color?: Color;
  } & BaseTailDecoration;

  /**
   * A shape that can be optionally rendered behind the overlay icon. Can be used to further refine colors.
   */
  export type IconOverlayBackground = {
    /**
     * Either `circle` or `square`.
     */
    readonly shape: 'circle' | 'square';
    /**
     * The color of the background shape.
     */
    readonly color?: Color;
  };
  /**
   * Has not effect if the widget being decorated has no associated icon.
   */
  export type BaseOverlay = {
    /**
     * The position where the decoration will be placed on the top of the original icon.
     */
    readonly position: IconOverlayPosition;
    /**
     * The color of the overlaying icon. If not specified, then the default icon color will be used.
     */
    readonly color?: Color;
    /**
     * The optional background color of the overlay icon.
     */
    readonly background?: IconOverlayBackground;
  };
  export type IconOverlay = {
    /**
     * This should be the name of the Font Awesome icon with out the `fa fa-` prefix, just the name, for instance `paw`.
     * For the existing icons, see here: https://fontawesome.com/v4.7.0/icons/.
     */
    readonly icon: string;
  } & BaseOverlay;
  export type IconClassOverlay = {
    /**
     * This should be the entire Font Awesome class array, for instance ['fa', 'fa-paw']
     * For the existing icons, see here: https://fontawesome.com/v4.7.0/icons/.
     */
    readonly iconClass: string[];
  } & BaseOverlay;
}
/**
 * Encapsulates styling information that has to be applied on the widget which we decorate.
 */
export type TreeViewDecorationData = {
  /**
   * The higher number has higher priority. If not specified, treated as `0`.
   * When multiple decorators are available for the same item, and decoration data cannot be merged together,
   * then the higher priority item will be applied on the decorated element and the lower priority will be ignored.
   */
  readonly priority?: number;
  /**
   * The font data for the caption.
   */
  readonly fontData?: TreeViewDecoration.FontData;
  /**
   * The background color of the entire row.
   */
  readonly backgroundColor?: TreeViewDecoration.Color;
  /**
   * Optional, leading prefixes right before the caption.
   */
  readonly captionPrefixes?: TreeViewDecoration.CaptionAffix[];
  /**
   * Suffixes that might come after the caption as an additional information.
   */
  readonly captionSuffixes?: TreeViewDecoration.CaptionAffix[];
  /**
   * Optional right-aligned decorations that appear after the widget caption and after the caption suffixes (is any).
   */
  readonly tailDecorations?: (
    | TreeViewDecoration.TailDecoration
    | TreeViewDecoration.TailDecorationIcon
    | TreeViewDecoration.TailDecorationIconClass
  )[];
  /**
   * Custom tooltip for the decorated item. Tooltip will be appended to the original tooltip, if any.
   */
  readonly tooltip?: string;
  /**
   * Sets the color of the icon. Ignored if the decorated item has no icon.
   */
  readonly iconColor?: TreeViewDecoration.Color;
  /**
   * Has not effect if given, but the widget does not have an associated image.
   */
  readonly iconOverlay?:
    | TreeViewDecoration.IconOverlay
    | TreeViewDecoration.IconClassOverlay;
  /**
   * An array of ranges to highlight the caption.
   */
  readonly highlight?: CaptionHighlight;
  /**
   * A count badge for widgets.
   */
  readonly badge?: number;
};
export namespace TreeViewDecorationData {
  /**
   * Compares the decoration data based on the priority. Lowest priorities come first.
   */
  export const comparePriority = (
    left: TreeViewDecorationData,
    right: TreeViewDecorationData,
  ): number => (left.priority || 0) - (right.priority || 0);
}
