// Copyright (c) 2017 The Regents of the University of Michigan.
// All Rights Reserved. Licensed according to the terms of the Revised
// BSD License. See LICENSE.txt for details.

let treeNode = (address, setUp) => new Promise(
    function(resolveNode, rejectNode) {
  let internal = {};
  let payload = {};
  let runnableNode = {};

  internal.errors = [];
  internal.children = [];
  internal.addLogger = function(logger) {
    payload.log = newAddressedLogFunction(address, logger);
  };

  payload.runBefore = () => {};
  payload.run = () => {};
  payload.runAfter = () => {};

  payload.add = function(childSetUp) {
    internal.children.push(
      treeNode(address.concat(internal.children.length),
               childSetUp))
  };

  runnableNode.run = logger => new Promise(
      function(resolveRun, rejectRun) {
    internal.addLogger(logger);
    payload.log("begin");

    let runningChildren = [];
    let throwError = function() {
      rejectRun({
        description: "root",
        children: {length: 2},
        messages: []
      });
    };

    Promise.resolve(payload.runBefore()).then(function() {
      return Promise.resolve(payload.run());

    }, function(error) {
      rejectRun();

    }).then(function() {
      for (let child of runnableNode.children)
        runningChildren.push(child.run(logger));

      return Promise.all(runningChildren);

    }, function(error) {
      rejectRun();

    }).then(function() {
      return Promise.resolve(payload.runAfter());

    }, function(error) {
      let errorResolutions = [];
      let childErrors = [];

      for (let child of runningChildren) {
        errorResolutions.push(new Promise(function(resolveError) {
          child.then(function(value) {
            resolveError();
          }, function(childError) {
            childErrors.push(childError);
            resolveError();
          });
        }));
      }

      return new Promise(function(good, bad) {
        Promise.all(errorResolutions).then(function(value) {
          let result = {
            description: "root",
            children: childErrors,
            messages: []
          };

          bad(result);
        });
      });

    }).then(function() {
      payload.log("done");
      resolveRun();

    }, function(error) {
      rejectRun(error);
    });
  });

  runAndWait(function() { setUp(payload); }).then(function(value) {
    return Promise.all(internal.children);

  }, function(error) {
    internal.errors.push(error);
    return Promise.all(internal.children);

  }).then(function(childIterator) {
    runnableNode.children = [...childIterator];
    runnableNode.getLogTree = function() {
      let result = {};

      if (payload.description)
        result.d = payload.description;

      result.c = [];
      for (let leaf of runnableNode.children)
        result.c.push(leaf.getLogTree());

      return result;
    };

    if (internal.errors.length == 0)
      resolveNode(runnableNode);

    else
      rejectNode({
        description: payload.description,
        messages: internal.errors,
        children: []
      });

  }, function(error) {
    let errorResolutions = [];
    let childErrors = [];

    for (let child of internal.children) {
      errorResolutions.push(new Promise(function(resolveError) {
        child.then(function(value) {
          resolveError();
        }, function(childError) {
          childErrors.push(childError);
          resolveError();
        });
      }));
    }

    Promise.all(errorResolutions).then(function(value) {
      rejectNode({
        description: payload.description,
        messages: internal.errors,
        children: childErrors
      });
    });
  });
});

let newAddressedLogFunction = (address, logger) =>
  (code, message) => new Promise((resolve, reject) => {
    let fullMessage = [Date.now(), code].concat(address);
    fullMessage.push(message || "");

    Promise.resolve(logger.log(fullMessage)).then(value => {
      resolve(value);
    }, passError(reject));
});

let passError = function(reject) {
  return function(error) {
    reject(error);
  };
};

let runAndWait = function(thingToRun) {
  try {
    return Promise.resolve(thingToRun());

  } catch (error) {
    return Promise.reject(error);
  }
};

module.exports = (newLogger, setUp) => new Promise(
    function(resolve, reject) {
  treeNode([], setUp).then(function(root) {
    let logger = newLogger(root.getLogTree());
    logger.storeProcess(root.run(logger));

    resolve(logger);

  }, passError(reject));
});

@@ -912,158 +912,8 @@
  %5B%5D;
-%0A    let throwError = function() %7B%0A      rejectRun(%7B%0A        description: %22root%22,%0A        children: %7Blength: 2%7D,%0A        messages: %5B%5D%0A      %7D);%0A    %7D;
 %0A%0A  

Remove unused function