/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import Foundation

internal func isRootTextContentEmpty(isEditorComposing: Bool, trim: Bool = true) -> Bool {
  if isEditorComposing {
    return false
  }

  var text = rootTextContent()
  if trim {
    text = text.trimmingCharacters(in: .whitespacesAndNewlines)
  }

  return text.isEmpty
}

internal func rootTextContent() -> String {
  guard let root = getRoot() else { return "" }

  return root.getTextContent()
}

internal func canShowPlaceholder(isComposing: Bool) -> Bool {
  if !isRootTextContentEmpty(isEditorComposing: isComposing, trim: false) {
    return false
  }

  guard let root = getRoot() else { return false }

  let children = root.getChildren()
  if children.count > 1 {
    return false
  }

  for childNode in children {
    guard let childNode = childNode as? ElementNode else { return true }

    if childNode.type != NodeType.paragraph {
      return false
    }

    let nodeChildren = childNode.getChildren()
    for nodeChild in nodeChildren {
      if !isTextNode(nodeChild) {
        return false
      }
    }
  }

  return true
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import Foundation

/* These functions will return a value when inside a read or update block. They should not be used when
 * not inside a read or update block (and will return nil in that case).
 */

public func getActiveEditor() -> Editor? {
  return Thread.current.threadDictionary[activeEditorThreadDictionaryKey] as? Editor
}

public func getActiveEditorState() -> EditorState? {
  return Thread.current.threadDictionary[activeEditorStateThreadDictionaryKey] as? EditorState
}

public func isReadOnlyMode() -> Bool {
  if let readOnlyMode = Thread.current.threadDictionary[readOnlyModeThreadDictionaryKey] as? Bool {
    return readOnlyMode
  }
  return true
}

internal func isEditorPresentInUpdateStack(_ editor: Editor) -> Bool {
  let updateEditors: [Editor] = Thread.current.threadDictionary[previousParentUpdateBlocksThreadDictionaryKey] as? [Editor] ?? []
  return updateEditors.contains(editor)
}

public func errorOnReadOnly() throws {
  if isReadOnlyMode() {
    throw LexicalError.invariantViolation("Editor should be in writeable state")
  }
}

public func triggerUpdateListeners(activeEditor: Editor, activeEditorState: EditorState, previousEditorState: EditorState, dirtyNodes: DirtyNodeMap) {
  for listener in activeEditor.listeners.update.values {
    listener(activeEditorState, previousEditorState, dirtyNodes)
  }
}

func triggerErrorListeners(activeEditor: Editor, activeEditorState: EditorState, previousEditorState: EditorState, error: Error) {
  for listener in activeEditor.listeners.errors.values {
    listener(activeEditorState, previousEditorState, error)
  }
}

public func triggerTextContentListeners(activeEditor: Editor, activeEditorState: EditorState, previousEditorState: EditorState) throws {
  let activeTextContent = try getEditorStateTextContent(editorState: activeEditorState)
  let previousTextContent = try getEditorStateTextContent(editorState: previousEditorState)

  if activeTextContent != previousTextContent {
    for listener in activeEditor.listeners.textContent.values {
      listener(activeTextContent)
    }
  }
}

public func triggerCommandListeners(activeEditor: Editor, type: CommandType, payload: Any?) -> Bool {
  let listenersInPriorityOrder = activeEditor.commands[type]

  var handled = false

  let closure: () throws -> Void = {
    for priority in [
      CommandPriority.Critical,
      CommandPriority.High,
      CommandPriority.Normal,
      CommandPriority.Low,
      CommandPriority.Editor,
    ] {
      guard let listeners = listenersInPriorityOrder?[priority]?.values else {
        continue
      }

      // TODO: handle throws
      for listener in listeners {
        if listener(payload) {
          handled = true
          return
        }
      }
    }
  }

  do {
    if !activeEditor.isUpdating {
      try activeEditor.update(closure)
    } else {
      try closure()
    }
  } catch {
    print("\(error)")
    return false
  }

  if handled { return true }

  if let parent = activeEditor.parentEditor {
    return triggerCommandListeners(activeEditor: parent, type: type, payload: payload)
  }

  // no parent, no handler
  return false
}

// MARK: - Private implementation

private let activeEditorThreadDictionaryKey = "kActiveEditor"
private let activeEditorStateThreadDictionaryKey = "kActiveEditorState"
private let readOnlyModeThreadDictionaryKey = "kReadOnlyMode"
private let previousParentUpdateBlocksThreadDictionaryKey = "kpreviousParentUpdateBlocks"

internal func runWithStateLexicalScopeProperties(activeEditor: Editor?, activeEditorState: EditorState?, readOnlyMode: Bool, closure: () throws -> Void) throws {
  let previousActiveEditor = Thread.current.threadDictionary[activeEditorThreadDictionaryKey]
  let previousActiveEditorState = Thread.current.threadDictionary[activeEditorStateThreadDictionaryKey]
  let previousReadOnly = Thread.current.threadDictionary[readOnlyModeThreadDictionaryKey]
  let previousParentUpdateBlocks: [Editor] = Thread.current.threadDictionary[previousParentUpdateBlocksThreadDictionaryKey] as? [Editor] ?? []

  Thread.current.threadDictionary[activeEditorThreadDictionaryKey] = activeEditor
  Thread.current.threadDictionary[activeEditorStateThreadDictionaryKey] = activeEditorState
  Thread.current.threadDictionary[readOnlyModeThreadDictionaryKey] = readOnlyMode

  if let activeEditor {
    var newParentUpdateBlocks = previousParentUpdateBlocks
    newParentUpdateBlocks.append(activeEditor)
    Thread.current.threadDictionary[previousParentUpdateBlocksThreadDictionaryKey] = newParentUpdateBlocks
  }

  try closure()

  Thread.current.threadDictionary[activeEditorThreadDictionaryKey] = previousActiveEditor
  Thread.current.threadDictionary[activeEditorStateThreadDictionaryKey] = previousActiveEditorState
  Thread.current.threadDictionary[readOnlyModeThreadDictionaryKey] = previousReadOnly
  Thread.current.threadDictionary[previousParentUpdateBlocksThreadDictionaryKey] = previousParentUpdateBlocks
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import Foundation
import UIKit

public func getNodeByKey<N: Node>(key: NodeKey) -> N? {
  guard let editorState = getActiveEditorState(), let node = editorState.nodeMap[key] else {
    return nil
  }
  return node as? N
}

@discardableResult
public func generateKey(node: Node) throws -> NodeKey? {
  try errorOnReadOnly()

  guard let editor = getActiveEditor(), let editorState = getActiveEditorState() else {
    return nil
  }

  while editorState.nodeMap[String(editor.keyCounter)] != nil {
    editor.keyCounter += 1
  }

  let key = editor.keyCounter
  editor.keyCounter += 1

  let stringKey = String(key)
  node.key = stringKey
  editorState.nodeMap[stringKey] = node
  editor.cloneNotNeeded.insert(stringKey)
  editor.dirtyType = .hasDirtyNodes
  editor.dirtyNodes[stringKey] = .editorInitiated
  return stringKey
}

private func internallyMarkChildrenAsDirty(  element: ElementNode,
                                             nodeMap: [NodeKey: Node],
                                             editor: Editor,
                                             status: DirtyStatusCause = .editorInitiated) {
  for childKey in element.children {
    editor.dirtyNodes[childKey] = status
    if let childElement = nodeMap[childKey] as? ElementNode {
      internallyMarkChildrenAsDirty(element: childElement, nodeMap: nodeMap, editor: editor)
    }
  }
}

private func internallyMarkParentElementsAsDirty(
  parentKey: NodeKey,
  nodeMap: [NodeKey: Node],
  editor: Editor,
  status: DirtyStatusCause = .editorInitiated) {
  var nextParentKey: NodeKey? = parentKey

  while let unwrappedParentKey = nextParentKey {
    if editor.dirtyNodes[unwrappedParentKey] != nil {
      return
    }

    let node = nodeMap[unwrappedParentKey]

    if node == nil {
      break
    }

    editor.dirtyNodes[unwrappedParentKey] = status
    nextParentKey = node?.parent
  }
} // Never use this function directly! It will break
// the cloning heuristic. Instead use node.getWritable().

internal func internallyMarkNodeAsDirty(node: Node, cause: DirtyStatusCause = .editorInitiated) {
  let latest = node.getLatest()
  guard
    let editorState = getActiveEditorState(),
    let editor = getActiveEditor()
  else {
    fatalError()
  }

  let nodeMap = editorState.nodeMap

  if let parent = latest.parent {
    internallyMarkParentElementsAsDirty(parentKey: parent, nodeMap: nodeMap, editor: editor)
  }
  if let elementNode = node as? ElementNode {
    internallyMarkChildrenAsDirty(element: elementNode, nodeMap: nodeMap, editor: editor)
  }

  editor.dirtyType = .hasDirtyNodes
  editor.dirtyNodes[latest.key] = cause
}

internal func internallyMarkSiblingsAsDirty(node: Node, status: DirtyStatusCause = .editorInitiated) {
  if let previousNode = node.getPreviousSibling() {
    internallyMarkNodeAsDirty(node: previousNode, cause: status)
  }
  if let nextNode = node.getNextSibling() {
    internallyMarkNodeAsDirty(node: nextNode, cause: status)
  }
}

// TODO: update this method when updateCaretSelectionForUnicodeCharacter is ported
// check if string contains utf-16 grapheme clusters: /[\uD800-\uDBFF][\uDC00-\uDFFF]/g
public func doesContainGrapheme(_ str: String) -> Bool {
  return false
}

public func getCompositionKey() -> NodeKey? {
  return getActiveEditor()?.compositionKey
}

public func createTextNode(text: String?) -> TextNode {
  TextNode(text: text ?? "", key: nil)
}

public func createParagraphNode() -> ParagraphNode {
  ParagraphNode()
}

public func createHeadingNode(headingTag: HeadingTagType) -> HeadingNode {
  HeadingNode(tag: headingTag)
}

public func createQuoteNode() -> QuoteNode {
  QuoteNode()
}

public func createLineBreakNode() -> LineBreakNode {
  LineBreakNode()
}

public func createCodeNode(language: String = "") -> CodeNode {
  CodeNode(language: language)
}

public func createCodeHighlightNode(text: String, highlightType: String?) -> CodeHighlightNode {
  CodeHighlightNode(text: text, highlightType: highlightType)
}

public func toggleTextFormatType(format: TextFormat, type: TextFormatType, alignWithFormat: TextFormat?) -> TextFormat {
  var activeFormat = format
  let isStateFlagPresent = format.isTypeSet(type: type)
  var flag = false

  if let alignWithFormat {
    // remove the type from format
    if isStateFlagPresent && !alignWithFormat.isTypeSet(type: type) {
      flag = false
    }

    // add the type to format
    if alignWithFormat.isTypeSet(type: type) {
      flag = true
    }
  } else {
    if isStateFlagPresent {
      flag = false
    } else {
      flag = true
    }
  }

  activeFormat.updateFormat(type: type, value: flag)

  return activeFormat
}

public func isElementNode(node: Node?) -> Bool {
  node is ElementNode
}

public func isTextNode(_ node: Node?) -> Bool {
  node is TextNode
}

public func isRootNode(node: Node?) -> Bool {
  node is RootNode
}

public func isHeadingNode(_ node: Node?) -> Bool {
  node is HeadingNode
}

public func isQuoteNode(_ node: Node?) -> Bool {
  node is QuoteNode
}

public func isCodeNode(_ node: Node?) -> Bool {
  node is CodeNode
}

public func isCodeHighlightNode(_ node: Node?) -> Bool {
  node is CodeHighlightNode
}

public func isLineBreakNode(_ node: Node?) -> Bool {
  node is LineBreakNode
}

public func isDecoratorNode(_ node: Node?) -> Bool {
  node is DecoratorNode
}

// TODO: - update function when we add LineBreakNode and DecoratorNode
public func isLeafNode(_ node: Node?) -> Bool {
  node is TextNode || node is LineBreakNode
}

public func isTokenOrInert(_ node: TextNode?) -> Bool {
  guard let node else { return false }
  return node.isToken() || node.isInert()
}

public func isTokenOrInertOrSegmented(_ node: TextNode?) -> Bool {
  guard let node else { return false }
  return isTokenOrInert(node) || node.isSegmented()
}

public func getRoot() -> RootNode? {
  guard let editorState = getActiveEditorState(),
        let rootNode = editorState.nodeMap[kRootNodeKey] as? RootNode
  else { return nil }

  return rootNode
}

func getEditorStateTextContent(editorState: EditorState) throws -> String {
  var textContent: String = ""

  try editorState.read {
    if let rootNode = getRoot() {
      textContent += rootNode.getTextContent()
    }
  }

  return textContent
}

public func getNodeHierarchy(editorState: EditorState?) throws -> String {
  var hierarchyString = ""
  var cacheString = ""
  var formatString = ""

  try editorState?.read {
    guard let editorState = getActiveEditorState(), let rootNode = editorState.getRootNode() else {
      throw LexicalError.invariantViolation("Need editor and state")
    }

    let sortedNodeMap = editorState.nodeMap.sorted(
      by: { $0.key.localizedStandardCompare($1.key) == .orderedAscending }
    )

    var currentNodes: [(Node, Int)] = [(rootNode, 0)]
    var description: [String] = []

    repeat {
      let (node, depth) = currentNodes.removeLast()

      let indentation = (0..<depth).map({ _ in "\t " }).joined(separator: "")

      if let textNode = node as? TextNode {
        if textNode.format.debugDescription != "" {
          formatString = "{ format: \(textNode.format.debugDescription) }"
        } else {
          formatString = ""
        }
        description.append("\(indentation)(\(node.key)) \(node.type.rawValue) \"\(textNode.getTextPart())\" \(formatString)")
      } else {
        description.append("\(indentation)(\(node.key)) \(node.type.rawValue)")
      }

      if let elementNode = node as? ElementNode {
        let childNodes = elementNode.children.map({ (getNodeByKey(key: $0) ?? Node(), depth + 1) }).reversed()
        currentNodes.append(contentsOf: childNodes)
      }
    } while !currentNodes.isEmpty

    hierarchyString = description.joined(separator: "\n")
    cacheString = sortedNodeMap.map({ node in "\(node.key): \(node.value.type)" }).joined(separator: ", ")
  }

  return "Tree:\n\(hierarchyString)\nCache:\n\(cacheString)"
}

public func getSelectionData(editorState: EditorState?) throws -> String {
  var selectionString = "selection: range\n"

  guard let debugDescription = editorState?.selection?.debugDescription else {
    throw LexicalError.invariantViolation("Need selection")
  }

  selectionString += debugDescription

  return selectionString
}

public func removeParentEmptyElements(startingNode: ElementNode?) throws {
  guard var node = startingNode else { return }

  while !(node is RootNode) {
    let latest = node.getLatest()

    let parentNode = try latest.getParentOrThrow()

    if latest.children.isEmpty {
      try node.remove()
    }

    node = parentNode
  }
}

func checkSelectionIsOnlyLinebreak(selection: RangeSelection) throws -> Bool {
  let nodes = try selection.getNodes()
  return nodes.count == 1 && nodes.contains(where: { $0 is LineBreakNode })
}

public func sliceSelectedTextNodeContent(selection: BaseSelection, textNode: TextNode) throws -> TextNode {
  if try textNode.isSelected(), !textNode.isSegmented(), !textNode.isToken(), let selection = selection as? RangeSelection {
    // && ($isRangeSelection(selection) || $isGridSelection(selection)){
    let anchorNode = try selection.anchor.getNode()
    let focusNode = try selection.focus.getNode()
    let isAnchor = textNode == anchorNode
    let isFocus = textNode == focusNode

    if isAnchor || isFocus {
      let isBackward = try selection.isBackward()
      let (anchorOffset, focusOffset) = selection.getCharacterOffsets(selection: selection)
      let isSame = anchorNode == focusNode
      let isFirst = textNode == (isBackward ? focusNode : anchorNode)
      let isLast = textNode == (isBackward ? anchorNode : focusNode)
      var startOffset = 0
      var endOffset: Int?

      if isSame {
        startOffset = anchorOffset > focusOffset ? focusOffset : anchorOffset
        endOffset = anchorOffset > focusOffset ? anchorOffset : focusOffset
      } else if isFirst {
        let offset = isBackward ? focusOffset : anchorOffset
        startOffset = offset
        endOffset = nil
      } else if isLast {
        let offset = isBackward ? anchorOffset : focusOffset
        startOffset = 0
        endOffset = offset
      }

      let text = textNode.getText_dangerousPropertyAccess() as NSString
      let length = text.length - startOffset - (text.length - (endOffset ?? text.length))

      let range = NSRange(location: startOffset, length: length)
      let subString = text.substring(with: range)
      textNode.setText_dangerousPropertyAccess(String(subString))
      return textNode
    }
  }
  return textNode
}

public func decoratorView(forKey key: NodeKey, createIfNecessary: Bool) -> UIView? {
  guard let editor = getActiveEditor() else {
    return nil
  }

  guard let cacheItem = editor.decoratorCache[key] else {
    editor.log(.editor, .warning, "Requested decorator view for a node not in the cache")
    return nil
  }

  switch cacheItem {
  case .needsCreation:
    guard let node = getNodeByKey(key: key) as? DecoratorNode else {
      editor.log(.editor, .warning, "Requested decorator view for a node that is not a decorator node")
      return nil
    }
    let newView = node.createView()
    node.decorate(view: newView)
    editor.decoratorCache[key] = DecoratorCacheItem.unmountedCachedView(newView)
    editor.log(.editor, .verbose, "Creating view (and setting to unmounted): key \(key)")
    return newView
  case .cachedView(let view):
    editor.log(.editor, .verbose, "Returning cached view: key \(key)")
    return view
  case .unmountedCachedView(let view):
    editor.log(.editor, .verbose, "Returning unmounted cached view: key \(key)")
    return view
  case .needsDecorating(let view):
    editor.log(.editor, .verbose, "Returning needs decorating cached view: key \(key)")
    return view
  }
}

internal func destroyCachedDecoratorView(forKey key: NodeKey) {
  guard let editor = getActiveEditor() else {
    return
  }
  editor.decoratorCache.removeValue(forKey: key)
}

public typealias FindFunction = (
  _ node: Node
) -> Bool

public func findMatchingParent(startingNode: Node?, findFn: FindFunction) -> Node? {
  var currentNode: Node? = startingNode

  while let curr = currentNode, curr != getRoot() {
    if findFn(curr) {
      return curr
    }

    currentNode = curr.getParent()
  }

  return nil
}

/**
 *Returns the element node of the nearest ancestor, otherwise throws an error.
 * @param startNode - The starting node of the search
 * @returns The ancestor node found
 */
public func getNearestBlockElementAncestorOrThrow(startNode: Node) throws -> ElementNode {
  let blockNode = findMatchingParent(startingNode: startNode) { node in
    if let elementNode = node as? ElementNode {
      return !elementNode.isInline()
    }
    return false
  }

  guard let blockNode = blockNode as? ElementNode else {
    throw LexicalError.invariantViolation("expected node to have closest block element node")
  }

  return blockNode
}

public func applyNodeReplacement<N: Node>(
  node: N
) throws -> N {
  // TODO: Lexical iOS doesn't support node replacement yet
  return node as N
}

/**
 * Takes a node and traverses up its ancestors (toward the root node)
 * in order to find a specific type of node.
 * @param node - the node to begin searching.
 * @param klass - an instance of the type of node to look for.
 * @returns the node of type klass that was passed, or null if none exist.
 */
public func getNearestNodeOfType<T: ElementNode>(
  node: Node,
  type: NodeType
) -> T? {
  var parent: Node? = node

  while let unwrappedParent = parent {
    if unwrappedParent.type == type, let typedParent = unwrappedParent as? T {
      return typedParent as T
    }

    parent = unwrappedParent.getParent()
  }

  return nil
}

public func hasAncestor(
  child: Node,
  targetNode: Node
) -> Bool {
  var parent = child.getParent()
  while let unwrappedParent = parent {
    if unwrappedParent.isSameNode(targetNode) {
      return true
    }
    parent = unwrappedParent.getParent()
  }
  return false
}

public func maybeMoveChildrenSelectionToParent(
  parentNode: Node,
  offset: Int = 0
) throws -> BaseSelection? {
  if offset != 0 {
    throw LexicalError.invariantViolation("TODO")
  }
  let selection = try getSelection()
  guard let selection = selection as? RangeSelection, let parentNode = parentNode as? ElementNode else {
    // Only works on range selection
    return selection
  }
  let anchorNode = try selection.anchor.getNode()
  let focusNode = try selection.focus.getNode()
  if hasAncestor(child: anchorNode, targetNode: parentNode) {
    selection.anchor.updatePoint(key: parentNode.getKey(), offset: 0, type: .element)
  }
  if hasAncestor(child: focusNode, targetNode: parentNode) {
    selection.focus.updatePoint(key: parentNode.getKey(), offset: 0, type: .element)
  }
  return selection
}

public func getAttributedStringFromFrontend() throws -> NSAttributedString {
  // @alexmattice - replace this with a version driven off a depth first search
  guard let editor = getActiveEditor() else { return NSAttributedString(string: "") }

  let selection = editor.getNativeSelection()

  if let range = selection.range, let textStorage = editor.textStorage {
    return textStorage.attributedSubstring(from: range)
  } else {
    return NSAttributedString(string: "")
  }
}

public func removeFromParent(node: Node) throws {
  guard let writableParent = try node.getParent()?.getWritable() else {
    return
  }

  internallyMarkSiblingsAsDirty(node: node, status: .userInitiated)

  writableParent.children.removeAll { childKey in
    childKey == node.getKey()
  }

  internallyMarkNodeAsDirty(node: writableParent)
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import UIKit

public class CodeHighlightNode: TextNode {
  enum CodingKeys: String, CodingKey {
    case highlightType
  }

  public var highlightType: String?

  override public init() {
    super.init()
    self.type = NodeType.codeHighlight
  }

  required init(text: String, highlightType: String?, key: NodeKey? = nil) {
    super.init(text: text, key: key)
    self.highlightType = highlightType
    self.type = NodeType.codeHighlight
  }

  public required init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    try super.init(from: decoder)

    self.highlightType = try container.decode(String.self, forKey: .highlightType)
    self.type = NodeType.codeHighlight
  }

  public required convenience init(text: String, key: NodeKey?) {
    self.init(text: text, highlightType: nil, key: key)
  }

  override public func encode(to encoder: Encoder) throws {
    try super.encode(to: encoder)
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(self.highlightType, forKey: .highlightType)
  }

  override public func clone() -> Self {
    return Self(text: self.getText_dangerousPropertyAccess(), highlightType: self.highlightType, key: key)
  }

  // Prevent formatting (bold, underline, etc)
  override public func setFormat(format: TextFormat) throws -> CodeHighlightNode {
    return try self.getWritable()
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import UIKit

// This is an ObjC class because it needs to conform to NSObject's equality, otherwise the Layout Manager
// can't iterate through attributes properly.
@objc public class CodeBlockCustomDrawingAttributes: NSObject {
  public init(background: UIColor, border: UIColor, borderWidth: CGFloat) {
    self.background = background
    self.border = border
    self.borderWidth = borderWidth
  }

  let background: UIColor
  let border: UIColor
  let borderWidth: CGFloat

  override public func isEqual(_ object: Any?) -> Bool {
    let lhs = self
    guard let rhs = object as? CodeBlockCustomDrawingAttributes else {
      return false
    }
    return lhs.background == rhs.background &&
      lhs.border == rhs.border &&
      lhs.borderWidth == rhs.borderWidth
  }
}

public extension NSAttributedString.Key {
  static let codeBlockCustomDrawing: NSAttributedString.Key = .init(rawValue: "codeBlockCustomDrawing")
}

public class CodeNode: ElementNode {
  enum CodingKeys: String, CodingKey {
    case language
  }

  private var language: String = ""

  override public init() {
    super.init()
    self.type = NodeType.code
  }

  required init(language: String, key: NodeKey? = nil) {
    super.init(key)
    self.language = language
    self.type = NodeType.code
  }

  public required init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    try super.init(from: decoder)

    self.language = try container.decode(String.self, forKey: .language)
    self.type = NodeType.code
  }

  override public func encode(to encoder: Encoder) throws {
    try super.encode(to: encoder)
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(self.language, forKey: .language)
  }

  public func getLanguage() -> String {
    let latest: CodeNode = getLatest()
    return latest.language
  }

  public func setLanguage(_ language: String) throws {
    try errorOnReadOnly()
    try getWritable().language = language
  }

  override func canInsertTab() -> Bool {
    return true
  }

  override public func clone() -> Self {
    Self(language: language, key: key)
  }

  override public func collapseAtStart(selection: RangeSelection) throws -> Bool {
    let paragraph = createParagraphNode()

    try getChildren().forEach { node in
      try paragraph.append([node])
    }

    try replace(replaceWith: paragraph)

    return true
  }

  override public func getAttributedStringAttributes(theme: Theme) -> [NSAttributedString.Key: Any] {
    var attributeDictionary = super.getAttributedStringAttributes(theme: theme)
    if let codeTheme = theme.code {
      attributeDictionary.merge(codeTheme) { (_, new) in new }
    } else {
      // a few defaults
      attributeDictionary[.paddingHead] = 4.0
      attributeDictionary[.paddingTail] = -4.0
    }

    if attributeDictionary[.codeBlockCustomDrawing] == nil {
      let customAttr = CodeBlockCustomDrawingAttributes(background: .lightGray, border: .gray, borderWidth: 1)
      attributeDictionary[.codeBlockCustomDrawing] = customAttr
    }

    return attributeDictionary
  }

  override open func insertNewAfter(selection: RangeSelection?) throws -> ParagraphNode? {
    guard let selection else {
      return nil
    }

    let children = self.getChildren()
    let childrenLength = children.count

    if childrenLength >= 2 &&
        children.last is LineBreakNode &&
        children[childrenLength - 2] is LineBreakNode &&
        selection.isCollapsed() &&
        selection.anchor.key == self.key &&
        selection.anchor.offset == childrenLength {
      try children[childrenLength - 1].remove()
      try children[childrenLength - 2].remove()
      let newElement = createParagraphNode()
      try self.insertAfter(nodeToInsert: newElement)
      return newElement
    } else {
      return nil
    }
  }
}

extension CodeNode {
  internal static var codeBlockBackgroundDrawing: CustomDrawingHandler {
    get {
      return { attributeKey, attributeValue, layoutManager, attributeRunCharacterRange, granularityExpandedCharacterRange, glyphRange, rect, firstLineFragment in
        guard let context = UIGraphicsGetCurrentContext(), let attributeValue = attributeValue as? CodeBlockCustomDrawingAttributes else { return }
        context.setFillColor(attributeValue.background.cgColor)
        context.fill(rect)

        context.setStrokeColor(attributeValue.border.cgColor)
        context.stroke(rect, width: attributeValue.borderWidth)
      }
    }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import UIKit

/**
 A node that renders an arbitrary `UIView` inline in the text.

 Behind the scenes, decorator nodes work by instructing TextKit to reserve some rectangular space, then
 creating and positioning a UIView inside that space. Lexical handles the lifecycle of this UIView.

 To make your own decorators, you must subclass `DecoratorNode`.

 ## State Handling

 It is recommended that state is stored within your Node. This will allow it to be correctly serialized, moved
 between Lexical instances, etc.

 Override the ``decorate(view:)`` method to apply state from your Node to your View. This will be called whenever
 your decorator node is reconciled when it is dirty. Therefore, assuming you correctly use ``Node/getWritable()`` to
 handle state updates within your node, then ``decorate(view:)`` will be called automatically whenever you change your
 node's properties.

 To handle communication from your View to your Node, e.g. tap handling or any other interaction, it is recommended
 that your View keeps a weak reference to its ``Editor``. This will require you to use a custom subclass of `UIView` of course.
 Set your view's Editor in ``decorate(view:)`` (it is safe to use ``getActiveEditor()`` in this method). Then in your view,
 you can call an ``Editor/update(_:)`` and either dispatch a command, or obtain and modify your node using ``getNodeByKey(key:)``.

 ## Documentation on using Decorators

 Read <doc:BuildingDecorators> for more information on how to build and use decorator nodes.

 ## Topics

 ### Key methods to override

 - ``createView()``
 - ``decorate(view:)``
 - ``sizeForDecoratorView(textViewWidth:)``

 ### Optional methods to override

 - ``decoratorWillAppear(view:)``
 - ``decoratorDidDisappear(view:)``

 */
open class DecoratorNode: Node {
  override public init() {
    super.init()
  }

  override public required init(_ key: NodeKey?) {
    super.init(key)
  }

  public required init(from decoder: Decoder) throws {
    try super.init(from: decoder)
  }

  override open func encode(to encoder: Encoder) throws {
    try super.encode(to: encoder)
  }

  override open func clone() -> Self {
    Self(key)
  }

  /// Create your `UIView` here.
  ///
  /// Do not add it to the view hierarchy or size it; Lexical will do that later.
  open func createView() -> UIView {
    fatalError("createView: base method not extended")
  }

  /// Called by Lexical when reconciling a dirty decorator node. This is where you update your view to match
  /// the state encapsulated in the decorator node.
  open func decorate(view: UIView) {
    fatalError("decorate: base method not extended")
  }

  open func decoratorWillAppear(view: UIView) {
    // no-op unless overridden
  }

  open func decoratorDidDisappear(view: UIView) {
    // no-op unless overridden
  }

  /// Calculate the size that your view should be. You can take into account the width of the text view,
  /// for example if you want to make a decorator that is always full width.
  open func sizeForDecoratorView(textViewWidth: CGFloat, attributes: [NSAttributedString.Key: Any]) -> CGSize {

    fatalError("sizeForDecoratorView: base method not extended")
  }

  public func isTopLevel() -> Bool {
    return false
  }

  public func isIsolated() -> Bool {
    return false
  }

  override public final func getPreamble() -> String {
    guard let unicodeScalar = Unicode.Scalar(NSTextAttachment.character) else {
      return ""
    }
    return String(Character(unicodeScalar))
  }

  override public func getAttributedStringAttributes(theme: Theme) -> [NSAttributedString.Key: Any] {
    let textAttachment = TextAttachment()

    guard let editor = getActiveEditor() else { return [:] }

    textAttachment.editor = editor
    textAttachment.key = key

    return [.attachment: textAttachment]
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import Foundation

open class ElementNode: Node {
  enum CodingKeys: String, CodingKey {
    case children
    case direction
    case indent
    case format // text alignment. Not supported yet.
  }

  // TODO: once the various accessor methods are written, make this var private
  var children: [NodeKey] = []
  var direction: Direction?
  var indent: Int = 0

  func getDirection() -> Direction? {
    return direction
  }

  override public init() {
    super.init()
  }

  override public init(_ key: NodeKey?) {
    super.init(key)
  }

  public required init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    self.children = []
    var childNodes: [Node] = []

    guard let editor = getActiveEditor() else {
      throw LexicalError.internal("Could not get active editor")
    }

    do {
      let deserializationMap = editor.registeredNodes
      var childrenUnkeyedContainer = try container.nestedUnkeyedContainer(forKey: .children)

      while !childrenUnkeyedContainer.isAtEnd {
        var containerCopy = childrenUnkeyedContainer
        let unprocessedContainer = try childrenUnkeyedContainer.nestedContainer(keyedBy: PartialCodingKeys.self)
        let type = try NodeType(rawValue: unprocessedContainer.decode(String.self, forKey: .type))

        let klass = deserializationMap[type] ?? UnknownNode.self

        do {
          let decoder = try containerCopy.superDecoder()
          let decodedNode = try klass.init(from: decoder)
          childNodes.append(decodedNode)
          self.children.append(decodedNode.key)
        } catch {
          print(error)
        }
      }
    } catch {
      print(error)
    }

    self.direction = try container.decodeIfPresent(Direction.self, forKey: .direction)
    self.indent = try container.decodeIfPresent(Int.self, forKey: .indent) ?? 0
    try super.init(from: decoder)

    for node in childNodes {
      node.parent = self.key
    }
  }

  override open func encode(to encoder: Encoder) throws {
    try super.encode(to: encoder)
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(self.getChildren(), forKey: .children)
    try container.encode(self.direction, forKey: .direction)
    try container.encode(self.indent, forKey: .indent)
    try container.encode("", forKey: .format)
  }

  @discardableResult
  func setDirection(direction: Direction?) throws -> ElementNode {
    try errorOnReadOnly()
    let node = try getWritable() as ElementNode
    node.direction = direction
    return node
  }

  open func canIndent() -> Bool {
    return true
  }

  open func getIndent() -> Int {
    let node = getLatest() as ElementNode
    return node.indent
  }

  @discardableResult
  open func setIndent(_ indent: Int) throws -> ElementNode {
    try errorOnReadOnly()
    let node = try getWritable() as ElementNode
    node.indent = indent
    return node
  }

  open func append(_ nodesToAppend: [Node]) throws {
    try errorOnReadOnly()
    let writeableSelf: ElementNode = try self.getWritable()
    let writeableSelfKey = writeableSelf.key
    var writeableSelfChildren = writeableSelf.children
    if let lastChild = self.getLastChild() {
      internallyMarkNodeAsDirty(node: lastChild, cause: .userInitiated)
    }

    for node in nodesToAppend {
      let writeableNodeToAppend = try node.getWritable()

      // Remove node from previous parent
      if let oldParent = writeableNodeToAppend.getParent() {
        let writeableParent = try oldParent.getWritable()
        guard let index = writeableParent.children.firstIndex(of: writeableNodeToAppend.key) else {
          throw LexicalError.invariantViolation("Node is not a child of its parent")
        }

        writeableParent.children.remove(at: index)
      }

      // Set child parent to self
      writeableNodeToAppend.parent = writeableSelfKey

      // Append children.
      let newKey = writeableNodeToAppend.key
      writeableSelfChildren.append(newKey)
    }

    writeableSelf.children = writeableSelfChildren
  }

  public func getFirstChild<T: Node>() -> T? {
    let children = getLatest().children

    if children.count == 0 {
      return nil
    }

    guard let firstChild = children.first else { return nil }

    return getNodeByKey(key: firstChild)
  }

  public func getLastChild() -> Node? {
    let children = getLatest().children

    if children.count == 0 {
      return nil
    }

    return getNodeByKey(key: children[children.count - 1])
  }

  public func getChildrenSize() -> Int {
    let latest = getLatest() as ElementNode
    return latest.children.count
  }

  public func getChildAtIndex(index: Int) -> Node? {
    let children = self.children
    if index >= 0 && index < children.count {
      let key = children[index]
      return getNodeByKey(key: key)
    } else {
      return nil
    }
  }

  public func getDescendantByIndex(index: Int) -> Node? {
    let children = getChildren()

    if index >= children.count {
      if let resolvedNode = children.last as? ElementNode,
         let lastDescendant = resolvedNode.getLastDescendant() {
        return lastDescendant
      }

      return children.last
    }

    if let node = children[index] as? ElementNode, let firstDescendant = node.getFirstDescendant() {
      return firstDescendant
    }

    return children[index]
  }

  public func getFirstDescendant() -> Node? {
    var node: Node? = self.getFirstChild()
    while let unwrappedNode = node {
      if let child = (unwrappedNode as? ElementNode)?.getFirstChild() {
        node = child
      } else {
        break
      }
    }

    return node
  }

  public func getLastDescendant() -> Node? {
    var node = self.getLastChild()
    while let unwrappedNode = node {
      if let child = (unwrappedNode as? ElementNode)?.getLastChild() {
        node = child
      } else {
        break
      }
    }

    return node
  }

  func canInsertTab() -> Bool {
    return false
  }

  open func collapseAtStart(selection: RangeSelection) throws -> Bool {
    return false
  }

  public func excludeFromCopy(destination: Destination? = nil) -> Bool {
    return false
  }

  func canExtractContents() -> Bool {
    return true
  }

  func canReplaceWith(replacement: Node) -> Bool {
    return true
  }

  func canInsertAfter(node: Node) -> Bool {
    return true
  }

  open func canBeEmpty() -> Bool {
    return true
  }

  open func canInsertTextBefore() -> Bool {
    return true
  }

  open func canInsertTextAfter() -> Bool {
    return true
  }

  open func isInline() -> Bool {
    return false
  }

  func canSelectionRemove() -> Bool {
    return true
  }

  public func canMergeWith(node: ElementNode) -> Bool {
    return false
  }

  public func extractWithChild(
    child: Node,
    selection: BaseSelection?,
    destination: Destination) -> Bool {
    return false
  }

  public func getChildren() -> [Node] {
    return getLatest().children.compactMap { nodeKey in
      getNodeByKey(key: nodeKey)
    }
  }

  public func getChildrenKeys() -> [NodeKey] {
    let latest: ElementNode = getLatest()
    return latest.children
  }

  // Element nodes can't have a text part. Making this final so subclasses are bound by that rule.
  override public final func getTextPart() -> String {
    return ""
  }

  override public func getPreamble() -> String {
    if self.isInline() {
      return ""
    }

    guard let prevSibling = getPreviousSibling() else {
      return ""
    }

    guard prevSibling is ElementNode else {
      // prev is not an element node. Treat it as inline (TODO: inline handling in decorators)
      // Since prev is inline but not an element node, and we're not inline, return a newline
      return "\n"
    }

    // note that if prev is an element node (inline or not), it'll handle the newline.
    return ""
  }

  override public func getPostamble() -> String {
    let nextSibling = getNextSibling()

    if nextSibling == nil {
      // we have no next sibling, return "" no matter whether we're inline or not
      return ""
    } else if isInline() {
      if let nextSiblingAsElement = nextSibling as? ElementNode, !nextSiblingAsElement.isInline() {
        // we're inline but the next sibling is an element but is not inline
        return "\n"
      } else {
        // we're inline, next sibling is either a text node or inline
        return ""
      }
    } else {
      // we're not inline
      return "\n"
    }
  }

  public func getAllTextNodes(includeInert: Bool = false) -> [TextNode] {
    var textNodes = [TextNode]()
    let node = getLatest() as ElementNode

    for child in node.children {
      guard let childNode = getNodeByKey(key: child) else { return textNodes }

      if let childNode = childNode as? TextNode {
        if includeInert || !childNode.isInert() {
          textNodes.append(childNode)
        }
      } else if let childNode = childNode as? ElementNode {
        let subChildrenNodes = childNode.getAllTextNodes(includeInert: includeInert)
        textNodes.append(contentsOf: subChildrenNodes)
      }
    }

    return textNodes
  }

  override public func getTextContent(includeInert: Bool = false, includeDirectionless: Bool = false) -> String {
    let children = getChildren()
    let preamble = getPreamble()
    let postamble = getPostamble()
    var textContent = ""

    textContent += preamble

    for child in children {
      textContent += child.getTextContent(includeInert: includeInert, includeDirectionless: includeDirectionless)
      if child is LineBreakNode {
        textContent += child.getPostamble()
      }
    }

    textContent += postamble

    return textContent
  }

  // MARK: - Mutators
  @discardableResult
  public func select(anchorOffset: Int?, focusOffset: Int?) throws -> RangeSelection {
    try errorOnReadOnly()

    let selection = try getSelection()
    let childrenCount = getChildrenSize()
    var updatedAnchorOffset = childrenCount
    var updatedFocusOffset = childrenCount

    if let anchorOffset {
      updatedAnchorOffset = anchorOffset
    }

    if let focusOffset {
      updatedFocusOffset = focusOffset
    }

    guard let selection = selection as? RangeSelection else {
      return try makeRangeSelection(
        anchorKey: key,
        anchorOffset: updatedAnchorOffset,
        focusKey: key,
        focusOffset: updatedFocusOffset,
        anchorType: .element,
        focusType: .element)
    }

    selection.anchor.updatePoint(key: key, offset: updatedAnchorOffset, type: .element)
    selection.focus.updatePoint(key: key, offset: updatedFocusOffset, type: .element)
    selection.dirty = true

    return selection
  }

  public func isEmpty() -> Bool {
    return getChildrenSize() == 0
  }

  // These are intended to be extends for specific element heuristics.
  open func insertNewAfter(selection: RangeSelection?) throws -> Node? {
    throw LexicalError.internal("Subclasses need to implement this method")
  }

  @discardableResult
  public func selectStart() throws -> RangeSelection {
    let firstNode = getFirstDescendant()
    if let node = firstNode as? ElementNode {
      return try node.select(anchorOffset: 0, focusOffset: 0)
    }

    if let node = firstNode as? TextNode {
      return try node.select(anchorOffset: 0, focusOffset: 0)
    }
    if let firstNode {
      return try firstNode.selectPrevious(anchorOffset: nil, focusOffset: nil)
    }
    return try select(anchorOffset: 0, focusOffset: 0)
  }

  @discardableResult
  public func selectEnd() throws -> RangeSelection {
    if let lastNode = getLastDescendant() {
      if let elementNode = lastNode as? ElementNode {
        return try elementNode.select(anchorOffset: nil, focusOffset: nil)
      }

      if let textNode = lastNode as? TextNode {
        return try textNode.select(anchorOffset: nil, focusOffset: nil)
      }

      // Decorator or LineBreak
      // selectNext()
    }

    return try select(anchorOffset: nil, focusOffset: nil)
  }

  @discardableResult
  func clear() throws -> ElementNode {
    try errorOnReadOnly()

    let writableSelf = try getWritable()

    let children = writableSelf.getChildren()
    _ = try children.map({ try $0.remove() })

    return writableSelf
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import Foundation

public enum HeadingTagType: String, Codable {
  case h1
  case h2
  case h3
  case h4
  case h5
}

enum HeadingDefaultFontSize: Float {
  case h1 = 36
  case h2 = 32
  case h3 = 28
  case h4 = 24
  case h5 = 20
}

public class HeadingNode: ElementNode {
  enum CodingKeys: String, CodingKey {
    case tag
  }

  private var tag: HeadingTagType

  // MARK: - Init

  public init(tag: HeadingTagType) {
    self.tag = tag

    super.init()
    self.type = NodeType.heading
  }

  public required init(_ key: NodeKey?, tag: HeadingTagType) {
    self.tag = tag
    super.init(key)
    self.type = NodeType.heading
  }

  public required init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    self.tag = try container.decode(HeadingTagType.self, forKey: .tag)
    try super.init(from: decoder)

    self.type = NodeType.heading
  }

  override public func encode(to encoder: Encoder) throws {
    try super.encode(to: encoder)
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(self.tag, forKey: .tag)
  }

  public func getTag() -> HeadingTagType {
    tag
  }

  override public func clone() -> Self {
    Self(key, tag: tag)
  }

  override public func getAttributedStringAttributes(theme: Theme) -> [NSAttributedString.Key: Any] {
    switch tag {
    case .h1:
      return theme.getValue(.heading, withSubtype: "h1") ?? [.fontSize: HeadingDefaultFontSize.h1.rawValue]
    case .h2:
      return theme.getValue(.heading, withSubtype: "h2") ?? [.fontSize: HeadingDefaultFontSize.h2.rawValue]
    case .h3:
      return theme.getValue(.heading, withSubtype: "h3") ?? [.fontSize: HeadingDefaultFontSize.h3.rawValue]
    case .h4:
      return theme.getValue(.heading, withSubtype: "h4") ?? [.fontSize: HeadingDefaultFontSize.h4.rawValue]
    case .h5:
      return theme.getValue(.heading, withSubtype: "h5") ?? [.fontSize: HeadingDefaultFontSize.h5.rawValue]
    }
  }

  // MARK: - Mutation

  override open func insertNewAfter(selection: RangeSelection?) throws -> Node? {
    let newElement = createParagraphNode()

    try newElement.setDirection(direction: getDirection())
    try insertAfter(nodeToInsert: newElement)

    return newElement
  }

  override public func collapseAtStart(selection: RangeSelection) throws -> Bool {
    let paragraph = createParagraphNode()

    try getChildren().forEach { node in
      try paragraph.append([node])
    }

    try replace(replaceWith: paragraph)

    return true
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

public class LineBreakNode: Node {
  override public init() {
    super.init()
    self.type = NodeType.linebreak
  }

  override required init(_ key: NodeKey?) {
    super.init(key)
    self.type = NodeType.linebreak
  }

  public required init(from decoder: Decoder) throws {
    try super.init(from: decoder)
    self.type = NodeType.linebreak
  }

  override public func encode(to encoder: Encoder) throws {
    try super.encode(to: encoder)
  }

  override public func clone() -> Self {
    Self(key)
  }

  override public func getPostamble() -> String {
    return "\n"
  }

  public func createLineBreakNode() -> LineBreakNode {
    return LineBreakNode()
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import UIKit

public typealias NodeKey = String

/// The base class for all Lexical nodes to inherit from.
///
/// This class provides various methods for reading and manipulating the node tree, as well as node lifecycle support.
///
/// If you're creating your own node class, typically you would inherit from ``TextNode``, ``DecoratorNode`` or ``ElementNode``, rather than directly inheriting from ``Node``.
open class Node: Codable {
  enum CodingKeys: String, CodingKey {
    case type
    case version
  }

  public var key: NodeKey
  var parent: NodeKey?
  public var type: NodeType
  public var version: Int

  public init() {
    self.type = Node.getType()
    self.version = 1
    self.key = LexicalConstants.uninitializedNodeKey

    _ = try? generateKey(node: self)
  }

  public init(_ key: NodeKey?) {
    self.type = Node.getType()
    self.version = 1

    if let key, key != LexicalConstants.uninitializedNodeKey {
      self.key = key
    } else {
      self.key = LexicalConstants.uninitializedNodeKey
      _ = try? generateKey(node: self)
    }
  }

  /// Used when initialising node from JSON
  public required init(from decoder: Decoder) throws {
    let values = try decoder.container(keyedBy: CodingKeys.self)
    key = LexicalConstants.uninitializedNodeKey
    type = try NodeType(rawValue: values.decode(String.self, forKey: .type))
    version = try values.decode(Int.self, forKey: .version)

    _ = try? generateKey(node: self)
  }

  /// Used when serialising node to JSON
  open func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(self.type.rawValue, forKey: .type)
    try container.encode(self.version, forKey: .version)
  }

  /**
   Called whenever the node is moved to a new editor, e.g. when initialising an editor
   with an existing editor state.
   */
  open func didMoveTo(newEditor editor: Editor) {}

  // This is an initial value for `type`.
  // static methods cannot be overridden in swift so,
  // each subclass needs to assign the type property in their init method
  static func getType() -> NodeType {
    NodeType.unknown
  }

  /// Provides the **preamble** part of the node's content. Typically the preamble is used for control characters to represent embedded objects (see ``DecoratorNode``).
  ///
  /// In Lexical iOS, a node's content is split into four parts: preamble, children, text, postamble. ``ElementNode`` subclasses can implement preamble/postamble, and TextNode subclasses can implement the text part.
  public func getPreamble() -> String {
    return ""
  }

  /// Provides the **postamble** part of the node's content. Typically the postamble is used for paragraph-trailing newlines.
  ///
  /// In Lexical iOS, a node's content is split into four parts: preamble, children, text, postamble. ``ElementNode`` subclasses can implement preamble/postamble, and TextNode subclasses can implement the text part.
  public func getPostamble() -> String {
    return ""
  }

  /// Provides the **text** part of the node's content. The text part of a node represents the text this node is providing (but not including the text of any children).
  ///
  /// In Lexical iOS, a node's content is split into four parts: preamble, children, text, postamble. ``ElementNode`` subclasses can implement preamble/postamble, and TextNode subclasses can implement the text part.
  public func getTextPart() -> String {
    return ""
  }

  // Returns the length of the text part (as UTF 16 codepoints). Note that all string lengths within Lexical work using UTF 16 codepoints, because that is what TextKit uses.
  func getTextPartSize() -> Int {
    return getTextPart().lengthAsNSString()
  }

  /// Returns true if this node has been marked dirty during this update cycle.
  func isDirty() -> Bool {
    guard let editor = getActiveEditor() else {
      fatalError()
    }
    return editor.dirtyNodes[key] != nil
  }

  /// Returns the latest version of the node from the active EditorState. This is used to avoid getting values from stale node references.
  public func getLatest() -> Self {
    guard let latest: Self = getNodeByKey(key: key) else {
      fatalError()
    }
    return latest
  }

  /// Clones this node, creating a new node with a different key and adding it to the EditorState (but not attaching it anywhere!). All nodes must implement this method.
  open func clone() -> Self {
    fatalError("LexicalNode: Node \(String(describing: self)) does not implement .clone().")
  }

  /// Lets the node provide attributes for TextKit to use to render the node's content.
  open func getAttributedStringAttributes(theme: Theme) -> [NSAttributedString.Key: Any] {
    [:]
  }

  /**
   Attributes that apply to an entire block.

   This is conceptually not a thing in TextKit, so we had to build our own solution. Note that a block
   is an element or decorator that is not inline. The values of the block level attributes are applied
   to the relevant paragraph style for the first or last paragraph within the node. (Paragraph is here
   used to refer to a TextKit paragraph, i.e. some text separated by newlines. It's nothing to do with
   Lexical's paragraph nodes!)
   */
  open func getBlockLevelAttributes(theme: Theme) -> BlockLevelAttributes? {
    return theme.getBlockLevelAttributes(self.type)
  }

  /// Returns a mutable version of the node. Will throw an error if called outside of a Lexical Editor ``Editor/update(_:)`` callback.
  public func getWritable() throws -> Self {
    try errorOnReadOnly()

    guard let editor = getActiveEditor(), let editorState = getActiveEditorState() else {
      throw LexicalError.invariantViolation("LexicalNode: Could not get active editor for \(String(describing: self)).")
    }

    let latestNode = getLatest()

    if editor.cloneNotNeeded.contains(key) {
      // Transforms clear the dirty node set on each iteration to keep track on newly dirty nodes
      internallyMarkNodeAsDirty(node: latestNode, cause: .userInitiated)
      return latestNode
    }

    let mutableNode = getLatest().clone()

    mutableNode.parent = latestNode.parent
    if let latestNode = latestNode as? ElementNode, let mutableNode = mutableNode as? ElementNode {
      mutableNode.children = latestNode.children
    } else if let latestNode = latestNode as? TextNode, let mutableNode = mutableNode as? TextNode {
      mutableNode.format = latestNode.format
      mutableNode.mode = latestNode.mode
    }

    editor.cloneNotNeeded.insert(key)
    mutableNode.key = key
    internallyMarkNodeAsDirty(node: mutableNode, cause: .userInitiated)

    editorState.nodeMap[key] = mutableNode
    return mutableNode
  }

  /// Returns the zero-based index of this node within the parent.
  public func getIndexWithinParent() -> Int? {
    guard let parent = self.getParent() else {
      return nil
    }

    return parent.children.firstIndex(of: self.key)
  }

  /// Returns the parent of this node, or nil if none is found.
  public func getParent() -> ElementNode? {
    guard let parent = getLatest().parent else { return nil }

    return getNodeByKey(key: parent)
  }

  /// Returns a list of the keys of every ancestor of this node, all the way up to the RootNode.
  public func getParentKeys() -> [NodeKey] {
    var parents: [NodeKey] = []
    var node = self.getParent()

    while let unwrappedNode = node {
      parents.append(unwrappedNode.key)
      node = unwrappedNode.getParent()
    }

    return parents
  }

  /// Returns the highest (in the ``EditorState`` tree) non-root ancestor of this node, or null if none is found.
  ///
  /// Lexical JS has the concept of 'shadow roots', but this has not been implemented in Lexical iOS yet.
  public func getTopLevelElement() -> ElementNode? {
    var node = getNodeByKey(key: key)

    while node != nil {
      let parent = node?.getParent()
      if isRootNode(node: parent) && isElementNode(node: node) {
        return node as? ElementNode
      }

      node = parent
    }

    return nil
  }

  /// Returns the highest (in the EditorState tree) non-root ancestor of this node, or throws if none is found.
  public func getTopLevelElementOrThrow() -> ElementNode {
    guard let parent = getTopLevelElement() else {
      fatalError("Expected node \(key) to have a top parent element.")
    }

    return parent
  }

  /// Returns a list of the every ancestor of this node, all the way up to the RootNode.
  public func getParents() -> [ElementNode] {
    var parents: [ElementNode] = []
    var node = self.getParent()
    while let unwrappedNode = node {
      parents.append(unwrappedNode)
      node = unwrappedNode.getParent()
    }

    return parents
  }

  /// Returns the closest common ancestor of this node and the provided one or nil if one cannot be found.
  public func getCommonAncestor(node: Node) -> ElementNode? {
    var a = getParents()
    var b = node.getParents()

    if isElementNode(node: self) {
      if let elementNode = self as? ElementNode {
        a.insert(elementNode, at: 0)
      }
    }

    if isElementNode(node: node) {
      if let elementNode = node as? ElementNode {
        b.insert(elementNode, at: 0)
      }
    }

    if a.count == 0 || b.count == 0 || a.last !== b.last {
      return nil
    }

    let bSet = Set(b)
    for (index, _) in a.enumerated() {
      let ancestor = a[index]
      if bSet.contains(ancestor) {
        return ancestor
      }
    }
    return nil
  }

  /// Returns the "previous" siblings - that is, the node that comes before this one in the same parent.
  public func getPreviousSibling() -> Node? {
    guard let parent = self.getParent() else { return nil }

    guard let index = parent.children.firstIndex(of: self.key) else {
      return nil
    }

    let childrenIndex = index - 1

    if childrenIndex < 0 {
      return nil
    }

    return getNodeByKey(key: parent.children[childrenIndex])
  }

  /// Returns the "next" sibling - that is, the node that comes after this one in the same parent
  public func getNextSibling() -> Node? {
    guard let parent = self.getParent() else { return nil }

    guard let index = parent.children.firstIndex(of: self.key) else {
      return nil
    }

    if index >= parent.children.count - 1 {
      return nil
    }

    let childrenIndex = index + 1

    return getNodeByKey(key: parent.children[childrenIndex])
  }

  /// Returns the "previous" siblings - that is, the nodes that come between this one and the first child of it's parent, inclusive.
  public func getPreviousSiblings() -> [Node] {
    guard let parent = getParent() else { return [] }

    let children = parent.children
    guard let index = children.firstIndex(of: key) else { return [] }

    let siblings = children[0..<index]
    return siblings.compactMap({ childKey in
      getNodeByKey(key: childKey)
    })
  }

  /// Returns all "next" siblings - that is, the nodes that come between this one and the last child of its parent, inclusive.
  public func getNextSiblings() -> [Node] {
    guard let parent = getParent() else { return [] }

    let children = parent.children
    if children.count == 1 {
      return []
    }

    guard let index = children.firstIndex(of: key) else { return [] }

    let siblings = children[(index + 1)...]
    return siblings.compactMap { childKey in
      getNodeByKey(key: childKey)
    }
  }

  /// Returns a list of nodes that are between this node and the target node in the EditorState.
  public func getNodesBetween(targetNode: Node) -> [Node] {
    let isBefore = isBefore(targetNode)
    var nodes = [Node]()
    var visited = Set<NodeKey>()
    var node = self
    var dfsAncestor: Node?

    while true {
      if !visited.contains(node.key) {
        visited.insert(node.key)
        nodes.append(node)
      }

      if node == targetNode {
        break
      }

      let elementNode = node as? ElementNode
      let child = isElementNode(node: node)
        ? isBefore
        ? elementNode?.getFirstChild()
        : elementNode?.getLastChild()
        : nil

      if child != nil {
        if dfsAncestor == nil {
          dfsAncestor = node
        }

        if let child {
          node = child
        }

        continue
      }

      let nextSibling = isBefore ? node.getNextSibling() : node.getPreviousSibling()

      if let nextSibling {
        node = nextSibling
        continue
      }

      let parent = node.getParent()

      if let parent {
        if !visited.contains(parent.key) {
          nodes.append(parent)
        }
      }

      if parent == targetNode {
        break
      }

      var parentSibling: Node?
      var ancestor = parent

      if let parent {
        if parent.isSameKey(dfsAncestor) {
          dfsAncestor = nil
        }
      }

      repeat {
        if ancestor == nil {
          fatalError("getNodesBetween: ancestor is nil")
        }

        parentSibling = isBefore ? ancestor?.getNextSibling() : ancestor?.getPreviousSibling()
        ancestor = ancestor?.getParent()

        if ancestor != nil {
          if let ancestor {
            if ancestor.isSameKey(dfsAncestor) {
              dfsAncestor = nil
            }

            if parentSibling == nil && !visited.contains(ancestor.key) {
              nodes.append(ancestor)
            }
          }
        }
      } while parentSibling == nil

      if let parentSibling {
        node = parentSibling
      }
    }

    if !isBefore {
      nodes.reverse()
    }

    return nodes
  }

  func isSameKey(_ object: Node?) -> Bool {
    guard let object else { return false }

    return getKey() == object.getKey()
  }

  public func getKey() -> NodeKey {
    return key
  }

  func isBefore(_ targetNode: Node) -> Bool {
    if targetNode.isParentOf(self) {
      return true
    }

    if isParentOf(targetNode) {
      return false
    }

    let commonAncestor = getCommonAncestor(node: targetNode)

    return getChildIndex(commonAncestor: commonAncestor, node: self) <
      getChildIndex(commonAncestor: commonAncestor, node: targetNode)
  }

  func getChildIndex(commonAncestor: ElementNode?, node: Node) -> Int {
    var index = 0
    var nodeTemp = node

    while true {
      if let parent = nodeTemp.getParent() {
        if parent == commonAncestor, let childIndex = parent.children.firstIndex(of: nodeTemp.key) {
          index = childIndex
          break
        }

        nodeTemp = parent
      }
    }

    return index
  }

  func isParentOf(_ targetNode: Node) -> Bool {
    var node: Node?

    if key == targetNode.key {
      return false
    }

    node = targetNode

    while node != nil {
      if node?.key == key {
        return true
      }

      node = node?.getParent()
    }

    return false
  }

  /// Returns the parent of this node, or throws if none is found.
  public func getParentOrThrow() throws -> ElementNode {
    guard let parent = getParent() else {
      throw LexicalError.invariantViolation("Expected node \(key) to have a parent.")
    }

    return parent
  }

  /// Returns the text content of the node, typically including its children.
  ///
  /// This is different from ``getTextPart()``, which just returns the text provided by this node.
  public func getTextContent(includeInert: Bool = false, includeDirectionless: Bool = false) -> String {
    return ""
  }

  /// Returns the length of the string produced by calling getTextContent on this node.
  public func getTextContentSize(includeInert: Bool = false, includeDirectionless: Bool = false) -> Int {
    return getTextContent(
      includeInert: includeInert,
      includeDirectionless: includeDirectionless
    ).lengthAsNSString()
  }

  /// Removes this LexicalNode from the EditorState. If the node isn't re-inserted somewhere, the Lexical garbage collector will eventually clean it up.
  open func remove() throws {
    try errorOnReadOnly()
    try Node.removeNode(nodeToRemove: self, restoreSelection: true)
  }

  public static func removeNode(nodeToRemove: Node, restoreSelection: Bool) throws {
    try errorOnReadOnly()
    let key = nodeToRemove.key
    guard let parent = nodeToRemove.getParent() else {
      return
    }

    // if the selection is in one of the node to remove's children, move it into the node to remove.
    let selection = try maybeMoveChildrenSelectionToParent(parentNode: nodeToRemove)

    var selectionMoved = false
    if let selection = selection as? RangeSelection, restoreSelection {
      let anchor = selection.anchor
      let focus = selection.focus
      if anchor.key == key {
        moveSelectionPointToSibling(point: anchor, node: nodeToRemove, parent: parent)
        selectionMoved = true
      }
      if focus.key == key {
        moveSelectionPointToSibling(point: focus, node: nodeToRemove, parent: parent)
        selectionMoved = true
      }
    }

    let writeableParent = try parent.getWritable()
    guard let index = writeableParent.children.firstIndex(of: key) else {
      throw LexicalError.invariantViolation("Node is not a child of its parent")
    }

    internallyMarkNodeAsDirty(node: nodeToRemove, cause: .userInitiated)
    writeableParent.children.remove(at: index)
    let writableNodeToRemove = try nodeToRemove.getWritable()
    writableNodeToRemove.parent = nil

    if let selection = selection as? RangeSelection, restoreSelection && !selectionMoved {
      try updateElementSelectionOnCreateDeleteNode(
        selection: selection,
        parentNode: parent,
        nodeOffset: index,
        times: -1)
    }

    if !isRootNode(node: parent) && !parent.canBeEmpty() && parent.getChildrenSize() == 0 {
      try removeNode(nodeToRemove: parent, restoreSelection: restoreSelection)
    }
  }

  /// Inserts a node after this LexicalNode (as the next sibling).
  @discardableResult
  open func insertAfter(nodeToInsert: Node) throws -> Node {
    try errorOnReadOnly()

    let writableSelf = try getWritable()
    let writableNodeToInsert = try nodeToInsert.getWritable()
    let oldParent = writableNodeToInsert.getParent()
    let selection = try getSelection()

    var elementAnchorSelectionOnNode = false
    var elementFocusSelectionOnNode = false

    if let oldParent {
      try removeFromParent(node: writableNodeToInsert)

      if let selection = selection as? RangeSelection,
         let oldIndex = nodeToInsert.getIndexWithinParent() {
        let oldParentKey = oldParent.key
        elementAnchorSelectionOnNode = selection.anchor.type == .element &&
          selection.anchor.key == oldParentKey &&
          selection.anchor.offset == oldIndex + 1
        elementFocusSelectionOnNode = selection.focus.type == .element &&
          selection.focus.key == oldParentKey &&
          selection.focus.offset == oldIndex + 1
      }
    }

    let writableParent = try getParentOrThrow().getWritable()
    let insertKey = writableNodeToInsert.key
    writableNodeToInsert.parent = writableSelf.parent

    guard let index = writableParent.children.firstIndex(where: { $0 == writableSelf.key }) else {
      throw LexicalError.invariantViolation("Node is not a child of its parent")
    }

    writableParent.children.insert(insertKey, at: index + 1)
    internallyMarkSiblingsAsDirty(node: writableNodeToInsert, status: .userInitiated)

    if let selection = selection as? RangeSelection {
      try updateElementSelectionOnCreateDeleteNode(
        selection: selection,
        parentNode: writableParent,
        nodeOffset: index + 1)
      let writableParentKey = writableParent.key

      if elementAnchorSelectionOnNode {
        selection.anchor.updatePoint(key: writableParentKey, offset: index + 2, type: .element)
      }

      if elementFocusSelectionOnNode {
        selection.focus.updatePoint(key: writableParentKey, offset: index + 2, type: .element)
      }
    }

    return nodeToInsert
  }

  /// Inserts a node before this LexicalNode (as the previous sibling).
  @discardableResult
  public func insertBefore(nodeToInsert: Node) throws -> Node {
    try errorOnReadOnly()
    let writableSelf = try getWritable()
    let writableNodeToInsert = try nodeToInsert.getWritable()

    if let oldParent = writableNodeToInsert.getParent() {
      let writableParent = try oldParent.getWritable() as ElementNode
      let children = writableParent.children
      let index = children.firstIndex(of: writableNodeToInsert.key)

      if let index {
        writableParent.children.remove(at: index)
      } else {
        throw LexicalError.invariantViolation("Node is not a child of its parent")
      }

      internallyMarkSiblingsAsDirty(node: writableNodeToInsert, status: .userInitiated)
    }

    let writableParent = try getParentOrThrow().getWritable() as ElementNode
    let insertKey = writableNodeToInsert.key
    writableNodeToInsert.parent = writableSelf.parent
    let children = writableParent.children
    let index = children.firstIndex(of: writableSelf.key)

    if let index {
      writableParent.children.insert(insertKey, at: index)
    } else {
      throw LexicalError.invariantViolation("Node is not a child of its parent")
    }

    internallyMarkSiblingsAsDirty(node: writableNodeToInsert, status: .userInitiated)

    if let selection = try getSelection() as? RangeSelection, let index {
      try updateElementSelectionOnCreateDeleteNode(
        selection: selection,
        parentNode: writableParent,
        nodeOffset: index
      )
    }

    return nodeToInsert
  }

  /// Replaces this LexicalNode with the provided node, optionally transferring the children of the replaced node to the replacing node.
  ///
  /// - Returns: the node that replaced the target node (as a writable copy)
  @discardableResult
  open func replace<T: Node>(replaceWith: T, includeChildren: Bool = false) throws -> T {
    try errorOnReadOnly()
    let toReplaceKey = key
    let writableReplaceWith = try replaceWith.getWritable() as T

    if let oldParent = writableReplaceWith.getParent() {
      let writableParent = try oldParent.getWritable() as ElementNode
      var children = writableParent.children
      let index = children.firstIndex(of: writableReplaceWith.key)

      internallyMarkSiblingsAsDirty(node: writableReplaceWith, status: .userInitiated)

      if let index {
        children.remove(at: index)
      } else {
        throw LexicalError.invariantViolation("Node is not a child of its parent")
      }
    }

    let newParent = try getParentOrThrow()
    let writableParent = try newParent.getWritable() as ElementNode
    let index = writableParent.children.firstIndex(of: key)
    let newKey = writableReplaceWith.key

    if let index {
      writableParent.children.insert(newKey, at: index)
    } else {
      throw LexicalError.invariantViolation("Node is not a child of its parent")
    }

    writableReplaceWith.parent = newParent.key
    try Node.removeNode(nodeToRemove: self, restoreSelection: false)
    internallyMarkSiblingsAsDirty(node: writableReplaceWith, status: .userInitiated)

    if includeChildren, let writableReplaceWith = writableReplaceWith as? ElementNode, let selfElement = self as? ElementNode {
      try writableReplaceWith.append(selfElement.getChildren())
    }

    if let selection = try getSelection() as? RangeSelection { // TODO: the logic here differs from web. Web clones the selection further up. Should make iOS match.
      let anchor = selection.anchor
      let focus = selection.focus

      if anchor.key == toReplaceKey {
        moveSelectionPointToEnd(point: anchor, node: writableReplaceWith)
      }

      if focus.key == toReplaceKey {
        moveSelectionPointToEnd(point: focus, node: writableReplaceWith)
      }
    }

    return writableReplaceWith
  }

  /// Moves selection to the previous sibling of this node, at the specified offsets.
  @discardableResult
  public func selectPrevious(anchorOffset: Int?, focusOffset: Int?) throws -> RangeSelection {
    try errorOnReadOnly()
    let parent = try getParentOrThrow()
    let previousSibling = getPreviousSibling()

    if previousSibling == nil {
      return try parent.select(anchorOffset: 0, focusOffset: 0)
    }

    if let previousSibling = previousSibling as? ElementNode {
      return try previousSibling.select(anchorOffset: nil, focusOffset: nil)
    } else if let previousSibling = previousSibling as? TextNode {
      return try previousSibling.select(anchorOffset: anchorOffset, focusOffset: focusOffset)
    } else {
      var index = previousSibling?.getIndexWithinParent()
      index = index ?? 0 + 1
      return try parent.select(anchorOffset: index, focusOffset: index)
    }
  }

  /// Moves selection to the next sibling of this node, at the specified offsets.
  public func selectNext(anchorOffset: Int?, focusOffset: Int?) throws -> RangeSelection {
    try errorOnReadOnly()
    let nextSibling = getNextSibling()
    let parent = try getParentOrThrow()

    if nextSibling == nil {
      return try parent.select(anchorOffset: nil, focusOffset: nil)
    }

    if let nextSibling = nextSibling as? ElementNode {
      return try nextSibling.select(anchorOffset: 0, focusOffset: 0)
    } else if let nextSibling = nextSibling as? TextNode {
      return try nextSibling.select(anchorOffset: anchorOffset, focusOffset: focusOffset)
    } else {
      let index = nextSibling?.getIndexWithinParent()
      return try parent.select(anchorOffset: index, focusOffset: index)
    }
  }

  public func isSameNode(_ node: Node) -> Bool {
    return self.getKey() == node.getKey()
  }
}

extension Node: Hashable {
  public func hash(into hasher: inout Hasher) {
    hasher.combine(ObjectIdentifier(self))
  }
}

extension Node: Equatable {
  public static func ==(lhs: Node, rhs: Node) -> Bool {
    //    return ObjectIdentifier(lhs) == ObjectIdentifier(rhs)
    return lhs.isSameKey(rhs)
  }

  /// Returns true if there is a path between this node and the RootNode, false otherwise. This is a way of determining if the node is "attached" EditorState. Unattached nodes won't be reconciled and will ultimately be cleaned up by the Lexical GC.
  public func isAttached() -> Bool {
    var nodeKey: NodeKey? = key

    while nodeKey != nil {
      if nodeKey == kRootNodeKey {
        return true
      }

      guard let key = nodeKey, let node = getNodeByKey(key: key) else { break }

      nodeKey = node.parent
    }

    return false
  }

  /// Returns true if this node is contained within the provided Selection., false otherwise. Relies on the algorithms implemented in ``BaseSelection/getNodes()`` to determine what's included.
  public func isSelected() throws -> Bool {
    guard let selection = try getSelection() else {
      return false
    }

    let isSelected = try selection.getNodes().contains(where: { $0.key == self.key })

    if isTextNode(self) {
      return isSelected
    }
    // For inline images inside of element nodes.
    // Without this change the image will be selected if the cursor is before or after it.
    if let selection = selection as? RangeSelection,
       selection.anchor.type == .element &&
        selection.focus.type == .element &&
        selection.anchor.key == selection.focus.key &&
        selection.anchor.offset == selection.focus.offset {
      return false
    }
    return isSelected
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import UIKit

public class ParagraphNode: ElementNode {
  override public init() {
    super.init()
    self.type = NodeType.paragraph
  }

  override required init(_ key: NodeKey?) {
    super.init(key)
    self.type = NodeType.paragraph
  }

  public required init(from decoder: Decoder) throws {
    try super.init(from: decoder)
    self.type = NodeType.paragraph
  }

  override public func encode(to encoder: Encoder) throws {
    try super.encode(to: encoder)
  }

  override public func clone() -> Self {
    Self(key)
  }

  override public func getAttributedStringAttributes(theme: Theme) -> [NSAttributedString.Key: Any] {
    if let paragraph = theme.paragraph {
      return paragraph
    }

    return [:]
  }

  override open func insertNewAfter(selection: RangeSelection?) throws -> ParagraphNode? {
    let newElement = createParagraphNode()
    let direction = getDirection()
    do {
      try newElement.setDirection(direction: direction)
      try insertAfter(nodeToInsert: newElement)
    } catch {
      throw LexicalError.internal("Error in insertNewAfter: \(error.localizedDescription)")
    }
    return newElement
  }

  public func createParagraphNode() -> ParagraphNode {
    return ParagraphNode()
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import UIKit

public class QuoteNode: ElementNode {
  override public init() {
    super.init()
    self.type = NodeType.quote
  }

  override public required init(_ key: NodeKey?) {
    super.init(key)
    self.type = NodeType.quote
  }

  public required init(from decoder: Decoder) throws {
    try super.init(from: decoder)

    self.type = NodeType.quote
  }

  override public func encode(to encoder: Encoder) throws {
    try super.encode(to: encoder)
  }

  override public func clone() -> Self {
    Self(key)
  }

  override public func getAttributedStringAttributes(theme: Theme) -> [NSAttributedString.Key: Any] {
    theme.quote ?? [:]
  }

  override public func getIndent() -> Int {
    1
  }

  // MARK: - Mutation

  override open func insertNewAfter(selection: RangeSelection?) throws -> Node? {
    let newBlock = createParagraphNode()
    let direction = getDirection()
    try newBlock.setDirection(direction: direction)

    try insertAfter(nodeToInsert: newBlock)

    return newBlock
  }

  override public func collapseAtStart(selection: RangeSelection) throws -> Bool {
    let paragraph = createParagraphNode()
    let children = getChildren()
    try children.forEach({ try paragraph.append([$0]) })
    try replace(replaceWith: paragraph)

    return true
  }
}

@objc public class QuoteCustomDrawingAttributes: NSObject {
  public init(barColor: UIColor, barWidth: CGFloat, rounded: Bool, barInsets: UIEdgeInsets) {
    self.barColor = barColor
    self.barWidth = barWidth
    self.rounded = rounded
    self.barInsets = barInsets
  }

  let barColor: UIColor
  let barWidth: CGFloat
  let rounded: Bool
  let barInsets: UIEdgeInsets

  override public func isEqual(_ object: Any?) -> Bool {
    let lhs = self
    guard let rhs = object as? QuoteCustomDrawingAttributes else {
      return false
    }
    return lhs.barColor == rhs.barColor &&
      lhs.barWidth == rhs.barWidth &&
      lhs.rounded == rhs.rounded &&
      lhs.barInsets == rhs.barInsets
  }
}

public extension NSAttributedString.Key {
  static let quoteCustomDrawing: NSAttributedString.Key = .init(rawValue: "quoteCustomDrawing")
}

extension QuoteNode {
  internal static var quoteBackgroundDrawing: CustomDrawingHandler {
    get {
      return { attributeKey, attributeValue, layoutManager, attributeRunCharacterRange, granularityExpandedCharacterRange, glyphRange, rect, firstLineFragment in
        guard let attributeValue = attributeValue as? QuoteCustomDrawingAttributes else { return }

        let barRect = CGRect(x: rect.minX + attributeValue.barInsets.left,
                             y: rect.minY + attributeValue.barInsets.top,
                             width: attributeValue.barWidth,
                             height: rect.height - attributeValue.barInsets.top - attributeValue.barInsets.bottom)
        attributeValue.barColor.setFill()

        if attributeValue.rounded {
          let bezierPath = UIBezierPath(roundedRect: barRect, cornerRadius: attributeValue.barWidth / 2)
          bezierPath.fill()
        } else {
          UIRectFill(barRect)
        }
      }
    }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import UIKit

public class RootNode: ElementNode {

  override required init() {
    super.init(kRootNodeKey)
    self.type = NodeType.root
  }

  public required init(from decoder: Decoder) throws {
    try super.init(from: decoder)
    self.type = NodeType.root
  }

  override public func encode(to encoder: Encoder) throws {
    try super.encode(to: encoder)
  }

  override public func clone() -> Self {
    Self()
  }

  override public func getAttributedStringAttributes(theme: Theme) -> [NSAttributedString.Key: Any] {
    if let root = theme.root {
      return root
    }

    return [.font: LexicalConstants.defaultFont]
  }

  // Root nodes cannot have a preamble. If they did, there would be no way to make a selection of the
  // beginning of the document. The same applies to postamble.
  override public final func getPreamble() -> String {
    return ""
  }

  override public func getTextContent(includeInert: Bool = false, includeDirectionless: Bool = false) -> String {
    return super.getTextContent(includeInert: includeInert, includeDirectionless: includeDirectionless)
  }

  override public final func getPostamble() -> String {
    return ""
  }

  override public func insertBefore(nodeToInsert: Node) throws -> Node {
    throw LexicalError.invariantViolation("insertBefore: cannot be called on root nodes")
  }

  override public func remove() throws {
    throw LexicalError.invariantViolation("remove: cannot be called on root nodes")
  }

  override public func replace<T: Node>(replaceWith: T, includeChildren: Bool = false) throws -> T {
    throw LexicalError.invariantViolation("replace: cannot be called on root nodes")
  }

  override public func insertAfter(nodeToInsert: Node) throws -> Node {
    throw LexicalError.invariantViolation("insertAfter: cannot be called on root nodes")
  }
}

extension RootNode: CustomDebugStringConvertible {
  public var debugDescription: String {
    return "(RootNode: key '\(key)', id \(ObjectIdentifier(self))"
  }
}