text: import {ENV, parseFont} from '@mesh.js/core';
  import {createTexture, deleteTexture} from '../utils/texture';
  import Block from './block';
  import Attr from '../attribute/label';
  import ownerDocument from '../document';
  
  const _textImage = Symbol('textImage');
  const _textImageTask = Symbol('textImageTask');
  const _textureContext = Symbol('textureContext');
  
  const _updateTextureRect = Symbol('updateTextureRect');
  const _textCanvas = Symbol('textCanvas');
  
  export default class Label extends Block {
    static Attr = Attr;
  
    constructor(attrs = {}) {
      if(typeof attrs === 'string') attrs = {text: attrs};
      super(attrs);
    }
  
    /* override */
    get contentSize() {
      let [w, h] = super.contentSize;
      const {width, height} = this.attributes;
      if(width == null || height == null) {
        const img = this[_textImage];
        if(img) {
          if(width == null) w = img.rect[2];
          if(height == null) h = img.rect[3];
        }
      }
      return [w, h];
    }
  
    get text() {
      return this.attributes.text;
    }
  
    set text(value) {
      this.attributes.text = value;
    }
  
    get textContent() {
      return this.attributes.text;
    }
  
    set textContent(value) {
      this.attributes.text = value;
    }
  
    get textImage() {
      return this[_textImage] || {};
    }
  
    get textImageReady() {
      return this[_textImageTask] || Promise.resolve();
    }
  
    /* override */
    draw(meshes) {
      super.draw(meshes);
      const mesh = this.mesh;
      if(mesh) {
        const textImage = this[_textImage];
        if(textImage) {
          let texture = mesh.texture;
  
          if(!texture
            || this[_textureContext] && this[_textureContext] !== this.renderer
            || textImage.needsUpdate) {
            textImage.needsUpdate = false;
            deleteTexture(textImage.image, this.renderer);
            texture = createTexture(textImage.image, this.renderer);
            this[_updateTextureRect] = true;
          } else {
            texture = mesh.uniforms.u_texSampler;
          }
  
          if(this[_updateTextureRect]) {
            const [width, height] = textImage.rect.slice(2);
            const [w, h] = this.contentSize;
            const textAlign = this.attributes.textAlign;
            const verticalAlign = this.attributes.verticalAlign;
  
            let x = 0;
            if(textAlign === 'center') {
              x = (w - width) / 2;
            } else if(textAlign === 'right' || textAlign === 'end') {
              x = w - width;
            }
  
            const fontHeight = this.attributes.fontSize;
            const lineHeight = this.attributes.lineHeight;
  
            let y = 0; // middle
            if(verticalAlign === 'top') {
              y = (fontHeight - lineHeight) / 2;
            } else if(verticalAlign === 'bottom') {
              y = (lineHeight - fontHeight) / 2;
            }
  
            const {paddingLeft, paddingTop} = this.attributes;
            const {borderWidth} = this.attributes;
  
            x += paddingLeft + borderWidth;
            y += paddingTop + borderWidth;
  
            const {anchorX, anchorY} = this.attributes;
  
            x -= this.clientSize[0] * anchorX;
            y -= this.clientSize[1] * anchorY;
  
            mesh.setTexture(texture, {
              rect: [x, y, width, height],
            });
            this[_updateTextureRect] = false;
            this[_textureContext] = this.renderer;
          }
        }
      }
      return meshes;
    }
  
    /* override */
    onPropertyChange(key, newValue, oldValue) {
      if(key === 'text' || key === 'fontSize' || key === 'fontFamily'
        || key === 'fontStyle' || key === 'fontVariant' || key === 'fontWeight'
        || key === 'fontStretch' || key === 'lineHeight'
        || key === 'strokeColor' || key === 'fillColor'
        || key === 'strokeWidth') {
        this.updateText();
      } else {
        if(key === 'textAlign' || key === 'verticalAlign') {
          this[_updateTextureRect] = true;
        }
        super.onPropertyChange(key, newValue, oldValue);
      }
    }
  
    /* override */
    updateContours() {
      super.updateContours();
      this[_updateTextureRect] = true;
    }
  
    updateText() {
      if(!this[_textImageTask]) {
        this[_textImageTask] = Promise.resolve().then(() => {
          this[_textImageTask] = null;
          const {text, font, fillColor, strokeColor, strokeWidth} = this.attributes;
          const ratio = this.layer ? this.layer.displayRatio : 1;
          this[_textCanvas] = this[_textCanvas] || ENV.createCanvas(1, 1);
          this[_textImage] = ENV.createText(text, {
            font, fillColor, strokeColor, strokeWidth, parseFont, ratio, textCanvas: this[_textCanvas]});
          this[_textImage].needsUpdate = true;
          this.updateContours();
          this.forceUpdate();
          return this[_textImage];
        });
      }
    }
  }
  
  ownerDocument.registerNode(Label, 'label');