/* Детекторы активных признаков инфокартины. Это детекторы случающихся ситуаций.
Инфо-детекторы (beast/_9_Conscience/info_detectors.js) выставляют в InfoPicture поля вроде criticalVital, badState, playMode, searchInterest и т.д.

Сбор «состава ситуации» — в beast/_9_Conscience/situatioms_tree.js, функция extractSituationComponentsFromInfo(info): по карте DETECTOR_COMPONENT_IDS для каждого ключа, если в info значение число и ≠ 0, в набор компонент ситуации попадает соответствующий числовой id компонента. Если набор пуст, но Норма/Хорошо (BadNormWell 2/3), активен контекст «Покой» (id 14) и нет крит. витала — добавляется synthetic peaceNormBaseline (36), иначе при спокойной норме ситуация не формировалась бы.

Создание/усиление образа ситуации — в SituationsTree_onPerceptionTreeActivated():
берутся situationComps, при непустом наборе вызывается findOrCreateSituationImage(mode, situationComps) (счётчик count, позже ready при count > 3).

Точка вызова — из Info_updateFromPerceptionTree() в informing.js после обновления ветки восприятия вызывается SituationsTree_onPerceptionTreeActivated().
*/

(function (global) {
  'use strict';

  // Утилита безопасной записи в InfoPicture.
  function setInfo(name, value) {
    if (typeof global.Info_setField === 'function') {
      global.Info_setField(name, value);
    } else if (global.InfoPicture) {
      global.InfoPicture[name] = value;
    }
  }

  // -------- Целевой режим / Тема / Ситуация --------

  /**
   * Целевой / пассивный режим мышления главного цикла ФО (thinkingMode), не поле инфокартины.
   * Ячейка «Режим» в UI обновляется через Info_updateView (Consciousness_getMainCycleThinkingMode).
   */
  function InfoDet_updateTargetMode(isTarget) {
    if (typeof global.Consciousness_getMainCycle === 'function') {
      var mc = global.Consciousness_getMainCycle();
      if (mc) {
        mc.thinkingMode = isTarget ? 'target' : 'passive';
      }
    }
    if (typeof global.Info_updateView === 'function') {
      global.Info_updateView();
    }
  }

  /**
   * Текущая тема осмысления.
   * Активный образ темы – ID или 0.
   */
  function InfoDet_updateCurrentTheme(themeId) {
    setInfo('themeId', themeId || 0);
  }

  /**
   * Текущая ситуация осмысления.
   * Текущий активный образ ситуации – ID или 0.
   */
  function InfoDet_updateCurrentSituation(situationId) {
    setInfo('situationId', situationId || 0);
  }

  // -------- Виталы и базовые контексты --------

  /**
   * Критический витал:
   * если какой-то витал заходит за значение 70, возвращается его ID.
   * Если таких несколько – выбирается витал с наибольшим значением и его ID записывается.
   * @param {Array<{Id:number,value:number}>} vitalsArr
   */
  function InfoDet_updateCriticalVital(vitalsArr) {
    // При «Хорошо» и активной подаче еды/воды инфокартина не должна показывать критический витал —
    // стимул сразу задаёт удержание Хорошо (SetWellForHolding) и смыслово перекрывает «критичность».
    var bnw = typeof global.BadNormWell === 'number' ? global.BadNormWell : 0;
    if (bnw === 3 && Array.isArray(global.ActiveMotivationStimuls)) {
      var am = global.ActiveMotivationStimuls;
      if (am.indexOf(1) !== -1 || am.indexOf(2) !== -1) {
        setInfo('criticalVital', 0);
        setInfo('ImportantProblem', 0);
        return;
      }
    }
    if (!Array.isArray(vitalsArr) || !vitalsArr.length) {
      setInfo('criticalVital', 0);
      setInfo('ImportantProblem', 0);
      return;
    }
    var bestId = 0;
    var bestVal = 70;
    for (var i = 0; i < vitalsArr.length; i++) {
      var v = vitalsArr[i];
      if (!v || typeof v.value !== 'number') continue;
      if (v.value > bestVal) {
        bestVal = v.value;
        bestId = v.Id || 0;
      }
    }
    setInfo('criticalVital', bestId);
    setInfo('ImportantProblem', bestId ? 1 : 0);
  }

  /**
   * Резкое ухудшение состояния / Состояние Плохо.
   * Резкое ухудшение: 1 – сменилось состояние на Плохо; иначе 0.
   * Состояние Плохо: badState = 1 при BadNormWell=1, иначе 0.
   * @param {number} lastBadNormWell - предыдущее BadNormWell
   * @param {number} badNormWell - текущее BadNormWell (1/2/3)
   */
  function InfoDet_updateBadState(lastBadNormWell, badNormWell) {
    if (typeof badNormWell !== 'number') badNormWell = 0;
    if (typeof lastBadNormWell !== 'number') lastBadNormWell = 0;
    // badState: текущее Плохо.
    setInfo('badState', badNormWell === 1 ? 1 : 0);
    // suddenChange: 1 если было не Плохо, а стало Плохо.
    var sudden = (lastBadNormWell !== 1 && badNormWell === 1) ? 1 : 0;
    setInfo('suddenChange', sudden);
  }

  /**
   * Игровой режим (ячейка «Игра»):
   * — базовый контекст Игра (id=11), или
   * — кнопка пульта «Играть» (global.isGameMode), см. stimuls_ui.js.
   * @param {number[]} basicContextsActived
   */
  function InfoDet_updatePlayMode(basicContextsActived) {
    var active = Array.isArray(basicContextsActived) &&
      basicContextsActived.indexOf(11) !== -1;
    if (global.isGameMode) {
      active = true;
    }
    setInfo('playMode', active ? 1 : 0);
  }

  /**
   * Режим обучения / Поисковый интерес:
   * Обучение (ячейка «Обучение»): контекст Любопытство (id=12), или Интерес>70,
   * или кнопка «Учить» (global.isAuthoritarianEducation).
   * Поисковый интерес: только витал Интерес > 70 (без кнопки «Учить»).
   * @param {number[]} basicContextsActived
   * @param {Array<{Id:number,value:number}>} vitalsArr
   *   (ожидается витал с Id==10 или по договорённости как "Интерес")
   */
  function InfoDet_updateLearningAndSearch(basicContextsActived, vitalsArr) {
    var curiosityActive = Array.isArray(basicContextsActived) &&
      basicContextsActived.indexOf(12) !== -1;
    var interestHigh = false;
    if (Array.isArray(vitalsArr)) {
      for (var i = 0; i < vitalsArr.length; i++) {
        var v = vitalsArr[i];
        if (!v || typeof v.value !== 'number') continue;
        // Здесь предполагаем, что витал "Интерес" имеет Id == 10 (или другой, по факту).
        if (v.Id === 10 && v.value > 70) {
          interestHigh = true;
          break;
        }
      }
    }
    setInfo('searchInterest', interestHigh ? 1 : 0);
    var learning = curiosityActive || interestHigh || !!global.isAuthoritarianEducation;
    setInfo('learningMode', learning ? 1 : 0);
  }

  // -------- Эффекты действий и автоматизмов --------

  /**
   * Негативный эффект моторного автоматизма:
   * getDiffImportance(), вызываемая при стимуле во время периода ожидания
   * последствий моторного автоматизма, выдаёт значение < 0.
   * @param {number} z - значение getDiffImportance() в период ожидания
   */
  function InfoDet_updateNegativeMotorEffect(z) {
    if (typeof z !== 'number' || isNaN(z)) {
      setInfo('negativeMotorEffect', 0);
      return;
    }
    setInfo('negativeMotorEffect', z < 0 ? z : 0);
  }

  /**
   * Негативный эффект ментального автоматизма:
   * аналогично моторному, но для ментальных автоматизмов.
   * Пока может быть заготовкой: просто принимает z и пишет, если < 0.
   * @param {number} z
   */
  function InfoDet_updateNegativeMentalEffect(z) {
    if (typeof z !== 'number' || isNaN(z)) {
      setInfo('negativeMentalEffect', 0);
      return;
    }
    setInfo('negativeMentalEffect', z < 0 ? z : 0);
  }

  /**
   * Положительный / отрицательный эффект действия:
   * при закрытии кадра ЭП после автоматизма.
   * @param {number} effect - last.effect из ЭП (diffAfter - diffBefore)
   */
  function InfoDet_updateActionEffect(effect) {
    if (typeof effect !== 'number' || isNaN(effect)) {
      setInfo('positiveActionEffect', 0);
      setInfo('negativeActionEffect', 0);
      return;
    }
    setInfo('positiveActionEffect', effect > 0 ? effect : 0);
    setInfo('negativeActionEffect', effect < 0 ? effect : 0);
  }

  /**
   * Нет реакции на стимул:
   * период ожидания результата автоматизма закончился без ответного стимула.
   * @param {boolean} noReaction
   */
  function InfoDet_updateNoReaction(noReaction) {
    setInfo('noReactionToStimulus', noReaction ? 1 : 0);
  }

  /**
   * Активное ожидание ответа:
   * запущен период ожидания результата автоматизма из функции осмысления.
   * @param {boolean} active
   */
  function InfoDet_updateActiveWaiting(active) {
    setInfo('activeWaitingAnswer', active ? 1 : 0);
  }

  /**
   * Низкая полезность автоматизма:
   * эффект в ожидании не превышает 1 (по модулю).
   * @param {number} usefulnessSample
   */
  function InfoDet_updateLowAutomatismUsefulness(usefulnessSample) {
    if (typeof usefulnessSample !== 'number' || isNaN(usefulnessSample)) {
      setInfo('lowAutomatismUsefulness', 0);
      return;
    }
    if (Math.abs(usefulnessSample) <= 1 && usefulnessSample !== 0) {
      setInfo('lowAutomatismUsefulness', usefulnessSample);
    } else {
      setInfo('lowAutomatismUsefulness', 0);
    }
  }

  /**
   * Пока найден подходящий автоматизм:
   * продолжается цикл осмысления в целевом режиме.
   * @param {boolean} active
   */
  function InfoDet_updateFoundSuitableAutomatism(active) {
    setInfo('foundSuitableAutomatism', active ? 1 : 0);
  }

  // -------- Стимулы и семантика --------

  /**
   * Стимул с Пульта:
   * появился новый актуальный стимул, вызвавший ориентировочный рефлекс.
   * Записывается ID образа стимула (FinalImageId) или 0.
   * @param {number} finalImageId
   */
  function InfoDet_updateConsoleStimulus(finalImageId) {
    if (typeof finalImageId !== 'number' || finalImageId <= 0) {
      setInfo('consoleStimulus', 0);
    } else {
      setInfo('consoleStimulus', finalImageId);
    }
  }

  /**
   * Есть объект высокой значимости:
   * текущий образ актуального стимула содержит стимул с высокой семантической значимостью
   * в данном контексте условий. Здесь ожидается, что вызывающая сторона уже обнаружила
   * такой объект и передала его ID или уровень значимости.
   * @param {number} objectIdOrLevel
   */
  function InfoDet_updateHighImportanceObject(objectIdOrLevel) {
    if (typeof objectIdOrLevel !== 'number' || objectIdOrLevel <= 0) {
      setInfo('highImportanceObject', 0);
    } else {
      setInfo('highImportanceObject', objectIdOrLevel);
    }
  }

  /**
   * Улучшение настроения:
   * появилось состояние Хорошо или улучшение более, чем на 5 единиц.
   * @param {number} diffLevel - положительная разница (нормализованная), если есть.
   */
  function InfoDet_updateMoodImproving(diffLevel) {
    if (typeof diffLevel !== 'number' || isNaN(diffLevel)) {
      setInfo('moodImproving', 0);
      return;
    }
    if (diffLevel > 5) {
      setInfo('moodImproving', diffLevel);
    } else {
      setInfo('moodImproving', 0);
    }
  }

  /**
   * Новизна образа:
   * детектор новизны ОР даёт большое значение novelty.
   * @param {number} novelty
   * @param {number} threshold
   */
  function InfoDet_updateImageNovelty(novelty, threshold) {
    if (typeof novelty !== 'number' || isNaN(novelty)) {
      setInfo('imageNovelty', 0);
      return;
    }
    if (typeof threshold !== 'number' || isNaN(threshold)) threshold = 0.5;
    setInfo('imageNovelty', novelty > threshold ? novelty : 0);
  }

  /**
   * Высокая семантическая значимость:
   * для текущего образа семантика >> 0.
   * @param {number} meanImportance
   */
  function InfoDet_updateHighSemanticImportance(meanImportance) {
    if (typeof meanImportance !== 'number' || isNaN(meanImportance)) {
      setInfo('highSemanticImportance', 0);
      return;
    }
    setInfo('highSemanticImportance', meanImportance > 0 ? meanImportance : 0);
  }

  /**
   * Противоречивая семантика:
   * для данного образа семантика около нуля при высокой силе измерений (samplesCount),
   * и при этом в других условиях есть как большие положительные, так и большие отрицательные значения.
   * Здесь ожидается, что вызывающий код уже провёл анализ и передаст 1/0.
   * @param {boolean} hasConflict
   */
  function InfoDet_updateConflictingSemantics(hasConflict) {
    setInfo('conflictingSemantics', hasConflict ? 1 : 0);
  }

  // -------- Цикл осмысления / проблемы --------

  /**
   * Обучение с учителем:
   * 1 – включается, когда функция осмысления даёт Нерешённая проблема
   * в течение непрекращающегося цикла попыток решения (режим отслеживания действий оператора).
   * @param {boolean} active
   */
  function InfoDet_updateTeacherLearning(active) {
    setInfo('teacherLearning', active ? 1 : 0);
  }

  /**
   * Игнорирование оператором:
   * активизация мотивирующего стимула «Игнорировать» или отсутствие стимула в период
   * ожидания после реакции и завершившийся период.
   * @param {boolean} active
   */
  var operatorIgnoring_waiting = false;
  var operatorIgnoring_stimulus = false;

  function InfoDet_recalcOperatorIgnoring() {
    setInfo('operatorIgnoring', (operatorIgnoring_waiting || operatorIgnoring_stimulus) ? 1 : 0);
  }

  /**
   * Источник 1: логика периода ожидания / отсутствия ответа.
   */
  function InfoDet_updateOperatorIgnoring(active) {
    operatorIgnoring_waiting = !!active;
    InfoDet_recalcOperatorIgnoring();
  }

  /**
   * Источник 2: активен мотивирующий стимул «Игнорировать» (StimulMotivarion.Id = 18).
   * Вызывать при переключении стимула или при массовом сбросе стимулов.
   */
  function InfoDet_updateOperatorIgnoringFromStimulus() {
    var arr = global.ActiveMotivationStimuls;
    operatorIgnoring_stimulus = Array.isArray(arr) && arr.indexOf(18) !== -1;
    InfoDet_recalcOperatorIgnoring();
  }

  /**
   * Неудовлетворенность существующим:
   * базовый контекст Покой активен, нет актуальных стимулов и закончен цикл
   * осмысления в пассивном режиме.
   * @param {boolean} active
   */
  function InfoDet_updateDissatisfaction(active) {
    var v = active ? 1 : 0;
    setInfo('dissatisfaction', v);
    setInfo('isDissatisfaction', v);
  }

  /**
   * Непонимание:
   * цикл осмысления дошёл до конца в целевом режиме или число шагов итерации превысило 10.
   * @param {boolean} active
   */
  function InfoDet_updateMisunderstanding(active) {
    setInfo('misunderstanding', active ? 1 : 0);
  }

  /**
   * Сомнение в штатном автоматизме:
   * начат второй уровень осмысления в целевом режиме.
   * @param {boolean} active
   */
  function InfoDet_updateDoubtInDefaultAutomatism(active) {
    setInfo('doubtInDefaultAutomatism', active ? 1 : 0);
  }

  /**
   * Защита:
   * активен базовый контекст Страх и/или Агрессия.
   * @param {boolean} active
   */
  function InfoDet_updateDefense(active) {
    setInfo('defense', active ? 1 : 0);
  }

  /**
   * Страх:
   * активен базовый контекст Страх.
   * @param {boolean} active
   */
  function InfoDet_updateFear(active) {
    setInfo('fear', active ? 1 : 0);
  }

  /**
   * Агрессия:
   * активен базовый контекст Агрессия.
   * @param {boolean} active
   */
  function InfoDet_updateAggression(active) {
    setInfo('aggression', active ? 1 : 0);
  }

  /**
   * Трудная проблема:
   * массив образов, для которых в ходе осмысления возникает нерешённая проблема
   * более чем на 10 шагов и цикл доходит до конца в целевом режиме.
   * Здесь ожидается, что вызывающий код ведёт такой массив и передаёт уровень/счётчик.
   * @param {number} level
   */
  function InfoDet_updateHardProblem(level) {
    if (typeof level !== 'number' || isNaN(level)) {
      setInfo('hardProblem', 0);
    } else {
      setInfo('hardProblem', level);
    }
  }

  /**
   * Ответ оператора получен:
   * @param {boolean} received
   */
  function InfoDet_updateOperatorAnswer(received) {
    setInfo('operatorAnswerReceived', received ? 1 : 0);
    if (!received) {
      setInfo('isOperatorAnswerReceived', 0);
    }
  }

  /**
   * Серия эпизодов без эффекта:
   * @param {number} length
   */
  function InfoDet_updateSeriesWithoutEffect(length) {
    if (typeof length !== 'number' || isNaN(length) || length <= 0) {
      setInfo('seriesWithoutEffect', 0);
    } else {
      setInfo('seriesWithoutEffect', length);
    }
  }

  // Экспорт детекторов в global
  global.InfoDet_updateTargetMode = InfoDet_updateTargetMode;
  global.InfoDet_updateCurrentTheme = InfoDet_updateCurrentTheme;
  global.InfoDet_updateCurrentSituation = InfoDet_updateCurrentSituation;
  global.InfoDet_updateCriticalVital = InfoDet_updateCriticalVital;
  global.InfoDet_updateBadState = InfoDet_updateBadState;
  global.InfoDet_updatePlayMode = InfoDet_updatePlayMode;
  global.InfoDet_updateLearningAndSearch = InfoDet_updateLearningAndSearch;
  global.InfoDet_updateNegativeMotorEffect = InfoDet_updateNegativeMotorEffect;
  global.InfoDet_updateNegativeMentalEffect = InfoDet_updateNegativeMentalEffect;
  global.InfoDet_updateActionEffect = InfoDet_updateActionEffect;
  global.InfoDet_updateNoReaction = InfoDet_updateNoReaction;
  global.InfoDet_updateActiveWaiting = InfoDet_updateActiveWaiting;
  global.InfoDet_updateLowAutomatismUsefulness = InfoDet_updateLowAutomatismUsefulness;
  global.InfoDet_updateFoundSuitableAutomatism = InfoDet_updateFoundSuitableAutomatism;
  global.InfoDet_updateConsoleStimulus = InfoDet_updateConsoleStimulus;
  global.InfoDet_updateHighImportanceObject = InfoDet_updateHighImportanceObject;
  global.InfoDet_updateMoodImproving = InfoDet_updateMoodImproving;
  global.InfoDet_updateImageNovelty = InfoDet_updateImageNovelty;
  global.InfoDet_updateHighSemanticImportance = InfoDet_updateHighSemanticImportance;
  global.InfoDet_updateConflictingSemantics = InfoDet_updateConflictingSemantics;
  global.InfoDet_updateTeacherLearning = InfoDet_updateTeacherLearning;
  global.InfoDet_updateOperatorIgnoring = InfoDet_updateOperatorIgnoring;
  global.InfoDet_updateOperatorIgnoringFromStimulus = InfoDet_updateOperatorIgnoringFromStimulus;
  global.InfoDet_updateDissatisfaction = InfoDet_updateDissatisfaction;
  global.InfoDet_updateMisunderstanding = InfoDet_updateMisunderstanding;
  global.InfoDet_updateDoubtInDefaultAutomatism = InfoDet_updateDoubtInDefaultAutomatism;
  global.InfoDet_updateDefense = InfoDet_updateDefense;
  global.InfoDet_updateFear = InfoDet_updateFear;
  global.InfoDet_updateAggression = InfoDet_updateAggression;
  global.InfoDet_updateHardProblem = InfoDet_updateHardProblem;
  global.InfoDet_updateOperatorAnswer = InfoDet_updateOperatorAnswer;
  global.InfoDet_updateSeriesWithoutEffect = InfoDet_updateSeriesWithoutEffect;

})(window);

