import { ChainingContextualSubstitutionTable, Lookup } from '../tables';
import { ILookupTree } from '../types';
import mergeTrees from '../merge';

import { listGlyphsByIndex } from './coverage';
import getGlyphClass, { listClassGlyphs } from './classDef';
import { processInputPosition, processLookaheadPosition, processBacktrackPosition, getInputTree, IEntryMeta } from './helper';

/**
 * Build lookup tree for GSUB lookup table 6, format 2.
 * https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#62-chaining-context-substitution-format-2-class-based-glyph-contexts
 *
 * @param table JSON representation of the table
 * @param lookups List of lookup tables
 * @param tableIndex Index of this table in the overall lookup
 */
export default function buildTree(table: ChainingContextualSubstitutionTable.IFormat2, lookups: Lookup[], tableIndex: number): ILookupTree {
  const results: ILookupTree[] = [];

  const firstGlyphs = listGlyphsByIndex(table.coverage);

  for (const { glyphId } of firstGlyphs) {
    const firstInputClass = getGlyphClass(table.inputClassDef, glyphId);
    for (const [glyphId, inputClass] of firstInputClass.entries()) {
      // istanbul ignore next - invalid font
      if (inputClass === null) {
        continue;
      }

      const classSet = table.chainClassSet[inputClass];

      // If the class set is null there's nothing to do with this table.
      if (!classSet) {
        continue;
      }

      for (const [subIndex, subTable] of classSet.entries()) {
        const result: ILookupTree = {
          individual: {},
          range: []
        };

        let currentEntries: IEntryMeta[] = getInputTree(
          result,
          subTable.lookupRecords,
          lookups,
          0,
          glyphId
        ).map(({ entry, substitution }) => ({ entry, substitutions: [substitution] }));

        for (const [index, classNum] of subTable.input.entries()) {
          currentEntries = processInputPosition(
            listClassGlyphs(table.inputClassDef, classNum),
            index + 1,
            currentEntries,
            subTable.lookupRecords,
            lookups
          );
        }

        for (const classNum of subTable.lookahead) {
          currentEntries = processLookaheadPosition(
            listClassGlyphs(table.lookaheadClassDef, classNum),
            currentEntries
          );
        }

        for (const classNum of subTable.backtrack) {
          currentEntries = processBacktrackPosition(
            listClassGlyphs(table.backtrackClassDef, classNum),
            currentEntries
          );
        }

        // When we get to the end, all of the entries we've accumulated
        // should have a lookup defined
        for (const { entry, substitutions } of currentEntries) {
          entry.lookup = {
            substitutions,
            index: tableIndex,
            subIndex,
            length: subTable.input.length + 1,
            contextRange: [
              -1 * subTable.backtrack.length,
              1 + subTable.input.length + subTable.lookahead.length
            ]
          };
        }

        results.push(result);
      }
    }
  }

  return mergeTrees(results);
}
