"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const async = require("../../../../base/common/async.js");
const color$1 = require("../../../../base/common/color.js");
const errors = require("../../../../base/common/errors.js");
const lifecycle = require("../../../../base/common/lifecycle.js");
const stopwatch = require("../../../../base/common/stopwatch.js");
const strings = require("../../../../base/common/strings.js");
const editorDom = require("../../../browser/editorDom.js");
const editorExtensions = require("../../../browser/editorExtensions.js");
const range = require("../../../common/core/range.js");
const textModel = require("../../../common/model/textModel.js");
const languageFeatureDebounce = require("../../../common/services/languageFeatureDebounce.js");
const languageFeatures = require("../../../common/services/languageFeatures.js");
const color = require("./color.js");
const configuration = require("../../../../platform/configuration/common/configuration.js");
var __decorate = globalThis && globalThis.__decorate || function(decorators, target, key, desc) {
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
    r = Reflect.decorate(decorators, target, key, desc);
  else
    for (var i = decorators.length - 1; i >= 0; i--)
      if (d = decorators[i])
        r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
  return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __param = globalThis && globalThis.__param || function(paramIndex, decorator) {
  return function(target, key) {
    decorator(target, key, paramIndex);
  };
};
var __awaiter = globalThis && globalThis.__awaiter || function(thisArg, _arguments, P, generator) {
  function adopt(value) {
    return value instanceof P ? value : new P(function(resolve) {
      resolve(value);
    });
  }
  return new (P || (P = Promise))(function(resolve, reject) {
    function fulfilled(value) {
      try {
        step(generator.next(value));
      } catch (e) {
        reject(e);
      }
    }
    function rejected(value) {
      try {
        step(generator["throw"](value));
      } catch (e) {
        reject(e);
      }
    }
    function step(result) {
      result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
    }
    step((generator = generator.apply(thisArg, _arguments || [])).next());
  });
};
const ColorDecorationInjectedTextMarker = /* @__PURE__ */ Object.create({});
const MAX_DECORATORS = 500;
exports.ColorDetector = class ColorDetector extends lifecycle.Disposable {
  constructor(_editor, _configurationService, _languageFeaturesService, languageFeatureDebounceService) {
    super();
    this._editor = _editor;
    this._configurationService = _configurationService;
    this._languageFeaturesService = _languageFeaturesService;
    this._localToDispose = this._register(new lifecycle.DisposableStore());
    this._decorationsIds = [];
    this._colorDatas = /* @__PURE__ */ new Map();
    this._colorDecoratorIds = /* @__PURE__ */ new Set();
    this._ruleFactory = new editorDom.DynamicCssRules(this._editor);
    this._colorDecorationClassRefs = this._register(new lifecycle.DisposableStore());
    this._debounceInformation = languageFeatureDebounceService.for(_languageFeaturesService.colorProvider, "Document Colors", { min: ColorDetector.RECOMPUTE_TIME });
    this._register(_editor.onDidChangeModel(() => {
      this._isEnabled = this.isEnabled();
      this.onModelChanged();
    }));
    this._register(_editor.onDidChangeModelLanguage(() => this.onModelChanged()));
    this._register(_languageFeaturesService.colorProvider.onDidChange(() => this.onModelChanged()));
    this._register(_editor.onDidChangeConfiguration(() => {
      let prevIsEnabled = this._isEnabled;
      this._isEnabled = this.isEnabled();
      if (prevIsEnabled !== this._isEnabled) {
        if (this._isEnabled) {
          this.onModelChanged();
        } else {
          this.removeAllDecorations();
        }
      }
    }));
    this._timeoutTimer = null;
    this._computePromise = null;
    this._isEnabled = this.isEnabled();
    this.onModelChanged();
  }
  isEnabled() {
    const model = this._editor.getModel();
    if (!model) {
      return false;
    }
    const languageId = model.getLanguageId();
    const deprecatedConfig = this._configurationService.getValue(languageId);
    if (deprecatedConfig && typeof deprecatedConfig === "object") {
      const colorDecorators = deprecatedConfig["colorDecorators"];
      if (colorDecorators && colorDecorators["enable"] !== void 0 && !colorDecorators["enable"]) {
        return colorDecorators["enable"];
      }
    }
    return this._editor.getOption(
      17
      /* colorDecorators */
    );
  }
  static get(editor) {
    return editor.getContribution(this.ID);
  }
  dispose() {
    this.stop();
    this.removeAllDecorations();
    super.dispose();
  }
  onModelChanged() {
    this.stop();
    if (!this._isEnabled) {
      return;
    }
    const model = this._editor.getModel();
    if (!model || !this._languageFeaturesService.colorProvider.has(model)) {
      return;
    }
    this._localToDispose.add(this._editor.onDidChangeModelContent(() => {
      if (!this._timeoutTimer) {
        this._timeoutTimer = new async.TimeoutTimer();
        this._timeoutTimer.cancelAndSet(() => {
          this._timeoutTimer = null;
          this.beginCompute();
        }, this._debounceInformation.get(model));
      }
    }));
    this.beginCompute();
  }
  beginCompute() {
    this._computePromise = async.createCancelablePromise((token) => __awaiter(this, void 0, void 0, function* () {
      const model = this._editor.getModel();
      if (!model) {
        return Promise.resolve([]);
      }
      const sw = new stopwatch.StopWatch(false);
      const colors = yield color.getColors(this._languageFeaturesService.colorProvider, model, token);
      this._debounceInformation.update(model, sw.elapsed());
      return colors;
    }));
    this._computePromise.then((colorInfos) => {
      this.updateDecorations(colorInfos);
      this.updateColorDecorators(colorInfos);
      this._computePromise = null;
    }, errors.onUnexpectedError);
  }
  stop() {
    if (this._timeoutTimer) {
      this._timeoutTimer.cancel();
      this._timeoutTimer = null;
    }
    if (this._computePromise) {
      this._computePromise.cancel();
      this._computePromise = null;
    }
    this._localToDispose.clear();
  }
  updateDecorations(colorDatas) {
    const decorations = colorDatas.map((c) => ({
      range: {
        startLineNumber: c.colorInfo.range.startLineNumber,
        startColumn: c.colorInfo.range.startColumn,
        endLineNumber: c.colorInfo.range.endLineNumber,
        endColumn: c.colorInfo.range.endColumn
      },
      options: textModel.ModelDecorationOptions.EMPTY
    }));
    this._decorationsIds = this._editor.deltaDecorations(this._decorationsIds, decorations);
    this._colorDatas = /* @__PURE__ */ new Map();
    this._decorationsIds.forEach((id, i) => this._colorDatas.set(id, colorDatas[i]));
  }
  updateColorDecorators(colorData) {
    this._colorDecorationClassRefs.clear();
    let decorations = [];
    for (let i = 0; i < colorData.length && decorations.length < MAX_DECORATORS; i++) {
      const { red, green, blue, alpha } = colorData[i].colorInfo.color;
      const rgba = new color$1.RGBA(Math.round(red * 255), Math.round(green * 255), Math.round(blue * 255), alpha);
      let color2 = `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${rgba.a})`;
      const ref = this._colorDecorationClassRefs.add(this._ruleFactory.createClassNameRef({
        backgroundColor: color2
      }));
      decorations.push({
        range: {
          startLineNumber: colorData[i].colorInfo.range.startLineNumber,
          startColumn: colorData[i].colorInfo.range.startColumn,
          endLineNumber: colorData[i].colorInfo.range.endLineNumber,
          endColumn: colorData[i].colorInfo.range.endColumn
        },
        options: {
          description: "colorDetector",
          before: {
            content: strings.noBreakWhitespace,
            inlineClassName: `${ref.className} colorpicker-color-decoration`,
            inlineClassNameAffectsLetterSpacing: true,
            attachedData: ColorDecorationInjectedTextMarker
          }
        }
      });
    }
    this._colorDecoratorIds = new Set(this._editor.deltaDecorations([...this._colorDecoratorIds], decorations));
  }
  removeAllDecorations() {
    this._decorationsIds = this._editor.deltaDecorations(this._decorationsIds, []);
    this._colorDecoratorIds = new Set(this._editor.deltaDecorations([...this._colorDecoratorIds], []));
    this._colorDecorationClassRefs.clear();
  }
  getColorData(position) {
    const model = this._editor.getModel();
    if (!model) {
      return null;
    }
    const decorations = model.getDecorationsInRange(range.Range.fromPositions(position, position)).filter((d) => this._colorDatas.has(d.id));
    if (decorations.length === 0) {
      return null;
    }
    return this._colorDatas.get(decorations[0].id);
  }
  isColorDecorationId(decorationId) {
    return this._colorDecoratorIds.has(decorationId);
  }
};
exports.ColorDetector.ID = "editor.contrib.colorDetector";
exports.ColorDetector.RECOMPUTE_TIME = 1e3;
exports.ColorDetector = __decorate([
  __param(1, configuration.IConfigurationService),
  __param(2, languageFeatures.ILanguageFeaturesService),
  __param(3, languageFeatureDebounce.ILanguageFeatureDebounceService)
], exports.ColorDetector);
editorExtensions.registerEditorContribution(exports.ColorDetector.ID, exports.ColorDetector);
exports.ColorDecorationInjectedTextMarker = ColorDecorationInjectedTextMarker;
