import React, { ReactNode, useState } from "react";
import styles from "./OrgAst.module.css";
import { Fragment } from "react";

const OrgAst = (props: {
  setHighlight: Function;
  clearHighlights: Function;
  astTree: any;
  value: string;
}) => {
  const [selectedNode, setSelectedNode] = useState<string>("");

  function selectNode(uid: string, start: number, end: number) {
    props.setHighlight(start, end);
    setSelectedNode(uid);
  }

  if (props.astTree.status !== "success") {
    return <div className={styles.OrgAst}>Error! {props.astTree.content}</div>;
  } else {
    return (
      <div className={styles.OrgAst}>
        <OrgAstNode
          key="^"
          uid="^"
          selectNode={selectNode}
          node={props.astTree.content}
          selectedNode={selectedNode}
        />
      </div>
    );
  }
};

const OrgAstNode = (props: {
  selectNode: Function;
  node: any;
  uid: string;
  selectedNode: string;
}) => {
  function selectNode() {
    props.selectNode(
      props.uid,
      props.node["standard-properties"]["begin"] - 1,
      props.node["standard-properties"]["end"] - 1,
    );
  }

  const nodeClassName =
    props.selectedNode === props.uid
      ? styles.OrgAstNode + " " + styles.selected
      : styles.OrgAstNode;

  return (
    <div className={nodeClassName}>
      <div className={styles.OrgAstNodeType} onClick={selectNode}>
        {props.node["ast-node"]}
      </div>
      <details>
        <summary>Standard Properties</summary>
        <OrgPropertiesList
          selectNode={props.selectNode}
          parentUniqueId={props.uid}
          selectedNode={props.selectedNode}
          properties={props.node["standard-properties"]}
        />
      </details>
      {!!Object.keys(props.node.properties).length ? (
        <>
          <details>
            <summary>Properties</summary>
            <OrgPropertiesList
              selectNode={props.selectNode}
              parentUniqueId={props.uid}
              selectedNode={props.selectedNode}
              properties={props.node.properties}
            />
          </details>
        </>
      ) : null}
      {Array.isArray(props.node.children) && props.node.children.length > 0 ? (
        <details open={true}>
          <summary>Children</summary>
          <div className={styles.OrgAstChildren}>
            <OrgAstNodeList
              selectNode={props.selectNode}
              parentUniqueId={props.uid}
              selectedNode={props.selectedNode}
              node_list={props.node.children}
            />
          </div>
        </details>
      ) : null}
    </div>
  );
};

const OrgAstNodeList = (props: {
  selectNode: Function;
  parentUniqueId: string;
  selectedNode: string;
  node_list: any[];
}): React.JSX.Element[] => {
  return props.node_list.map((node) => {
    const uid =
      props.parentUniqueId +
      "_" +
      node["ast-node"] +
      "/" +
      node["standard-properties"]["begin"] +
      "/" +
      node["standard-properties"]["end"] +
      "#";
    return (
      <OrgAstNode
        key={uid}
        uid={uid}
        selectNode={props.selectNode}
        selectedNode={props.selectedNode}
        node={node}
      />
    );
  });
};

const OrgPropertiesList = (props: {
  selectNode: Function;
  parentUniqueId: string;
  selectedNode: string;
  properties: Object;
}): React.JSX.Element => {
  const entries = Object.entries(props.properties)
    .sort((a, b) => {
      if (a[0] < b[0]) {
        return -1;
      } else if (a[0] > b[0]) {
        return 1;
      } else {
        return 0;
      }
    })
    .filter((entry) => {
      return !(is_object(entry[1]) && entry[1]["noop"] == "Noop");
    })
    .map(([key, value]) => {
      return (
        <Fragment key={key}>
          <tr>
            <th scope="row">{key}:</th>
            <td>
              <OrgPropertyValue
                selectNode={props.selectNode}
                parentUniqueId={props.parentUniqueId}
                selectedNode={props.selectedNode}
                value={value}
              />
            </td>
          </tr>
        </Fragment>
      );
    });
  return (
    <table className={styles.OrgAstProperties}>
      <tbody>{entries}</tbody>
    </table>
  );
};

const OrgPropertyValue = (props: {
  selectNode: Function;
  parentUniqueId: string;
  selectedNode: string;
  value: any;
}): React.ReactNode => {
  return JSON.stringify(props.value);
};

function is_object(val: any): boolean {
  return val instanceof Object && !(val instanceof Array);
}

export default OrgAst;
