// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import type * as Protocol from '../../generated/protocol.js';
import type * as TextUtils from '../../models/text_utils/text_utils.js';

import {CSSLocation, type CSSModel, type Edit} from './CSSModel.js';
import type {CSSStyleSheetHeader} from './CSSStyleSheetHeader.js';

type CSSQueryPayload =
    Protocol.CSS.CSSMedia|Protocol.CSS.CSSContainerQuery|Protocol.CSS.CSSSupports|Protocol.CSS.CSSScope;

export abstract class CSSQuery {
  text = '';
  range?: TextUtils.TextRange.TextRange|null;
  styleSheetId?: Protocol.DOM.StyleSheetId;
  protected cssModel: CSSModel;

  constructor(cssModel: CSSModel) {
    this.cssModel = cssModel;
  }

  protected abstract reinitialize(payload: CSSQueryPayload): void;

  abstract active(): boolean;

  rebase(edit: Edit): void {
    if (this.styleSheetId !== edit.styleSheetId || !this.range) {
      return;
    }
    if (edit.oldRange.equal(this.range)) {
      this.reinitialize(edit.payload as CSSQueryPayload);
    } else {
      this.range = this.range.rebaseAfterTextEdit(edit.oldRange, edit.newRange);
    }
  }

  equal(other: CSSQuery): boolean {
    if (!this.styleSheetId || !this.range || !other.range) {
      return false;
    }
    return this.styleSheetId === other.styleSheetId && this.range.equal(other.range);
  }

  lineNumberInSource(): number|undefined {
    if (!this.range) {
      return undefined;
    }
    return this.header()?.lineNumberInSource(this.range.startLine);
  }

  columnNumberInSource(): number|undefined {
    if (!this.range) {
      return undefined;
    }
    return this.header()?.columnNumberInSource(this.range.startLine, this.range.startColumn);
  }

  header(): CSSStyleSheetHeader|null {
    return this.styleSheetId ? this.cssModel.styleSheetHeaderForId(this.styleSheetId) : null;
  }

  rawLocation(): CSSLocation|null {
    const header = this.header();
    if (!header || this.lineNumberInSource() === undefined) {
      return null;
    }
    const lineNumber = Number(this.lineNumberInSource());
    return new CSSLocation(header, lineNumber, this.columnNumberInSource());
  }
}
