import CheckboxTree from 'react-checkbox-tree';
import ContextMenu from 'context-menu';
import './Translation.css';
import 'react-checkbox-tree/lib/react-checkbox-tree.css';
import PropTypes from 'prop-types';
import React from 'react';
import deepcopy from 'deepcopy';
// import { graphqlSync, introspectionQuery, printSchema } from 'graphql';
import { getGraphqlSchema } from 'translation-to-graphql';
import EndpointPopup from '../Popup/EndpointPopup';
import LinkPopup from '../Popup/LinkPopup';
import DescriptionPopup from '../Popup/DescriptionPopup';

/**
 * @file Translation
 * Handles the translation process, checkbox tree, saving and opening translation file
 * @module Translation
 * @extends React.Component
 */
export default class Translation extends React.Component {
  constructor(props) {
    super(props);

    this.endpoints = {};

    this.state = {
      translationFile: null,
      nodes: [],
      checked: [],
      expanded: [],
      openLinkModal: false,
      openDescriptionModal: false,
      editingPart: '',
      pathParts: [],
    };
  }

  /**
   * Called after this component is mounted
   * @method
   */
  componentDidMount() {
    const checkboxTree = document.querySelector('.translation-checkbox-tree');
    // parent handles all contextmenu events (https://www.kirupa.com/html5/handling_events_for_many_elements.htm)
    checkboxTree.addEventListener(
      'contextmenu',
      this.contextMenuListener,
      false
    );

    // handle clicking away from context menu
    checkboxTree.addEventListener('click', this.textAreaListener, false);
  }

  /**
   * Handle creation of a new translation file
   * @method
   */
  newTranslationFile = () => {
    const { swaggerFile } = this.props;
    this.setState({
      translationFile: {
        title: swaggerFile.info.title,
        version: swaggerFile.info.version,
      },
    });
  };

  /**
   * Read in existing translation file and intialize the checked nodes in the checkbox tree
   * based off translation file
   * @method
   * @param {Object} translationFile - the translation file
   * @param {Array} leafNodes - array of checked leaf nodes
   * @param {string} path - string value stored in leafNodes array
   */
  initCheckedFromTranslation = (translationFile, leafNodes, path) => {
    Object.keys(translationFile).forEach((node) => {
      // ignore any names that start with '/' since they are not part of the checkbox tree
      // (e.g. /subreddits/hot, /labs/2/users, etc. )
      if (node.startsWith('/')) {
        this.initCheckedFromTranslation(translationFile[node], leafNodes, path);
      }

      // check if translationFile[node] is not an Object type
      if (
        !(
          Object.prototype.toString.call(translationFile[node]) ===
          '[object Object]'
        )
      ) {
        // at a leaf so push to leafNodes
        leafNodes.push(`${path}/${node}: ${translationFile[node]}`);
      } else {
        // recursing on level down so append to end of path
        this.initCheckedFromTranslation(
          translationFile[node],
          leafNodes,
          path === '' ? `${node}` : `${path}/${node}`
        );
      }
    });
  };

  /**
   * Handling opening of swagger file button
   * @method
   * @param {ChangeEvent<HTMLInputElement>} event - event containing name of the swagger file
   */
  onOpenTranslation = (event) => {
    const { onTranslationChange, swaggerFile } = this.props;

    const fileReader = new FileReader();
    fileReader.addEventListener('load', () => {
      const translationFile = JSON.parse(fileReader.result);

      // intialize the checked array for the checkbox tree from the opened translation file
      const checked = [];
      this.initCheckedFromTranslation(translationFile.endpoints, checked, '');

      this.setState(
        {
          nodes: [],
          checked,
          expanded: [],
          translationFile,
        },
        () => {
          // iterate through all endpoints the translation file and add that to the checkbox tree
          Object.keys(translationFile.endpoints).forEach((realEndpoint) => {
            Object.keys(translationFile.endpoints[realEndpoint]).forEach(
              (endpointView) => {
                this.handleNewEndpoint(
                  realEndpoint,
                  endpointView,
                  swaggerFile.paths[realEndpoint]
                );
              }
            );
          });
        }
      );

      // alert App componenet that the translation file has changed so the preview can be updated
      onTranslationChange(translationFile);
    });
    fileReader.readAsText(event.target.files[0]);
  };

  /**
   * Save the translation file to file system
   * @method
   */
  saveTranslation = async () => {
    const { translationFile } = this.state;
    const { swaggerFile } = this.props;

    // create the graphql schema
    const schema = await getGraphqlSchema(swaggerFile, translationFile);

    // const resul = graphqlSync(schema, introspectionQuery).data;
    // console.log(result);

    // const data = {
    //   variables: {
    //     schema: schemaConfig,
    //     accessToken: '352480127796-UoShgNdvB_zlnJ7oYbHIyDxF9iI',
    //   },
    //   query: '{ top (subreddit: "uiuc") { data { children { data { title } } } } }',
    // };

    // fetch('http://localhost:4000/', {
    //   method: 'POST',
    //   body: JSON.stringify(data),
    //   headers: {
    //     'Content-Type': 'application/json',
    //   },
    // }).then((response) => {
    //   console.log(response);
    // });

    // save as JSON file
    const blob = new Blob([JSON.stringify(translationFile, null, 4)], {
      type: 'application/json',
    });
    // saveAs(blob);
  };

  /**
   * Create new node in the checkbox tree after user adds a new endpoint
   * @method
   * @param {Object} elements - object containing data for the endpoint
   * @param {Array} nodes - array containing the nodes for the checkbox tree
   * @param {String} value - name of the path
   */
  getNewEndpointCheckbox = (elements, nodes, value) => {
    if (Object.prototype.toString.call(elements) === '[object Object]') {
      Object.keys(elements).forEach((el) => {
        if (
          Object.prototype.toString.call(elements[el]) !== '[object Object]' &&
          !(elements[el] instanceof Array)
        ) {
          if (el !== 'operationId') {
            // add leaf nodes of checkbox tree
            nodes.push({
              value: `${value}/${el
                .replace(/:/g, '%3A')
                .replace(/\//g, '%2F')}:${
                typeof elements[el] === 'string'
                  ? elements[el].replace(/\//g, '%2F')
                  : elements[el]
              }-type:${typeof elements[el]}`,
              label: `${el}: ${elements[el]}`,
            });
          }
        } else if (el !== 'security') {
          nodes.push({
            // add parent node that can contain children nodes
            value: `${value}/${
              elements[el] instanceof Array
                ? `${el.replace(/\//g, '%2F')}-array`
                : el.replace(/\//g, '%2F')
            }`,
            label: `${el}`,
            children: [],
          });

          // add nodes to newly created parent's children array
          const responseNodeLen = nodes.length - 1;
          this.getNewEndpointCheckbox(
            elements[el],
            nodes[responseNodeLen].children,
            `${value}/${
              elements[el] instanceof Array
                ? `${el.replace(/\//g, '%2F')}-array`
                : el.replace(/\//g, '%2F')
            }`
          );
        }
      });
    } else if (elements instanceof Array) {
      elements.forEach((arrayEl) => {
        if (
          Object.prototype.toString.call(arrayEl) === '[object Object]' &&
          'name' in arrayEl
        ) {
          // update name field to become the parent:
          /* e.g.
            {
              name: foo,
              type: string,
              required: false,
              ...
            }

            to

            foo: {
              type: string,
              required: false,
              ...
            }
          */
          const { name } = arrayEl;
          nodes.push({
            value: `${value}/${name.replace(/\//g, '%2F')}`,
            label: `${name}`,
            children: [],
          });

          // delete the name field from the property
          const noName = { ...arrayEl };
          delete noName.name;

          // add nodes to the newly created parent's children array
          const responseNodeLen = nodes.length - 1;
          this.getNewEndpointCheckbox(
            noName,
            nodes[responseNodeLen].children,
            `${value}/${name.replace(/\//g, '%2F')}`
          );
        } else {
          this.getNewEndpointCheckbox(arrayEl, nodes, `${value}`);
        }
      });
    } else {
      nodes.push({
        value: `${value}/${elements.replace(/\//g, '%2F')}`,
        label: `${elements}`,
      });
    }
  };

  /**
   * Event listener for context menu
   * @method
   * @param {Event} event - context menu listener
   */
  contextMenuListener = (event) => {
    if (event.target !== event.currentTarget) {
      this.handleOnContextMenu(this.getNodeElement(event.target));
    }
    event.stopPropagation();
  };

  /**
   * Converts the node's label to a value
   * @method
   * @param {Array} path - array containing every node
   * @returns array containing every node's value
   */
  labelToValue = (path) => {
    let { nodes } = this.state;

    const newPath = [];
    for (let i = 0; i < path.length; i += 1) {
      // search for the node in the nodes state value
      const node = nodes.find((x) => x.label.startsWith(path[i]));
      newPath.push(node.value.substring(node.value.lastIndexOf('/') + 1));
      nodes = node.children;
    }

    return newPath;
  };

  /**
   * Get the array of nodes that lead to the selected element
   * @method
   * @param {HTMLElement} node - HTML element that user has just edited
   * @returns array of nodes
   */
  getCheckboxPath = (node) => {
    let el = node;
    const pathParts = [];
    while (el.className !== 'translation-checkbox-tree') {
      el = el.parentNode;
      if (el.tagName === 'LI') {
        const nodeTitle = el.querySelector('.rct-title').innerHTML;
        // add to beginning of array
        pathParts.unshift(nodeTitle);
      }
    }

    return this.labelToValue(pathParts);
  };

  /**
   * Search for the checkbox node recursively
   * @method
   * @param {Array} path - array containing nodes that lead to targeted checked node
   * @param {Array} nodes - array of all checkbox nodes
   * @param {String} searchValue - search for the checkbox through the searchValue
   */
  searchCheckboxNode = (path, nodes, searchValue) => {
    if (path.length === 0) {
      return nodes.some((node) => node.value.includes(searchValue));
    }
    // find the next nodes to recurse down
    const nextNodes = nodes.find(
      (node) =>
        node.value.substring(node.value.lastIndexOf('/') + 1) === path[0]
    );
    // remove the first element from path array
    const newPath = path.slice(1);
    return this.searchCheckboxNode(newPath, nextNodes.children, searchValue);
  };

  /**
   * Update the checkbox
   * @method
   * @param {Array} path - array containing nodes that lead to targeted checked node
   * @param {Array} nodes - array of all checkbox nodes
   * @param {String} value - search for the checkbox through the value
   */
  updateCheckboxNode = (path, nodes, value) => {
    if (path.length !== 0) {
      const nextNodes = nodes.find(
        (node) =>
          node.value.substring(node.value.lastIndexOf('/') + 1) === path[0]
      );
      if (path.length === 1) {
        if (!('children' in nextNodes)) {
          if (value.substring(value.indexOf(':') + 2)) {
            // update nodes's value
            const key = nextNodes.value.substring(
              0,
              nextNodes.value.indexOf(':')
            );
            const type = nextNodes.value.substring(
              nextNodes.value.indexOf('-type')
            );
            nextNodes.value = `${key}:${value.substring(
              value.indexOf(':') + 2
            )}${type}`;
          }
          // update the node's label
          nextNodes.label = value;
        } else {
          nextNodes.children.unshift({
            label: value,
            value: `${nextNodes.value}/${value}-type:string`,
          });
        }
      } else {
        const newPath = path.slice(1);
        this.updateCheckboxNode(newPath, nextNodes.children, value);
      }
    }
  };

  /**
   * Listener for text area events
   * @method
   * @param {Event} event - event for text area
   */
  textAreaListener = (event) => {
    if (
      event.target !== event.currentTarget &&
      event.target.tagName !== 'TEXTAREA'
    ) {
      const text = document.querySelector('textarea');
      if (text !== null) {
        const { nodes, checked, editingPart } = this.state;
        const nodeCopy = deepcopy(nodes);
        let checkedCopy = [...checked];
        const pathParts = this.getCheckboxPath(text);
        const path = pathParts.join('/');

        if (editingPart === 'endpoint') {
          checkedCopy = checkedCopy.map((node) => {
            if (node.startsWith(path.trim())) {
              return node;
            }
            return node;
          });
        } else {
          const checkedIndex = checkedCopy.findIndex((node) =>
            node.startsWith(path)
          );

          if (checkedIndex !== -1) {
            const colonIndex = checkedCopy[checkedIndex].indexOf(':');
            const previous = checkedCopy[checkedIndex].substring(0, colonIndex);
            checkedCopy[checkedIndex] = `${previous}:${text.value}-type:string`;
            this.onCheckHandler(checkedCopy);
          }
        }

        this.updateCheckboxNode(
          pathParts,
          nodeCopy,
          `${editingPart}${text.value}`
        );
        this.setState({
          nodes: nodeCopy,
          editingPart: '',
        });

        text.parentNode.removeChild(text);
      }
    }
  };

  /**
   * Handle context menu interaction
   * @method
   * @param {HTMLElement} nodeElement - HTML element node
   */
  handleOnContextMenu = (nodeElement) => {
    const possibleOperations = [
      'get',
      'post',
      'put',
      'patch',
      'delete',
      'head',
      'options',
    ];
    const data = [[]];

    if (nodeElement.innerHTML.startsWith('description:')) {
      data[0].push({
        label: 'Edit Description',
        onClick: () => {
          const { editingPart } = this.state;
          if (!editingPart) {
            // create new textarea
            const newText = document.createElement('textarea');
            newText.value = nodeElement.innerHTML.substring(
              nodeElement.innerHTML.indexOf(':') + 2
            );

            // set the size of the new text area
            newText.rows = 4;
            newText.cols = 75;

            const { nodes } = this.state;
            const nodeCopy = deepcopy(nodes);

            const checkboxPath = this.getCheckboxPath(nodeElement);
            this.updateCheckboxNode(checkboxPath, nodeCopy, 'description: ');

            // insert the text area into the DOM
            nodeElement.parentNode.insertBefore(
              newText,
              nodeElement.nextSibling
            );

            this.setState({
              nodes: nodeCopy,
              editingPart: 'description: ',
            });
          }
        },
      });
    } else if (nodeElement.innerHTML.startsWith('required:')) {
      const requiredValue = nodeElement.innerHTML.substring(
        nodeElement.innerHTML.indexOf(':') + 2
      );
      data[0].push({
        label: `Change to ${requiredValue === 'true' ? 'not' : ''} required`,
        onClick: () => {
          const { nodes, checked } = this.state;
          const nodeCopy = deepcopy(nodes);

          const checkboxPath = this.getCheckboxPath(nodeElement);
          const path = checkboxPath.join('/');
          const checkedCopy = [...checked];

          const checkedIndex = checkedCopy.findIndex((node) =>
            node.startsWith(path)
          );

          if (checkedIndex !== -1) {
            const key = checkedCopy[checkedIndex].substring(
              0,
              checkedCopy[checkedIndex].indexOf(':')
            );
            const type = checkedCopy[checkedIndex].substring(
              checkedCopy[checkedIndex].indexOf('-type')
            );
            checkedCopy[checkedIndex] = `${key}:${
              requiredValue === 'true' ? 'false' : 'true'
            }${type}`;
            this.onCheckHandler(checkedCopy);
          }

          this.updateCheckboxNode(
            checkboxPath,
            nodeCopy,
            `required: ${requiredValue === 'true' ? 'false' : 'true'}`
          );

          this.setState({
            nodes: nodeCopy,
            editingPart: '',
          });
        },
      });
    } else {
      const checkboxPath = this.getCheckboxPath(nodeElement);
      const escaped = nodeElement.innerHTML.replace(/\//g, '%2F');
      const { nodes } = this.state;

      if (
        escaped in this.endpoints ||
        checkboxPath[checkboxPath.length - 2] === 'parameters-array' ||
        checkboxPath[checkboxPath.length - 2] === 'properties'
      ) {
        data[0].push({
          label: 'Rename',
          onClick: () => {
            const { editingPart } = this.state;
            if (!editingPart) {
              const newText = document.createElement('textarea'); // create new textarea
              newText.value = nodeElement.innerHTML;

              newText.rows = 1;
              newText.cols = 25;

              const nodeCopy = deepcopy(nodes);

              this.updateCheckboxNode(checkboxPath, nodeCopy, '');

              this.setState({
                nodes: nodeCopy,
                editingPart: 'endpoint',
              });

              nodeElement.parentNode.insertBefore(
                newText,
                nodeElement.nextSibling
              );
            }
          },
        });
      }

      if (
        (possibleOperations.includes(nodeElement.innerHTML) ||
          checkboxPath[checkboxPath.length - 2] === 'parameters-array' ||
          checkboxPath[checkboxPath.length - 2] === 'properties' ||
          checkboxPath[checkboxPath.length - 1] === '200' ||
          checkboxPath[checkboxPath.length - 1] === 'items-array') &&
        !this.searchCheckboxNode(checkboxPath, nodes, 'description:')
      ) {
        data[0].push({
          label: 'Add Description',
          onClick: () => {
            this.setState({
              openDescriptionModal: true,
              pathParts: checkboxPath,
            });
          },
        });
      }

      if (
        possibleOperations.includes(nodeElement.innerHTML) ||
        checkboxPath.includes('responses')
      ) {
        data[0].push({
          label: 'Add Link',
          onClick: () => {
            this.setState({ openLinkModal: true, pathParts: checkboxPath });
          },
        });
      }
    }

    const handle = ContextMenu.showMenu(data);

    // Optional operations
    handle.onShow(() => {
      /** impl */
    });
    handle.onClose(() => {
      /** impl */
    });
    handle.close();
  };

  /**
   * Get the node
   * @method
   * @param {HTMLElement} el - HTML element node
   * @returns HTML element node
   */
  getNodeElement = (el) => {
    let node = el;
    while (node.className !== 'rct-text') {
      node = node.parentNode;
    }
    return node.querySelector('.rct-title');
  };

  /**
   * Handle a new endpoint
   * @method
   * @param {String} endpoint - Endpoint from the swagger
   * @param {String} endpointName - User created endpoint name
   * @param {Object} path - nodes that lead to endpoint
   */
  handleNewEndpoint = (endpoint, endpointName, path) => {
    const escapedName = endpointName.replace(/\//g, '%2F');
    const newNode = {
      value: `${escapedName}`,
      label: endpointName,
      children: [],
    };
    this.getNewEndpointCheckbox(path, newNode.children, `${escapedName}`);

    this.endpoints = { ...this.endpoints, [escapedName]: endpoint };

    this.setState((prevState) => ({
      nodes: [...prevState.nodes, newNode],
    }));
  };

  /**
   * Get the node's parent
   * @method
   * @param {Array} path - array containing nodes leading to the targeted checkbox
   * @param {Object} nodes - all checkbox nodes
   * @returns the parent
   */
  getParent = (path, nodes) => {
    if (path.length === 0) {
      return nodes;
    }
    const nextNodes = nodes.find(
      (node) =>
        node.value.substring(node.value.lastIndexOf('/') + 1) === path[0]
    );
    const newPath = path.slice(1);
    return this.getParent(newPath, nextNodes.children);
  };

  /**
   * Handle a new description specified from the text area
   * @method
   * @param {String} description - Description
   */
  handleNewDescription = (description) => {
    const { pathParts, nodes } = this.state;
    const newNodes = deepcopy(nodes);
    const parent = this.getParent(pathParts, newNodes);
    const path = pathParts.join('/');

    parent.unshift({
      value: `${path}/description:${description.replace(
        /\//g,
        '%2F'
      )}-type:string`,
      label: `description: ${description}`,
    });

    this.setState({ nodes: newNodes });
  };

  /**
   * Handle adding a new link
   * @method
   * @param {String} newLinkName - Name of new link
   * @param {String} targetEndpoint - Name of the target endpoint
   * @param {String} parameterName - Name of parameter
   * @param {String} value - Name of the value
   */
  handleNewLink = (newLinkName, targetEndpoint, parameterName, value) => {
    const { nodes, pathParts } = this.state;
    const [endpoint, operation] = [...pathParts];

    const newNodes = deepcopy(nodes);
    const endpointIndex = newNodes.findIndex((node) => node.label === endpoint);
    const operationIndex = newNodes[endpointIndex].children.findIndex(
      (node) => node.label === operation
    );
    const responsesIndex = newNodes[endpointIndex].children[
      operationIndex
    ].children.findIndex((node) => node.label === 'responses');

    newNodes[endpointIndex].children[operationIndex].children[
      responsesIndex
    ].children.push({
      value: `${endpoint}/${operation}/responses/links`,
      label: 'links',
      children: [
        {
          value: `${endpoint}/${operation}/responses/links/${newLinkName.replace(
            /\//g,
            '%2F'
          )}`,
          label: newLinkName,
          children: [
            {
              value: `${endpoint}/${operation}/responses/links/${newLinkName.replace(
                /\//g,
                '%2F'
              )}/target: ${targetEndpoint.replace(/\//g, '%2F')}-type:string`,
              label: `target: ${targetEndpoint}`,
            },
            {
              value: `${endpoint}/${operation}/responses/links/${newLinkName.replace(
                /\//g,
                '%2F'
              )}/parameters`,
              label: 'parameters',
              children: [
                {
                  value: `${endpoint}/${operation}/responses/links/${newLinkName.replace(
                    /\//g,
                    '%2F'
                  )}/parameters/${parameterName.replace(
                    /\//g,
                    '%2F'
                  )}: ${value.replace(/\//g, '%2F')}-type:string`,
                  label: `${parameterName}: ${value}`,
                },
              ],
            },
          ],
        },
      ],
    });

    this.setState({ nodes: newNodes });
  };

  /**
   * Convert the checkbox tree checked nodes to the translation file
   * @method
   * @param {Array} parts - Array containing strings that leads to this node
   * @param {Object} translationFile - object representing translation file
   */
  checkedToTranslation = (parts, translationFile) => {
    if (parts.length !== 0) {
      const desanitized = parts[0].replace(/%2F/gi, '/').trim();

      if (parts.length === 1) {
        if (!desanitized.includes('-type:')) {
          translationFile.push(desanitized);
        } else {
          const type = desanitized.substring(desanitized.lastIndexOf(':') + 1);
          const field = desanitized
            .substring(0, desanitized.indexOf(':'))
            .replace(/%3A/gi, ':');
          let value = desanitized.substring(
            desanitized.indexOf(':') + 1,
            desanitized.lastIndexOf('-')
          );
          if (type === 'number') {
            value = parseInt(value, 10);
          }
          translationFile[field] =
            value === 'true' || (value === 'false' ? false : value);
        }
      } else if (desanitized.includes('array')) {
        const cleaned = desanitized.substring(0, desanitized.indexOf('-array'));
        translationFile[cleaned] = translationFile[cleaned] || [];

        if (parts.length === 2) {
          this.checkedToTranslation(parts.splice(1), translationFile[cleaned]);
        } else {
          let index = translationFile[cleaned].findIndex(
            (el) => el.name === parts[1].replace(/%2F/gi, '/').trim()
          );

          if (index === -1) {
            translationFile[cleaned].push({
              name: parts[1].replace(/%2F/gi, '/').trim(),
            });
            index = translationFile[cleaned].length - 1;
          }
          this.checkedToTranslation(
            parts.splice(2),
            translationFile[cleaned][index]
          );
        }
      } else {
        translationFile[desanitized] = translationFile[desanitized] || {};
        this.checkedToTranslation(
          parts.splice(1),
          translationFile[desanitized]
        );
      }
    }
  };

  /**
   * Handle new checked nodes
   * @method
   * @param {Array} newChecked - array of the newly checked nodes
   */
  onCheckHandler = (newChecked) => {
    const { onTranslationChange, swaggerFile } = this.props;

    const translationFile = {
      title: swaggerFile.info.title,
      version: swaggerFile.info.version,
      endpoints: {},
    };

    newChecked.forEach((node) => {
      const parts = node.split('/');
      translationFile.endpoints[this.endpoints[parts[0]]] =
        translationFile.endpoints[this.endpoints[parts[0]]] || {};

      this.checkedToTranslation(
        parts,
        translationFile.endpoints[this.endpoints[parts[0]]]
      );
    });

    this.setState({ checked: newChecked, translationFile }, () => {
      onTranslationChange(translationFile);
    });
  };

  onExpandHandler = (newExpanded) => {
    this.setState({ expanded: newExpanded });
  };

  render() {
    const { swaggerFile } = this.props;
    const {
      translationFile,
      nodes,
      checked,
      expanded,
      openLinkModal,
      openDescriptionModal,
      pathParts,
    } = this.state;
    return (
      <div className="translation">
        <h2>Translation</h2>

        <div className="translation-btn">
          <div className="new">
            <input
              id="new-translation"
              type="button"
              onClick={this.newTranslationFile}
              disabled={!swaggerFile}
              className="custom-file-action"
            />

            <label
              htmlFor="new-translation"
              className={`custom-file-action ${!swaggerFile ? 'disabled' : ''}`}
            >
              Create New Translation
            </label>
          </div>

          <div className="existing">
            <input
              id="existing-translation"
              type="file"
              name="file"
              onChange={this.onOpenTranslation}
              accept="application/JSON"
              disabled={!swaggerFile}
            />

            <label
              htmlFor="existing-translation"
              className={`custom-file-action ${!swaggerFile ? 'disabled' : ''}`}
            >
              Open Existing Translation
            </label>
          </div>
        </div>

        <div className="save">
          <input
            id="save-translation"
            type="submit"
            onClick={this.saveTranslation}
            disabled={!swaggerFile}
            className="custom-file-action"
            accept="application/JSON"
          />

          <label
            htmlFor="save-translation"
            className={`custom-file-action ${!swaggerFile ? 'disabled' : ''}`}
          >
            Save Translation
          </label>
        </div>

        <div className="add">
          {translationFile && (
            <EndpointPopup
              endpoints={Object.keys(swaggerFile.paths)}
              onNewEndpoint={(endpoint, endpointName) =>
                this.handleNewEndpoint(
                  endpoint,
                  endpointName,
                  swaggerFile.paths[endpoint]
                )
              }
            />
          )}
        </div>

        <div className="translation-checkbox-tree">
          <CheckboxTree
            nodes={nodes}
            checkModel="leaf"
            checked={checked}
            expanded={expanded}
            onCheck={(newChecked) => this.onCheckHandler(newChecked)}
            onExpand={(newExpanded, targetNode) =>
              this.onExpandHandler(newExpanded, targetNode)
            }
          />

          <LinkPopup
            openModal={openLinkModal}
            pathParts={pathParts}
            onNewLink={(newLinkName, targetEndpoint, parameterName, value) =>
              this.handleNewLink(
                newLinkName,
                targetEndpoint,
                parameterName,
                value
              )
            }
            handleClose={() => this.setState({ openLinkModal: false })}
          />

          <DescriptionPopup
            openModal={openDescriptionModal}
            pathParts={pathParts}
            onNewDescription={(description) =>
              this.handleNewDescription(description)
            }
            handleClose={() => this.setState({ openDescriptionModal: false })}
          />
        </div>
      </div>
    );
  }
}

Translation.defaultProps = {
  swaggerFile: PropTypes.objectOf(PropTypes.object),
};

Translation.propTypes = {
  onTranslationChange: PropTypes.func.isRequired,
  swaggerFile: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
};
