import Rx from 'rxjs/Rx';

import { launch } from 'spawnteract';

import * as uuid from 'uuid';

import {
  ipcRenderer as ipc,
} from 'electron';

import {
  createControlSubject,
  createStdinSubject,
  createIOPubSubject,
  createShellSubject,
} from 'enchannel-zmq-backend';

import {
  createMessage,
} from '../kernel/messaging';

import {
  setExecutionState,
  setNotebookKernelInfo,
} from '../actions';

import {
  NEW_KERNEL,
  LAUNCH_KERNEL,
  SET_LANGUAGE_INFO,
  ERROR_KERNEL_LAUNCH_FAILED,
} from '../constants';

export function setLanguageInfo(langInfo) {
  return {
    type: SET_LANGUAGE_INFO,
    langInfo,
  };
}

export function acquireKernelInfo(channels) {
  const message = createMessage('kernel_info_request');

  const obs = channels.shell
    .childOf(message)
    .ofMessageType(['kernel_info_reply'])
    .first()
    .pluck('content', 'language_info')
    .map(setLanguageInfo);

  return Rx.Observable.create(observer => {
    const subscription = obs.subscribe(observer);
    channels.shell.next(message);
    return subscription;
  });
}

export function newKernelObservable(kernelSpecName, cwd) {
  return Rx.Observable.create((observer) => {
    launch(kernelSpecName, { cwd })
      .then(c => {
        const { config, spawn, connectionFile, kernelSpec } = c;

        const identity = uuid.v4();
        // TODO: I'm realizing that we could trigger on when the underlying sockets
        //       are ready with these subjects to let us know when the kernels
        //       are *really* ready
        const channels = {
          shell: createShellSubject(identity, config),
          iopub: createIOPubSubject(identity, config),
          control: createControlSubject(identity, config),
          stdin: createStdinSubject(identity, config),
        };
        observer.next(setNotebookKernelInfo({
          name: kernelSpecName,
          spec: kernelSpec,
        }));

        observer.next({
          type: NEW_KERNEL,
          channels,
          connectionFile,
          spawn,
          kernelSpecName,
          kernelSpec,
        });
        observer.complete();
      });
  });
}

export const watchExecutionStateEpic = action$ =>
  action$.ofType(NEW_KERNEL)
    .switchMap(action =>
      Rx.Observable.merge(
        action.channels.iopub
          .filter(msg => msg.header.msg_type === 'status')
          .map(msg => setExecutionState(msg.content.execution_state)),
        Rx.Observable.of(setExecutionState('idle'))
      )
    );

export const acquireKernelInfoEpic = action$ =>
  action$.ofType(NEW_KERNEL)
    .switchMap(action => {
      /* istanbul ignore if -- used for interactive debugging */
      if (process.env.DEBUG) {
        window.channels = action.channels;
      }
      return acquireKernelInfo(action.channels);
    });

export const newKernelEpic = action$ =>
  action$.ofType(LAUNCH_KERNEL)
    .do(action => {
      if (!action.kernelSpecName) {
        throw new Error('newKernel needs a kernelSpecName');
      }
      ipc.send('nteract:ping:kernel', action.kernelSpecName);
    })
    .mergeMap(action =>
      newKernelObservable(action.kernelSpecName, action.cwd)
    )
    .catch(error => Rx.Observable.of({
      type: ERROR_KERNEL_LAUNCH_FAILED,
      payload: error,
      error: true,
    }));

@@ -624,24 +624,209 @@
 fo,%0A  %7D;%0A%7D%0A%0A
+/**%0A  * Send a kernel_info_request to the kernel.%0A  *%0A  * @param  %7BObject%7D  channels  A object containing the kernel channels%0A  * @returns  %7BObservable%7D  The reply from the server%0A  */%0A
 export funct
@@ -1247,24 +1247,226 @@
 n;%0A  %7D);%0A%7D%0A%0A
+/**%0A  * Instantiate a connection to a new kernel.%0A  *%0A  * @param  %7BString%7D  kernelSpecName  The name of the kernel to launch%0A  * @param  %7BString%7D  cwd The working directory to launch the kernel in%0A  */%0A
 export funct
@@ -2519,16 +2519,148 @@
  %7D);%0A%7D%0A%0A
+/**%0A  * Sets the execution state after a kernel has been launched.%0A  *%0A  * @oaram  %7BActionObservable%7D  action$ The action type%0A  */%0A
 export c
@@ -3006,24 +3006,135 @@
   )%0A    );%0A%0A
+/**%0A  * Gets information about newly launched kernel.%0A  *%0A  * @param  %7BActionObservable%7D  The action type%0A  */%0A
 export const
@@ -3430,16 +3430,137 @@
    %7D);%0A%0A
+/**%0A  * Launches a new kernel.%0A  *%0A  * @param  %7BActionObservable%7D action$  The action type, kernelSpecName, and cwd%0A  */%0A
 export c

Add docstrings for kernel launch