import { Attribute } from '../tokenizer/attribute.js';
import { Position } from '../tokenizer/position.js';
import { Range } from '../tokenizer/range.js';
import { Token } from '../tokenizer/token.js';
import { Inspectable } from './inspectable.js';
import { Property } from './property.js';

export type Source = Attribute | Token | null;

export class Span extends Inspectable {
    #source: Source;
    #from: Position;
    #to: Position;
    #properties: Array<Property>;
    #tagName: keyof HTMLElementTagNameMap;

    private constructor(source: Source, from: Position, to: Position, tagName: keyof HTMLElementTagNameMap, properties: Array<Property>) {
        super();

        this.#source = source;
        this.#from = from;
        this.#to = to;
        this.#tagName = tagName;
        this.#properties = properties;
    }

    public get source(): Source {
        return this.#source;
    }

    public contains(index: number): boolean {
        return this.#from.index <= index && this.#to.index >= index;
    }

    public get properties(): Array<Property> {
        return this.#properties;
    }

    public get tagName(): keyof HTMLElementTagNameMap {
        return this.#tagName;
    }

    public static createFromRange(source: Source, range: Range, ...properties: Array<Property>): Span {
        return new Span(source, range.start, range.end, 'span', properties);
    }

    public static createAnchorFromRange(source: Source, range: Range, ...properties: Array<Property>): Span {
        return new Span(source, range.start, range.end, 'a', properties);
    }

    public static createAt(source: Source, position: Position, ...properties: Array<Property>): Span {
        return new Span(source, position, position, 'span', properties);
    }

    public static createAnchorAt(source: Source, position: Position, ...properties: Array<Property>): Span {
        return new Span(source, position, position, 'a', properties);
    }

    public override inspect(indent: number): string {
        let string = 'Span {\n';

        string += `    from: ${this.#from.inspect(0)}\n`;
        string += `    to: ${this.#to.inspect(0)}\n`;
        string += `    properties: [ ${this.#properties.map(property => property.inspect(0)).join(', ')} ]\n`;

        if (this.#source !== null) string += `    source: ${this.#source.inspect(0)}\n`;

        string += '}';

        return string;
    }
}
