"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.FontBuilderState = exports.CharBuilderState = void 0;
exports.buildFont = buildFont;
exports.processLine = processLine;
exports.requiredCharacters = void 0;

var _Case = /*#__PURE__*/require("@effect-ts/core/Case");

var A = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@effect-ts/core/Collections/Immutable/Array"));

var C = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@effect-ts/core/Collections/Immutable/Chunk"));

var Map = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@effect-ts/core/Collections/Immutable/Map"));

var NA = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@effect-ts/core/Collections/Immutable/NonEmptyArray"));

var Set = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@effect-ts/core/Collections/Immutable/Set"));

var E = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@effect-ts/core/Either"));

var Equal = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@effect-ts/core/Equal"));

var _Function = /*#__PURE__*/require("@effect-ts/core/Function");

var O = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@effect-ts/core/Option"));

var Ord = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@effect-ts/core/Ord"));

var crypto = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("crypto"));

var path = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("path"));

var FC = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../FigCharacter/index.js"));

var FH = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../FigHeader/index.js"));

var FE = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../FigletException/index.js"));

var SL = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../SubLines/index.js"));

var _definition = /*#__PURE__*/require("./definition.js");

var HorizontalLayout = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./HorizontalLayout/index.js"));

var PrintDirection = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./PrintDirection/index.js"));

var _index7 = /*#__PURE__*/require("./Settings/index.js");

var VerticalLayout = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./VerticalLayout/index.js"));

function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }

function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }

// ets_tracing: off
// -----------------------------------------------------------------------------
// Model
// -----------------------------------------------------------------------------
class CharBuilderState extends _Case.Case {}

exports.CharBuilderState = CharBuilderState;

class FontBuilderState extends _Case.Case {} // -----------------------------------------------------------------------------
// Builders
// -----------------------------------------------------------------------------

/**
 * The list of all required characters that all `FigFont`s must define.
 */


exports.FontBuilderState = FontBuilderState;
const requiredCharacters = /*#__PURE__*/C.map_( /*#__PURE__*/C.concat_( /*#__PURE__*/C.range(32, 126), /*#__PURE__*/C.from([196, 214, 220, 223, 228, 246, 252])), String.fromCharCode);
/**
 * Processes a line calling the appropriate action based on the current state.
 */

exports.requiredCharacters = requiredCharacters;

function processLine(state, line, index) {
  if (index === 0) {
    return buildHeader(state, line);
  }

  const commentLines = O.fold_(state.header, () => {
    throw new Error(`Bug! Attempting to access font header that does not exist`);
  }, _ => _.commentLines);

  if (index <= commentLines) {
    return buildComment(state, line);
  }

  if (state.processTaggedFonts) {
    return buildTaggedCharacter(state, line, index);
  }

  return buildCharacter(state, line, index);
}
/**
 * Build the `FigFont` by parsing the `FontBuilderState`.
 */


function buildFont(fontState) {
  if (C.size(fontState.loadedCharLines) !== 0) {
    return E.left(NA.single(new FE.FigCharacterError({
      message: "Incomplete character definition at the end of the file"
    })));
  }

  if (O.isNone(fontState.header)) {
    return E.left(NA.single(new FE.FigCharacterError({
      message: `Cannot build character without specifying a FigHeader`
    })));
  }

  const header = fontState.header.value;
  const nameV = E.right(path.parse(fontState.file).name);
  const fileV = E.right(fontState.file);
  const hashV = E.right(fontState.hash);
  const commentV = E.right(C.join_(fontState.commentLines, "\n"));
  const settingsV = E.map_(E.struct({
    hLayout: HorizontalLayout.fromHeader(header),
    vLayout: VerticalLayout.fromHeader(header),
    printDirection: PrintDirection.fromHeader(header)
  }), _ => new _index7.FigFontSettings(_));
  const charsV = E.map_(E.chain_(E.chain_(C.forEachF(FE.Applicative)(charState => buildChar(fontState, charState))(fontState.loadedChars), validateRequiredCharacters), chars => {
    const loadedTaggedCount = C.size(chars) - C.size(requiredCharacters);
    const codetagCount = O.getOrElse_(header.codeTagCount, () => loadedTaggedCount);

    if (loadedTaggedCount === codetagCount) {
      return E.right(chars);
    }

    return E.left(NA.single(new FE.FigFontError({
      message: `The number of loaded tagged fonts (${loadedTaggedCount}) ` + `does not correspond with the value indicated in the header ` + `(${codetagCount})`
    })));
  }), chars => Map.make(C.map_(chars, char => [char.name, char])));
  return E.map_(E.struct({
    id: hashV,
    name: nameV,
    file: fileV,
    header: E.right(header),
    comment: commentV,
    settings: settingsV,
    characters: charsV
  }), _ => new _definition.FigFont(_));
}
/**
 * Validates that all required characters are present for a given `FigFont`.
 */


function validateRequiredCharacters(characters) {
  const loadedCharset = Set.fromArray(Equal.string)(C.toArray(C.map_(characters, _ => _.name)));
  const missing = Set.toArray(Ord.string)(Set.difference_(Equal.string)(Set.fromArray(Equal.string)(C.toArray(requiredCharacters)), loadedCharset));

  if (A.isNonEmpty(missing)) {
    return E.left(NA.single(new FE.FigCharacterError({
      message: "Missing required character definitions for the following " + `Figlet characters: ${A.join_(missing, ", ")}`
    })));
  }

  return E.right(characters);
}
/**
 * Build the `FigFont` by parsing the character builder state.
 */


function buildChar(fontState, charState) {
  return O.fold_(fontState.header, () => E.left(NA.single(new FE.FigCharacterError({
    message: `Cannot build character without specifying a FigHeader`
  }))), header => FC.makeFromHeader(fontState.hash, header, charState.name, SL.fromValue(charState.lines), charState.comment, charState.position));
}
/**
 * Build the Figlet font header.
 */


function buildHeader(state, line) {
  return E.chain_(FH.fromLine(line), header => E.right(state.copy({
    header: O.some(header)
  })));
}
/**
 * Build the Figlet font comment section.
 */


function buildComment(state, line) {
  return E.right(state.copy({
    commentLines: C.append_(state.commentLines, line)
  }));
}
/**
 * Builds a `FigCharacter` using the given state and the current line.
 */


function buildCharacter(state, line, index) {
  if (O.isNone(state.header)) {
    return E.left(NA.single(new FE.FigCharacterError({
      message: `Cannot build character without specifying a FigHeader`
    })));
  }

  const header = state.header.value;
  const loadedCharLines = C.append_(state.loadedCharLines, line);
  const hash = crypto.createHash("md5").update(state.hash + crypto.createHash("md5").update(line).digest("hex")).digest("hex");

  if (C.size(state.loadedCharLines) + 1 < header.height) {
    return E.right(state.copy({
      loadedCharLines,
      hash
    }));
  }

  const startLine = index - C.size(state.loadedCharLines);
  const charNum = (startLine - header.commentLines - 1) / header.height;
  const charBuilder = new CharBuilderState({
    name: C.unsafeGet_(requiredCharacters, charNum),
    lines: C.append_(state.loadedCharLines, line),
    comment: O.none,
    position: startLine
  });
  return E.right(state.copy({
    hash,
    loadedCharLines: C.empty(),
    loadedNames: Set.insert_(Equal.string)(state.loadedNames, charBuilder.name),
    loadedChars: C.append_(state.loadedChars, charBuilder),
    processTaggedFonts: C.size(state.loadedChars) + 1 >= C.size(requiredCharacters)
  }));
}
/**
 * Builds a tagged `FigCharacter` using the given state and the current line.
 */


function buildTaggedCharacter(state, line, index) {
  if (O.isNone(state.header)) {
    return E.left(NA.single(new FE.FigCharacterError({
      message: `Cannot build character without specifying a FigHeader`
    })));
  }

  const header = state.header.value;
  const hash = crypto.createHash("md5").update(state.hash + crypto.createHash("md5").update(line).digest("hex")).digest("hex");

  if (state.loadedCharLines.length + 1 < header.height + 1) {
    const loadedCharLines = C.append_(state.loadedCharLines, line);
    return E.right(state.copy({
      loadedCharLines,
      hash
    }));
  }

  const tagLineIndex = index - C.size(state.loadedCharLines);
  const nameV = parseTagName(C.head(state.loadedCharLines), tagLineIndex);
  const commentV = parseTagComment(C.head(state.loadedCharLines));
  const loadedCharLines = C.append_(C.drop_(state.loadedCharLines, 1), line);
  return E.map_(E.struct({
    name: nameV,
    lines: E.right(loadedCharLines),
    comment: commentV,
    position: E.right(tagLineIndex)
  }), _ => {
    const charBuilder = new CharBuilderState(_);
    return state.copy({
      hash,
      loadedCharLines: C.empty(),
      loadedNames: Set.insert_(Equal.string)(state.loadedNames, charBuilder.name),
      loadedChars: C.append_(state.loadedChars, charBuilder)
    });
  });
}
/**
 * Parses the line of a tag to extract the name.
 */


function parseTagName(tagLine, tagLineIndex) {
  if (O.isNone(tagLine)) {
    return E.left(NA.single(new FE.FigCharacterError({
      message: "Cannot parse tag name from empty tag line"
    })));
  }

  const splitFontTag = C.from(tagLine.value.replace(/\s+/, "###").split("###"));
  return E.chain_(E.fromOption_(C.get_(splitFontTag, 0), () => NA.single(new FE.FigCharacterError({
    message: `Missing character code in the tag at line ${tagLineIndex + 1}: ` + `${tagLine}`
  }))), code => parseCharCode(tagLineIndex, code));
}
/**
 * Parses the line of a tag to extract the comment.
 */


function parseTagComment(tagLine) {
  if (O.isNone(tagLine)) {
    return E.left(NA.single(new FE.FigCharacterError({
      message: "Cannot parse tag name from empty tag line"
    })));
  }

  const splitFontTag = C.from(tagLine.value.replace(/\s+/, "###").split("###"));
  return E.map_(E.right(O.getOrElse_(C.get_(splitFontTag, 1), () => "")), O.some);
}
/**
 * Parses a character code into a character.
 */


function parseCharCode(index, code) {
  if (/^-?\d+$/.test(code)) {
    return E.right(Number.parseInt(code, 10).toString());
  }

  if (/^-?0x[0-9a-f]+$/.test(code.toLocaleLowerCase())) {
    return E.right(Number.parseInt(code.replace(/0x/g, ""), 16).toString());
  }

  if (/^-?0\d+$/.test(code)) {
    return E.right(Number.parseInt(code, 8).toString());
  }

  return E.left(NA.single(new FE.FigCharacterError({
    message: `Could not convert character code "${code}" defined at ` + `line ${index + 1} to integer`
  })));
}
//# sourceMappingURL=builder.js.map