<commit_before>import { formatNumber, formatPercentage } from 'common/format';
import SPELLS from 'common/SPELLS';
import { SpellIcon } from 'interface';
import { TooltipElement } from 'interface';
import Analyzer, { SELECTED_PLAYER } from 'parser/core/Analyzer';
import Events, { CastEvent, HealEvent } from 'parser/core/Events';
import { Options } from 'parser/core/Module';
import StatisticBox from 'parser/ui/StatisticBox';

import isAtonement from '../core/isAtonement';
import Atonement from './Atonement';

const EVANGELISM_DURATION = 6;

class Evangelism extends Analyzer {
  static dependencies = {
    atonementModule: Atonement,
  };
  _previousEvangelismCast: CastEvent | null = null;
  protected atonementModule!: Atonement;

  constructor(options: Options) {
    super(options);
    this.active = this.selectedCombatant.hasTalent(SPELLS.EVANGELISM_TALENT.id);
    this.addEventListener(
      Events.cast.by(SELECTED_PLAYER).spell(SPELLS.EVANGELISM_TALENT),
      this.onCast,
    );
    this.addEventListener(Events.heal.by(SELECTED_PLAYER), this.onHeal);
  }

  _evangelismStatistics: {
    [timestamp: number]: {
      count: number;
      atonementSeconds: number;
      healing: number;
    };
  } = {};

  get evangelismStatistics() {
    return Object.keys(this._evangelismStatistics)
      .map(Number)
      .map((key: number) => this._evangelismStatistics[key]);
  }

  onCast(event: CastEvent) {
    this._previousEvangelismCast = event;
    const atonedPlayers = this.atonementModule.numAtonementsActive;

    this._evangelismStatistics[event.timestamp] = {
      count: atonedPlayers,
      atonementSeconds: atonedPlayers * EVANGELISM_DURATION,
      healing: 0,
    };
  }

  onHeal(event: HealEvent) {
    if (isAtonement(event)) {
      const target = this.atonementModule.currentAtonementTargets.find(
        (id) => id.target === event.targetID,
      );
      // Pets, guardians, etc.
      if (!target) {
        return;
      }

      // Add all healing that shouldn't exist to expiration
      if (event.timestamp > target.atonementExpirationTimestamp && this._previousEvangelismCast) {
        this._evangelismStatistics[this._previousEvangelismCast.timestamp].healing +=
          event.amount + (event.absorbed || 0);
      }
    }
  }

  statistic() {
    const evangelismStatistics = this.evangelismStatistics;

    return (
      <StatisticBox
        icon={<SpellIcon id={SPELLS.EVANGELISM_TALENT.id} />}
        value={`${formatNumber(
          (evangelismStatistics.reduce((total, c) => total + c.healing, 0) /
            this.owner.fightDuration) *
            1000,<commit_msg>specified evangelism window<commit_after>import { formatNumber, formatPercentage } from 'common/format';
import SPELLS from 'common/SPELLS';
import { SpellIcon } from 'interface';
import { TooltipElement } from 'interface';
import Analyzer, { SELECTED_PLAYER } from 'parser/core/Analyzer';
import Events, { CastEvent, HealEvent } from 'parser/core/Events';
import { Options } from 'parser/core/Module';
import StatisticBox from 'parser/ui/StatisticBox';

import isAtonement from '../core/isAtonement';
import Atonement from './Atonement';

const EVANGELISM_DURATION = 6;
const EVANGELISM_DURATION_MS = 6000;

class Evangelism extends Analyzer {
  static dependencies = {
    atonementModule: Atonement,
  };
  _previousEvangelismCast: CastEvent | null = null;
  protected atonementModule!: Atonement;

  constructor(options: Options) {
    super(options);
    this.active = this.selectedCombatant.hasTalent(SPELLS.EVANGELISM_TALENT.id);
    this.addEventListener(
      Events.cast.by(SELECTED_PLAYER).spell(SPELLS.EVANGELISM_TALENT),
      this.onCast,
    );
    this.addEventListener(Events.heal.by(SELECTED_PLAYER), this.onHeal);
  }

  _evangelismStatistics: {
    [timestamp: number]: {
      count: number;
      atonementSeconds: number;
      healing: number;
    };
  } = {};

  get evangelismStatistics() {
    return Object.keys(this._evangelismStatistics)
      .map(Number)
      .map((key: number) => this._evangelismStatistics[key]);
  }

  onCast(event: CastEvent) {
    this._previousEvangelismCast = event;
    const atonedPlayers = this.atonementModule.numAtonementsActive;

    this._evangelismStatistics[event.timestamp] = {
      count: atonedPlayers,
      atonementSeconds: atonedPlayers * EVANGELISM_DURATION,
      healing: 0,
    };
  }

  onHeal(event: HealEvent) {
    if (isAtonement(event)) {
      const target = this.atonementModule.currentAtonementTargets.find(
        (id) => id.target === event.targetID,
      );
      // Pets, guardians, etc.
      if (!target) {
        return;
      }

      // Add all healing that shouldn't exist to expiration
      if (
        event.timestamp > target.atonementExpirationTimestamp &&
        event.timestamp < target.atonementExpirationTimestamp + EVANGELISM_DURATION_MS &&
        this._previousEvangelismCast
      ) {
        this._evangelismStatistics[this._previousEvangelismCast.timestamp].healing +=
          event.amount + (event.absorbed || 0);
      }
    }
  }

  statistic() {
    const evangelismStatistics = this.evangelismStatistics;

    return (
      <StatisticBox
        icon={<SpellIcon id={SPELLS.EVANGELISM_TALENT.id} />}
        value={`${formatNumber(
          (evangelismStatistics.reduce((total, c) => total + c.healing, 0) /
            this.owner.fightDuration) *
            1000,
{'fraction_non_alphanumeric': 0.11363217118612802, 'fraction_numerical': 0.003689356207341819, 'mean_word_length': 3.383185125303153, 'pattern_counts': {'":': 0, '<': 8, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '135262', 'n_tokens_mistral': 1751, 'n_tokens_neox': 1678, 'n_words': 311}