/**
 * Copyright (c) 2017 The xterm.js authors. All rights reserved.
 * @license MIT
 */

import { DIM_OPACITY, IGlyphIdentifier } from './Types';
import { CHAR_ATLAS_CELL_SPACING, ICharAtlasConfig } from '../../shared/atlas/Types';
import { generateStaticCharAtlasTexture } from '../../shared/atlas/CharAtlasGenerator';
import BaseCharAtlas from './BaseCharAtlas';

export default class StaticCharAtlas extends BaseCharAtlas {
  private _texture: HTMLCanvasElement | ImageBitmap;

  constructor(private _document: Document, private _config: ICharAtlasConfig) {
    super();
  }

  private _canvasFactory = (width: number, height: number) => {
    const canvas = this._document.createElement('canvas');
    canvas.width = width;
    canvas.height = height;

    // This is useful for debugging
    // document.body.appendChild(canvas);

    return canvas;
  }

  protected _doWarmUp(): void {
    const result = generateStaticCharAtlasTexture(window, this._canvasFactory, this._config);
    if (result instanceof HTMLCanvasElement) {
      this._texture = result;
    } else {
      result.then(texture => {
        this._texture = texture;
      });
    }
  }

  private _isCached(glyph: IGlyphIdentifier, colorIndex: number): boolean {
    const isAscii = glyph.code < 256;
    // A color is basic if it is one of the 4 bit ANSI colors.
    const isBasicColor = glyph.fg < 16;
    const isDefaultColor = glyph.fg >= 256;
    const isDefaultBackground = glyph.bg >= 256;
    return isAscii && (isBasicColor || isDefaultColor) && isDefaultBackground && !glyph.italic;
  }

  public draw(
    ctx: CanvasRenderingContext2D,
    glyph: IGlyphIdentifier,
    x: number,
    y: number
  ): boolean {
    // we're not warmed up yet
    if (this._texture === null || this._texture === undefined) {
      return false;
    }

    let colorIndex = 0;
    if (glyph.fg < 256) {
      colorIndex = 2 + glyph.fg + (glyph.bold ? 16 : 0);
    } else {
      // If default color and bold
      if (glyph.bold) {
        colorIndex = 1;
      }
    }
    if (!this._isCached(glyph, colorIndex)) {
      return false;
    }

    ctx.save();

    // ImageBitmap's draw about twice as fast as from a canvas
    const charAtlasCellWidth = this._config.scaledCharWidth + CHAR_ATLAS_CELL_SPACING;
    const charAtlasCellHeight = this._config.scaledCharHeight + CHAR_ATLAS_CELL_SPACING;

    // Apply alpha to dim the character
    if (glyph.dim) {
      ctx.globalAlpha = DIM_OPACITY;
    }

    ctx.drawImage(
      this._texture,
      glyph.code * charAtlasCellWidth,
      colorIndex * charAtlasCellHeight,
      charAtlasCellWidth,
      this._config.scaledCharHeight,
      x,
      y,
      charAtlasCellWidth,
      this._config.scaledCharHeight
    );

    ctx.restore();

    return true;
  }
}
