(function (global) {
  'use strict';

  /**
   * Дерево ситуаций: трёхуровневая структура
   *  Level 1: режим осмысления (mode: 0 — пассивный, 1 — целевой)
   *  Level 2: образ Темы (ThemeImage: id, mode, componentIds[], count, ready)
   *  Level 3: образ Ситуации (SituationImage: id, mode, componentIds[], count, ready)
   *
   * Образы Тем и Ситуаций формируются по состоянию полей инфокартины:
   *  - Тема: сначала только по уровням дерева восприятия (BaseID, EmotionID, ActivityID)
   *  - Ситуация: по набору детекторных переменных инфокартины (criticalVital, badState, ...).
   *
   * Новый образ становится «готовым» (ready = true), когда count > 3.
   * Попытка активации дерева — по каждой активации дерева восприятия:
   *  - из текущей инфокартины извлекается снимок (mode, themeComponents, situationComponents),
   *  - счётчики образов увеличиваются,
   *  - если count > 3 — образ помечается ready,
   *  - активируется та Тема/Ситуация, чьи готовые образы наиболее соответствуют текущим компонентам.
   *
   * Результат активации:
   *  - InfoDet_updateCurrentTheme(themeId)
   *  - InfoDet_updateCurrentSituation(situationId)
   *
   * Детекторы полей инфокартины (виталы, эффекты, ответы и т.д.) — в info_detectors.js.
   */

  var ThemeImages = [];      // { id, mode, componentIds, count, ready }
  var SituationImages = [];  // { id, mode, componentIds, count, ready }
  var nextThemeId = 1;
  var nextSituationId = 1;
  var treeDirty = false;
  var lastSavedSig = '';

  // Фиксированное отображение детекторных полей инфокартины в компонентные ID для ситуаций.
  // Это даёт стабильные числовые компоненты для образов ситуаций.
  var DETECTOR_COMPONENT_IDS = {
    // Соответствие полям InfoPicture: при ненулевом значении детектора в компоненты ситуации попадает этот числовой id.
    criticalVital: 1,              // активен критический витал (>0 — id витала)
    shockState: 2,                 // шоковое состояние
    suddenChange: 3,               // зафиксировано резкое изменение состояния
    playMode: 4,                   // игровой режим
    learningMode: 5,               // режим обучения
    negativeMotorEffect: 6,        // негативный эффект моторного автоматизма
    negativeMentalEffect: 7,       // негативный эффект ментального автоматизма
    badState: 8,                   // состояние «Плохо»
    consoleStimulus: 9,            // есть активный стимул с пульта
    searchInterest: 10,            // поисковый интерес
    teacherLearning: 11,           // идёт обучение с учителем
    operatorIgnoring: 12,          // оператор игнорирует
    dissatisfaction: 13,           // неудовлетворённость существующим
    misunderstanding: 14,          // непонимание
    doubtInDefaultAutomatism: 16,  // сомнение в штатном автоматизме
    defense: 17,                   // режим защиты
    fear: 18,                      // страх
    aggression: 19,                // агрессия
    highImportanceObject: 20,      // есть объект высокой значимости
    moodImproving: 21,             // зафиксировано улучшение настроения
    imageNovelty: 22,              // высокая новизна образа
    highSemanticImportance: 23,    // высокая семантическая значимость
    conflictingSemantics: 24,        // противоречивая семантика
    hardProblem: 25,               // трудная проблема
    positiveActionEffect: 26,      // положительный эффект последнего действия
    negativeActionEffect: 27,      // отрицательный эффект последнего действия
    noReactionToStimulus: 28,      // нет реакции на стимул
    operatorAnswerReceived: 29,    // получен ответ оператора
    activeWaitingAnswer: 30,       // активное ожидание ответа
    seriesWithoutEffect: 31,       // серия эпизодов без эффекта
    lowAutomatismUsefulness: 32,   // низкая полезность автоматизма
    foundSuitableAutomatism: 33,    // найден подходящий автоматизм
    notFoundSuitableAutomatism: 34, // НЕ найден подходящий автоматизм
    usedRule: 35,                   // реагирование по правилу
    /**
     * Синтетический маркер «всё спокойно»: не дублирует поле InfoPicture — подставляется в
     * extractSituationComponentsFromInfo, когда нет ни одного ненулевого детектора, но
     * BadNormWell — Норма/Хорошо, активен базовый контекст «Покой» (id 14), нет крит. витала.
     * Иначе ячейка «Ситуация» и образы ситуаций не накапливались при типичной спокойной фазе.
     */
    peaceNormBaseline: 36
  };

  function cloneSortedComponents(arr) {
    if (!Array.isArray(arr)) return [];
    var filtered = [];
    for (var i = 0; i < arr.length; i++) {
      var v = parseInt(arr[i], 10);
      if (!isNaN(v) && v !== 0 && filtered.indexOf(v) === -1) {
        filtered.push(v);
      }
    }
    filtered.sort(function (a, b) { return a - b; });
    return filtered;
  }

  function componentsEqual(a, b) {
    if (!Array.isArray(a) || !Array.isArray(b)) return false;
    if (a.length !== b.length) return false;
    for (var i = 0; i < a.length; i++) {
      if (a[i] !== b[i]) return false;
    }
    return true;
  }

  // Простейшая мера соответствия: число общих компонент.
  function overlapScore(a, b) {
    if (!Array.isArray(a) || !Array.isArray(b) || !a.length || !b.length) return 0;
    var score = 0;
    for (var i = 0; i < a.length; i++) {
      if (b.indexOf(a[i]) !== -1) score++;
    }
    return score;
  }

  function findOrCreateThemeImage(mode, componentIds) {
    var comps = cloneSortedComponents(componentIds);
    if (!comps.length) return null;
    for (var i = 0; i < ThemeImages.length; i++) {
      var t = ThemeImages[i];
      if (t.mode === mode && componentsEqual(t.componentIds, comps)) {
        t.count++;
        if (!t.ready && t.count > 3) {
          t.ready = true;
        }
        return t;
      }
    }
    // Новый образ темы
    var theme = {
      id: nextThemeId++,
      mode: mode,
      componentIds: comps,
      count: 1,
      ready: false
    };
    ThemeImages.push(theme);
    treeDirty = true;
    return theme;
  }

  function findOrCreateSituationImage(mode, componentIds) {
    var comps = cloneSortedComponents(componentIds);
    if (!comps.length) return null;
    for (var i = 0; i < SituationImages.length; i++) {
      var s = SituationImages[i];
      if (s.mode === mode && componentsEqual(s.componentIds, comps)) {
        s.count++;
        if (!s.ready && s.count > 3) {
          s.ready = true;
        }
        return s;
      }
    }
    // Новый образ ситуации
    var sit = {
      id: nextSituationId++,
      mode: mode,
      componentIds: comps,
      count: 1,
      ready: false
    };
    SituationImages.push(sit);
    treeDirty = true;
    return sit;
  }

  // -------------------- Сохранение/восстановление (файл + IndexedDB) --------------------

  function serializeState() {
    return {
      nextThemeId: nextThemeId,
      nextSituationId: nextSituationId,
      themeImages: ThemeImages.slice(0),
      situationImages: SituationImages.slice(0)
    };
  }

  function applyState(state) {
    state = state || {};
    nextThemeId = parseInt(state.nextThemeId, 10);
    if (isNaN(nextThemeId) || nextThemeId < 1) nextThemeId = 1;
    nextSituationId = parseInt(state.nextSituationId, 10);
    if (isNaN(nextSituationId) || nextSituationId < 1) nextSituationId = 1;

    ThemeImages = Array.isArray(state.themeImages) ? state.themeImages.slice(0) : [];
    SituationImages = Array.isArray(state.situationImages) ? state.situationImages.slice(0) : [];

    // пересчитать nextId на всякий случай (защита от конфликтов при загрузке)
    for (var i = 0; i < ThemeImages.length; i++) {
      var t = ThemeImages[i];
      if (t && typeof t.id === 'number' && t.id >= nextThemeId) nextThemeId = t.id + 1;
    }
    for (var j = 0; j < SituationImages.length; j++) {
      var s = SituationImages[j];
      if (s && typeof s.id === 'number' && s.id >= nextSituationId) nextSituationId = s.id + 1;
    }

    treeDirty = false;
    try {
      lastSavedSig = JSON.stringify(serializeState());
    } catch (e) {
      lastSavedSig = '';
    }
  }

  var DB_NAME = 'BEAST_SituationsTree_DB';
  // Важно: увеличение версии принудительно запускает onupgradeneeded и создаёт store,
  // если раньше БД была создана без него (из-за изменений в обёртке IndexedDB).
  var DB_VERSION = 2;
  var STORE = 'situations_tree_state';
  var KEY = 'situations_tree_singleton';

  function openDb() {
    if (!global.IndexedDBModule || typeof global.IndexedDBModule.openDB !== 'function') {
      return Promise.reject(new Error('IndexedDBModule not available'));
    }
    return global.IndexedDBModule.openDB({
      dbName: DB_NAME,
      version: DB_VERSION,
      storeName: STORE,
      keyPath: 'id'
    });
  }

  function loadFromIndexedDB() {
    openDb()
      .then(function (d) {
        return global.IndexedDBModule.get({ db: d, storeName: STORE, key: KEY });
      })
      .then(function (record) {
        if (record && record.state) {
          applyState(record.state);
        }
      })
      .catch(function (e) {
        if (global.console && typeof global.console.error === 'function') {
          global.console.error('SituationsTree IndexedDB load error:', e);
        }
      });
  }

  loadFromIndexedDB();

  if (typeof global.registerIndexedDBSaver === 'function') {
    global.registerIndexedDBSaver(function () {
      if (!treeDirty) return;
      treeDirty = false;
      var state = serializeState();
      var sig = '';
      try { sig = JSON.stringify(state); } catch (e) { sig = ''; }
      if (sig && sig === lastSavedSig) return;
      lastSavedSig = sig;
      openDb()
        .then(function (db) {
          return global.IndexedDBModule.put({
            db: db,
            storeName: STORE,
            value: { id: KEY, state: state, ts: Date.now() }
          });
        })
        .catch(function (e) {
          if (global.console && typeof global.console.error === 'function') {
            global.console.error('SituationsTree IndexedDB save error:', e);
          }
        });
    });
  }

  function findBestReadyTheme(mode, componentIds) {
    var comps = cloneSortedComponents(componentIds);
    if (!comps.length) return 0;
    var bestId = 0;
    var bestScore = 0;
    for (var i = 0; i < ThemeImages.length; i++) {
      var t = ThemeImages[i];
      if (!t.ready || t.mode !== mode) continue;
      var s = overlapScore(comps, t.componentIds);
      if (s > bestScore) {
        bestScore = s;
        bestId = t.id;
      }
    }
    return bestId;
  }

  function findBestReadySituation(mode, componentIds) {
    var comps = cloneSortedComponents(componentIds);
    if (!comps.length) return 0;
    var bestId = 0;
    var bestScore = 0;
    for (var i = 0; i < SituationImages.length; i++) {
      var sIt = SituationImages[i];
      if (!sIt.ready || sIt.mode !== mode) continue;
      var s = overlapScore(comps, sIt.componentIds);
      if (s > bestScore) {
        bestScore = s;
        bestId = sIt.id;
      }
    }
    return bestId;
  }

  // ---------- Публичные помощники для детализации ----------

  function SituationsTree_getThemeImage(id) {
    for (var i = 0; i < ThemeImages.length; i++) {
      if (ThemeImages[i].id === id) return ThemeImages[i];
    }
    return null;
  }

  function SituationsTree_getAllThemeImages() {
    return ThemeImages.slice(0);
  }

  function SituationsTree_getSituationImage(id) {
    for (var i = 0; i < SituationImages.length; i++) {
      if (SituationImages[i].id === id) return SituationImages[i];
    }
    return null;
  }

  function SituationsTree_getAllSituationImages() {
    return SituationImages.slice(0);
  }

  function SituationsTree_getSituationComponentName(componentId) {
    for (var key in DETECTOR_COMPONENT_IDS) {
      if (!Object.prototype.hasOwnProperty.call(DETECTOR_COMPONENT_IDS, key)) continue;
      if (DETECTOR_COMPONENT_IDS[key] === componentId) {
        return key;
      }
    }
    return '';
  }

  /**
   * Извлекает компоненты образа Темы из текущей инфокартины:
   * пока: [BaseID, EmotionID, ActivityID] (ненулевые).
   */
  function extractThemeComponentsFromInfo(info) {
    var comps = [];
    if (!info || !info.tree) return comps;
    if (info.tree.baseID) comps.push(info.tree.baseID);
    if (info.tree.emotionID) comps.push(info.tree.emotionID);
    if (info.tree.activityID) comps.push(info.tree.activityID);
    return comps;
  }

  /**
   * Извлекает компоненты образа Ситуации из текущей инфокартины:
   * по детекторным переменным (каждый ненулевой флаг даёт компонент).
   * Если набор пуст (типично при Норме виталов и «Покой», без тревожных детекторов),
   * подставляется компонент peaceNormBaseline (36), чтобы накапливался и активировался
   * образ ситуации «спокойная норма» — см. DETECTOR_COMPONENT_IDS.peaceNormBaseline.
   */
  function extractSituationComponentsFromInfo(info) {
    var comps = [];
    if (!info) return comps;
    var k;
    for (k in DETECTOR_COMPONENT_IDS) {
      if (!Object.prototype.hasOwnProperty.call(DETECTOR_COMPONENT_IDS, k)) continue;
      if (k === 'peaceNormBaseline') continue;
      var cid = DETECTOR_COMPONENT_IDS[k];
      var v = info[k];
      if (typeof v === 'number' && v !== 0) {
        if (comps.indexOf(cid) === -1) comps.push(cid);
      }
    }
    if (!comps.length) {
      var bnw = typeof global.BadNormWell === 'number' ? global.BadNormWell : 0;
      var wellBenign = (bnw === 2 || bnw === 3);
      var act = global.BasicContextsActived;
      var peace = Array.isArray(act) && act.indexOf(14) !== -1;
      var crit = typeof info.criticalVital === 'number' ? info.criticalVital : 0;
      if (wellBenign && peace && !crit) {
        var baselineCid = DETECTOR_COMPONENT_IDS.peaceNormBaseline;
        if (baselineCid != null) comps.push(baselineCid);
      }
    }
    return comps;
  }

  /**
   * Основная точка входа: вызывается по каждой активации дерева восприятия
   * (после обновления инфокартины по PerceptionTree).
   *
   * 1) Извлекает mode (по режиму мышления главного цикла ФО), themeComponents, situationComponents из InfoPicture.
   * 2) Обновляет/создаёт образы Темы и Ситуации (счётчики, ready при count>3).
   * 3) Находит лучшую готовую Тему/Ситуацию для данного режима и обновляет
   *    текущие themeId / situationId через инфо‑детекторы.
   */
  function SituationsTree_onPerceptionTreeActivated() {
    if (typeof global.Info_initIfNeeded !== 'function') return;
    var info = global.Info_initIfNeeded();
    if (!info) return;

    var mainTm = (typeof global.Consciousness_getMainCycleThinkingMode === 'function')
      ? global.Consciousness_getMainCycleThinkingMode()
      : 'target';
    var mode = (mainTm === 'target') ? 1 : 0;
    var themeComps = extractThemeComponentsFromInfo(info);
    var situationComps = extractSituationComponentsFromInfo(info);

    // Регистрируем / усиливаем образы (обновление счётчиков и ready).
    if (themeComps.length) {
      findOrCreateThemeImage(mode, themeComps);
    }
    if (situationComps.length) {
      findOrCreateSituationImage(mode, situationComps);
    }

    // Активация готовых образов — поиск наиболее соответствующей ТЕМЫ и СИТУАЦИИ.
    var activeThemeId = findBestReadyTheme(mode, themeComps);
    var activeSituationId = findBestReadySituation(mode, situationComps);

    if (typeof global.InfoDet_updateCurrentTheme === 'function') {
      global.InfoDet_updateCurrentTheme(activeThemeId);
    }
    if (typeof global.InfoDet_updateCurrentSituation === 'function') {
      global.InfoDet_updateCurrentSituation(activeSituationId);
    }
  }

  /**
   * Заготовка: рассмотрение новых образов ситуаций на будущее в ходе семантической обработки моделей понимания.
   * Вызывается из PassiveMode_processUnderstandingModels (goals.js), когда в пассивном режиме идёт
   * семантическая обработка моделей понимания; в ходе этой работы возникают новые образы ситуаций на будущее.
   * Полная реализация: по данным моделей понимания (семантика, образы) создавать или усиливать
   * SituationImages с соответствующими componentIds (режим 0 — пассивный).
   */
  function SituationsTree_considerNewSituationFromUnderstanding() {
    // Заглушка: при семантической обработке — формирование/обновление образов ситуаций на будущее.
  }

  /** Очистить все образы ситуаций (SituationImages); темы не трогаем. Состояние сохранится по treeDirty. */
  function SituationsTree_clearAllSituationImages() {
    SituationImages = [];
    nextSituationId = 1;
    treeDirty = true;
    if (typeof global.InfoDet_updateCurrentSituation === 'function') {
      global.InfoDet_updateCurrentSituation(0);
    } else if (global.InfoPicture) {
      global.InfoPicture.situationId = 0;
    }
    if (typeof global.Info_updateView === 'function') global.Info_updateView();
  }

  global.SituationsTree_onPerceptionTreeActivated = SituationsTree_onPerceptionTreeActivated;
  global.SituationsTree_getThemeImage = SituationsTree_getThemeImage;
  global.SituationsTree_getAllThemeImages = SituationsTree_getAllThemeImages;
  global.SituationsTree_getSituationImage = SituationsTree_getSituationImage;
  global.SituationsTree_getAllSituationImages = SituationsTree_getAllSituationImages;
  global.SituationsTree_getSituationComponentName = SituationsTree_getSituationComponentName;
  global.SituationsTree_considerNewSituationFromUnderstanding = SituationsTree_considerNewSituationFromUnderstanding;
  global.SituationsTree_serializeState = serializeState;
  global.SituationsTree_applyState = applyState;
  global.SituationsTree_clearAllSituationImages = SituationsTree_clearAllSituationImages;

})(window);

