'use babel';

import fs from 'fs';
import os from 'os';
import path from 'path';
import { exec } from 'child_process';
import { Task } from 'atom';
import bluebird from 'bluebird';
import glob from 'glob';
import { filterFalsyArray } from './util';

const readFileAsync = bluebird.promisify(fs.readFile);
const globAsync = bluebird.promisify(glob);

function findRtJar() {
  return new Promise((resolve, reject) => {
    exec('java -verbose -help', (err, stdout, stderr) => { // Cannot promisify due to multiple args in callback
      return (err) ? reject(err) : resolve(stdout.match(/[^\s]+rt\.jar/)[0]);
    });
  });
}

function trimEntries(entries) {
  return entries.map(entry => entry.trim());
}

function handleMissingClasspath(err) {
  if (err.code === 'ENOENT') {
    return [];
  }
  throw new Error(err);
}

function resolveEntries(entries) {
  return Promise.all(entries.map(entry => {
    if (entry.endsWith('.jar') || entry.endsWith('.class')) {
      return Promise.resolve([ entry ]);
    }

    return globAsync(`${entry}/**/*.*(class|jar)`);
  }));
}

function flattenEntries(entries) {
  return [].concat.apply([], entries);
}

function readClassPath() {
  const paths = atom.project.getPaths();
  if (paths.length > 1) {
    return Promise.reject(new Error('Only one open project supported.'));
  } else if (paths.length === 0) {
    return Promise.resolve([]);
  }

  return Promise.all([ readFileAsync(`${paths[0]}/.classpath`), findRtJar() ])
    .then(([ classpath, rtJar ]) => classpath.toString('utf8').split(path.delimiter).concat(rtJar))
    .catch(handleMissingClasspath)
    .then(filterFalsyArray)
    .then(trimEntries)
    .then(resolveEntries)
    .then(flattenEntries);
}

export const collect = (dictionary) => {
  return readClassPath().then(classpathEntries => new Promise((resolve, reject) => {
    const tasks = os.cpus().length - 1; // leave one cpu free for other tasks, such as rendering
    const chunk = Math.ceil(classpathEntries.length / tasks);

    let done = 0;
    const donefn = () => (++done === tasks) && resolve();

    for (let i = 0; i < tasks; ++i) {
      const entries = classpathEntries.slice(i * chunk, (i + 1) * chunk);
      const task = Task.once(require.resolve('./collector-task'), entries, donefn);
      task.on('entry', (entry) => dictionary.add(entry.name, entry));
    }
  }));
};

@@ -352,25 +352,28 @@
 unction 
-findRtJar
+implicitLibs
 () %7B%0A  r
@@ -535,76 +535,328 @@
     
-return (err) ? reject(err) : resolve(stdout.match(/%5B%5E%5Cs%5D+rt%5C.jar/)%5B0
+// Output sample: %60%5BLoaded java.lang.Shutdown from /Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/jre/lib/rt.jar%5D%60%0A      const match = stdout.match(/%5C%5BLoaded (?:%5B%5E%5Cs%5D+) from (.*)%5C/.*%5C.jar%5C%5D/);%0A      return (err %7C%7C !match) ?%0A        reject(err %7C%7C 'Failed to find implicit libs') :%0A        resolve(match%5B1
 %5D);%0A
@@ -1704,17 +1704,20 @@
 %60), 
-findRtJar
+implicitLibs
 () %5D
@@ -1742,21 +1742,20 @@
 sspath, 
-rtJar
+libs
  %5D) =%3E c
@@ -1813,13 +1813,12 @@
 cat(
-rtJar
+libs
 ))%0A 

Load all implicit libraries in the JDK