import {FC, HTMLProps, PropsWithChildren, useEffect, useRef, useState} from "react";
import styles from './ProducingGraph.module.css'
import {EntityIcon} from "../../home/EntityIcon/EntityIcon";
import {createPath, drawLine} from "../../../src/svg";
import {AdditionalNode, GraphNode, GraphNodeWithIds, isAdditionalNode} from "../../../src/graph-untangle/types";
import {graphUntangled} from "../../../src/graph-untangle";
import {Dict} from "../../../src/types";

interface Props<T extends {}> {
  nodes: GraphNode<T>[]
  inputs: string[]
  outputs?: string[]
  childType: FC<HTMLProps<HTMLDivElement> & {node: GraphNode<T>}>
}

export const ProducingGraph = <T extends Dict<unknown>,>({nodes, inputs, outputs, childType: ChildType}: PropsWithChildren<Props<T>>) => {
  const planeRef = useRef<HTMLDivElement>(null)
  const [[rows, nodeMap], setGraph] = useState<[string[][], Dict<GraphNodeWithIds<T|AdditionalNode>>]>([[], {}])
  useEffect(() => {
    setGraph(graphUntangled(nodes, inputs, outputs ?? []))
    setTimeout(() => setGraph(graphUntangled(nodes, inputs, outputs ?? [], 5000)), 0)
  }, [inputs, nodes, outputs])

  /*const rowsWithInOut: (GraphNode<T>|AdditionalNode)[][] = useMemo(() => {
    const addedInputs = new Set<string>()
    const res: (GraphNode<T>|AdditionalNode)[][] = deepcopy([[], ...rows, []])
    for (let i = 0; i < rows.length; i++) {
      const rowOutputs = uniquify(rows[i].flatMap(row => row.outputs))
      for (let rowOutput of rowOutputs) {
        outputs?.includes(rowOutput) && res[i+2].push({___type: 'o', value: rowOutput})
      }
      const rowInputs = uniquify(rows[i].flatMap(row => row.inputs))
      for (let rowInput of rowInputs) {
        if (addedInputs.has(rowInput))
          !res[i].some(node => isAdditionalNode(node) && node.value === rowInput) && res[i].push({___type: 'h', value: rowInput})
        else if (inputs.includes(rowInput))
          res[i].push({___type: 'i', value: rowInput})
        addedInputs.add(rowInput)
      }
    }
    return res
  }, [rows, inputs, outputs])*/

  useEffect(() => {
    if (!planeRef.current) return
    const plane = planeRef.current
    function createSvgElement() {
      const elem = document.createElementNS("http://www.w3.org/2000/svg", "svg")
      elem.id = 'arrows'
      elem.setAttribute('style', 'position: absolute; inset: 0 0 0 0; pointer-events: none; z-index: -10;')
      elem.setAttributeNS(null, 'stroke', 'black')
      elem.setAttributeNS(null, 'fill', 'none')
      plane.appendChild(elem)
      return elem
    }
    const svg = document.getElementById('arrows') ?? createSvgElement()
    const width = Math.round(plane.getBoundingClientRect().width)
    const height = Math.round(plane.getBoundingClientRect().height)
    svg.setAttribute('width', `${width}`)
    svg.setAttribute('height', `${height}`)
    svg.setAttributeNS(null, 'viewBox', `0 0 ${width} ${height}`)
    svg.replaceChildren()

    plane.querySelectorAll('[data-outputs]').forEach(startNode => {
      if (!(startNode instanceof HTMLElement)) return
      (startNode.dataset.outputs?.split(' ') ?? []).forEach(output => {
        const endNode = plane.querySelector(`[data-name="${output}"]`)
        if (!(endNode instanceof HTMLElement)) return
        const path = document.createElementNS('http://www.w3.org/2000/svg',"path")
        path.setAttributeNS(null, 'd', createPath(plane.getBoundingClientRect(), startNode.getBoundingClientRect(), endNode.getBoundingClientRect()))
        svg.appendChild(path)
      })
    })
    plane.querySelectorAll('[data-hidden][data-outputs]').forEach(elem => {
      if (!(elem instanceof HTMLElement)) return
      const path = document.createElementNS('http://www.w3.org/2000/svg',"path")
      path.setAttributeNS(null, 'd', drawLine(plane.getBoundingClientRect(), elem.getBoundingClientRect()))
      svg.appendChild(path)
    })
  })

  return <div className={styles.plane} ref={planeRef}>
    {rows.map((row, colIdx) => (
      <div className={styles.row} key={colIdx} data-row={true}>
        {
          row.map((uid) => {
            const node = nodeMap[uid]
            return (
              !isAdditionalNode(node)
                ? <ChildType
                  data-name={node.___uid}
                  data-inputs={node.___uidInputs.join(' ')}
                  data-outputs={node.___uidOutputs.join(' ')}
                  className={styles.node}
                  key={node.name}
                  node={node}
                />
                : node.___type === 'h'
                  ? <span
                      className={styles.hidden}
                      key={node.___uid}
                      data-name={node.___uid}
                      data-inputs={node.___uidInputs.join(' ')}
                      data-outputs={node.___uidOutputs.join(' ')}
                      data-hidden={true}></span>
                  : node.___type === 'i'
                    ? <span
                        className={styles.input}
                        key={node.___uid}
                        data-name={node.___uid}
                        data-outputs={node.___uidOutputs.join(' ')}
                        data-hidden={true}>
                        <EntityIcon value={node.name}/>
                    </span>
                    : <EntityIcon
                      className={styles.input}
                      key={node.___uid}
                      value={node.name}
                      data-name={node.___uid}
                      data-inputs={node.___uidInputs.join(' ')}
                    />
            );
          })
        }
      </div>
    ))}
  </div>
}
