//
//  ItemsExtension.swift
//  lottie-swift
//
//  Created by Brandon Withrow on 1/18/19.
//

// MARK: - NodeTree

final class NodeTree {
  var rootNode: AnimatorNode? = nil
  var transform: ShapeTransform? = nil
  var renderContainers = [ShapeContainerLayer]()
  var paths = [PathOutputNode]()
  var childrenNodes = [AnimatorNode]()
}

extension [ShapeItem] {
  func initializeNodeTree() -> NodeTree {
    let nodeTree = NodeTree()

    for item in self {
      guard item.hidden == false, item.type != .unknown else { continue }
      if let fill = item as? Fill {
        let node = FillNode(parentNode: nodeTree.rootNode, fill: fill)
        nodeTree.rootNode = node
        nodeTree.childrenNodes.append(node)
      } else if let stroke = item as? Stroke {
        let node = StrokeNode(parentNode: nodeTree.rootNode, stroke: stroke)
        nodeTree.rootNode = node
        nodeTree.childrenNodes.append(node)
      } else if let gradientFill = item as? GradientFill {
        let node = GradientFillNode(parentNode: nodeTree.rootNode, gradientFill: gradientFill)
        nodeTree.rootNode = node
        nodeTree.childrenNodes.append(node)
      } else if let gradientStroke = item as? GradientStroke {
        let node = GradientStrokeNode(parentNode: nodeTree.rootNode, gradientStroke: gradientStroke)
        nodeTree.rootNode = node
        nodeTree.childrenNodes.append(node)
      } else if let ellipse = item as? Ellipse {
        let node = EllipseNode(parentNode: nodeTree.rootNode, ellipse: ellipse)
        nodeTree.rootNode = node
        nodeTree.childrenNodes.append(node)
      } else if let rect = item as? Rectangle {
        let node = RectangleNode(parentNode: nodeTree.rootNode, rectangle: rect)
        nodeTree.rootNode = node
        nodeTree.childrenNodes.append(node)
      } else if let star = item as? Star {
        switch star.starType {
        case .none:
          continue

        case .polygon:
          let node = PolygonNode(parentNode: nodeTree.rootNode, star: star)
          nodeTree.rootNode = node
          nodeTree.childrenNodes.append(node)

        case .star:
          let node = StarNode(parentNode: nodeTree.rootNode, star: star)
          nodeTree.rootNode = node
          nodeTree.childrenNodes.append(node)
        }
      } else if let shape = item as? Shape {
        let node = ShapeNode(parentNode: nodeTree.rootNode, shape: shape)
        nodeTree.rootNode = node
        nodeTree.childrenNodes.append(node)
      } else if let trim = item as? Trim {
        let node = TrimPathNode(parentNode: nodeTree.rootNode, trim: trim, upstreamPaths: nodeTree.paths)
        nodeTree.rootNode = node
        nodeTree.childrenNodes.append(node)
      } else if let roundedCorners = item as? RoundedCorners {
        let node = RoundedCornersNode(
          parentNode: nodeTree.rootNode,
          roundedCorners: roundedCorners,
          upstreamPaths: nodeTree.paths
        )
        nodeTree.rootNode = node
        nodeTree.childrenNodes.append(node)
      } else if let xform = item as? ShapeTransform {
        nodeTree.transform = xform
        continue
      } else if let group = item as? Group {
        let tree = group.items.initializeNodeTree()
        let node = GroupNode(name: group.name, parentNode: nodeTree.rootNode, tree: tree)
        nodeTree.rootNode = node
        nodeTree.childrenNodes.append(node)
        /// Now add all child paths to current tree
        nodeTree.paths.append(contentsOf: tree.paths)
        nodeTree.renderContainers.append(node.container)
      } else if item is Repeater {
        LottieLogger.shared.warn("""
          The Main Thread rendering engine doesn't currently support repeaters.
          To play an animation with repeaters, you can use the Core Animation rendering engine instead.
          """)
      }

      if let pathNode = nodeTree.rootNode?.getPathNode() {
        //// Add path container to the node tree
        nodeTree.paths.append(pathNode.pathOutput)
      }

      if let renderNode = nodeTree.rootNode?.getRenderNode() {
        nodeTree.renderContainers.append(ShapeRenderLayer(renderer: renderNode.renderer))
      }
    }
    return nodeTree
  }
}

extension AnimatorNode {
  /// Helper function to avoid casting to `PathNode`
  /// More performant replacement for `nodeTree.rootNode as? PathNode`
  fileprivate func getPathNode() -> PathNode? {
    switch self {
    case let node as EllipseNode:
      node
    case let node as PolygonNode:
      node
    case let node as ShapeNode:
      node
    case let node as StarNode:
      node
    case let node as RectangleNode:
      node
    default:
      nil
    }
  }

  /// Helper function to avoid casting to `RenderNode`
  /// More performant replacement for `nodeTree.rootNode as? RenderNode`
  fileprivate func getRenderNode() -> RenderNode? {
    switch self {
    case let node as GradientFillNode:
      node
    case let node as StrokeNode:
      node
    case let node as FillNode:
      node
    case let node as GradientStrokeNode:
      node
    default:
      nil
    }
  }
}
