/**
 * Contains functions for actually commiting changes to the backend, mutating the interior composition tree
 * and causing the changes to be reflected in the visualization.
 */

import R from 'ramda';

import { getNodeData } from 'src/data/compositionTree/nodeTypes';
import { addNode, deleteNode, replaceNode } from 'src/interop';
import { denormalizeNode } from 'src/helpers/compositionTree/normalization';
import { getLeafAttr, getNodeParent } from 'src/selectors/compositionTree';
import { NULL_UUID } from 'src/data/misc';
import { mapIdsToEntites } from 'src/helpers/compositionTree/util';

/**
 * Finds the path to a node in the composition tree and returns it as an array of integers.
 */
const getNodeCoords = (entities, nodeId) => {
  let coordBuf = [];
  let curId = nodeId;

  while(true) {
    if(curId === NULL_UUID) {
      break;
    }

    const { id, type, settings, children } = getNodeParent(entities.nodes, curId);

    const indexOffset = getLeafAttr('indexOffset', getNodeData(type), mapIdsToEntites(entities.settings, settings));
    coordBuf = [children.indexOf(curId) - indexOffset, ...coordBuf];

    curId = id;
  }

  return coordBuf;
};

export const commitChanges = (entities, { new: newNodes, updated: updatedNodes, deleted: deletedNodes }) => {
  // console.log('COMMITING CHANGES: ', newNodes, updatedNodes, deletedNodes);

  // first handle all deleted nodes
  deletedNodes.forEach( ({ id, parentId, index }) => {
    const coords = getNodeCoords(entities, parentId);
    const { type: parentType, settings: parentSettingIds } = entities.nodes[parentId];
    const parentSettings = mapIdsToEntites(entities.settings, parentSettingIds);
    const indexOffset = getLeafAttr('indexOffset', getNodeData(parentType), parentSettings);

    deleteNode(coords, index - indexOffset);
  });

  // then, replace all modified nodes with newly-built versions.
  updatedNodes.forEach(nodeId => {
    const def = JSON.stringify(denormalizeNode(entities, nodeId));
    const coords = getNodeCoords(entities, nodeId);

    // console.log("Replacing node: ", def);
    replaceNode(R.init(coords), R.last(coords), def);
  });

  // finally, handle all new nodes, parsing them into their denormalized form and creating them on the backend
  newNodes.forEach(nodeId => {
    // only add it if it's of type `'noiseModule'`
    if(entities.nodes[nodeId].type === 'noiseModule') {
      const def = JSON.stringify(denormalizeNode(entities, nodeId));
      const coords = getNodeCoords(entities, nodeId);

      // Allocate memory in the Emscripten heap and call the backend function
      // console.log("Adding node: ", def);
      addNode(R.init(coords), R.last(coords), def);
    }
  });
};

@@ -441,16 +441,34 @@
 deParent
+, getSettingByName
  %7D from 
@@ -500,16 +500,16 @@
 nTree';%0A
-
 import %7B
@@ -882,40 +882,18 @@
 nst 
-%7B id, type, settings, children %7D
+parentNode
  = g
@@ -924,24 +924,129 @@
 des, curId);
+%0A    if(!parentNode) %7B%0A      return false;%0A    %7D%0A    const %7B id, type, settings, children %7D = parentNode;
 %0A%0A    const 
@@ -1609,90 +1609,587 @@
     
-const %7B type: parentType, settings: parentSettingIds %7D = entities.nodes%5BparentId%5D;
+if(coords === false) %7B%0A      // node's parent has been deleted already so we don't have to do anything%0A      return;%0A    %7D%0A%0A    const %7B type: parentType, settings: parentSettingIds %7D = entities.nodes%5BparentId%5D;%0A    const mappedParentSettings = mapIdsToEntites(entities.settings, parentSettingIds);%0A%0A    // don't try to delete this if the parent node isn't composed (switching from composed node to leaf node)%0A    const parentModuleType = getSettingByName(mappedParentSettings, 'moduleType');%0A    if(parentModuleType !== 'Composed' && parentType !== 'root') %7B%0A      return;%0A    %7D%0A
 %0A   

Handle issue when changing from composed node to leaf node