/*
 * Copyright (C) 2016-2021  Yomichan Authors
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

class TextSourceElement {
    constructor(element, fullContent=null, startOffset=0, endOffset=0) {
        this._element = element;
        this._fullContent = (typeof fullContent === 'string' ? fullContent : TextSourceElement.getElementContent(element));
        this._startOffset = startOffset;
        this._endOffset = endOffset;
        this._content = this._fullContent.substring(this._startOffset, this._endOffset);
    }

    get element() {
        return this._element;
    }

    get fullContent() {
        return this._fullContent;
    }

    get startOffset() {
        return this._startOffset;
    }

    get endOffset() {
        return this._endOffset;
    }

    get isConnected() {
        return this._element.isConnected;
    }

    clone() {
        return new TextSourceElement(this._element, this._fullContent, this._startOffset, this._endOffset);
    }

    cleanup() {
        // NOP
    }

    text() {
        return this._content;
    }

    setEndOffset(length, fromEnd=false) {
        if (fromEnd) {
            const delta = Math.min(this._fullContent.length - this._endOffset, length);
            this._endOffset += delta;
            this._content = this._fullContent.substring(this._startOffset, this._endOffset);
            return delta;
        } else {
            const delta = Math.min(this._fullContent.length - this._startOffset, length);
            this._endOffset = this._startOffset + delta;
            this._content = this._fullContent.substring(this._startOffset, this._endOffset);
            return delta;
        }
    }

    setStartOffset(length) {
        const delta = Math.min(this._startOffset, length);
        this._startOffset -= delta;
        this._content = this._fullContent.substring(this._startOffset, this._endOffset);
        return delta;
    }

    collapse(toStart) {
        if (toStart) {
            this._endOffset = this._startOffset;
        } else {
            this._startOffset = this._endOffset;
        }
        this._content = '';
    }

    getRect() {
        return this._element.getBoundingClientRect();
    }

    getWritingMode() {
        return 'horizontal-tb';
    }

    select() {
        // NOP
    }

    deselect() {
        // NOP
    }

    hasSameStart(other) {
        return (
            typeof other === 'object' &&
            other !== null &&
            other instanceof TextSourceElement &&
            this._element === other.element &&
            this._fullContent === other.fullContent &&
            this._startOffset === other.startOffset
        );
    }

    getNodesInRange() {
        return [this._element];
    }

    static getElementContent(element) {
        let content;
        switch (element.nodeName.toUpperCase()) {
            case 'BUTTON':
                content = element.textContent;
                break;
            case 'IMG':
                content = element.getAttribute('alt') || '';
                break;
            case 'SELECT':
                {
                    const {selectedIndex, options} = element;
                    const option = (selectedIndex >= 0 && selectedIndex < options.length ? options[selectedIndex] : null);
                    content = (option !== null ? option.textContent : '');
                }
                break;
            default:
                content = `${element.value}`;
                break;
        }

        // Remove zero-width space and zero-width non-joiner
        content = content.replace(/[\u200b\u200c]/g, '');

        return content;
    }
}