import { ResourcesDirectory } from './api';
import { join } from 'path';
import { spawn } from 'child_process';
import * as readline from 'readline';
import { RendererApi } from '../common/rendererApi';

const PYTHON = process.platform == 'win32' ? 'python.exe' : 'python3';
const GET_PLATFORMIO_PY = join(ResourcesDirectory, 'platformio-core', 'get-platformio.py');

// platformio の実行ファイルを取得
const _getExe = () =>
  new Promise<string>((resolve, reject) => {
    const p = spawn(PYTHON, [GET_PLATFORMIO_PY, 'check', 'core']);
    p.on('error', e => {
      console.log('Fail to run python:' + e.message);
      reject(e.message);
    });
    let result: string | null = null;
    readline
      .createInterface({
        input: p.stdout,
        terminal: false,
      })
      .on('line', data => {
        console.log(data);
        if (data.startsWith('Found compatible')) result = data.split('->')[1].trim();
      });
    readline
      .createInterface({
        input: p.stderr,
        terminal: false,
      })
      .on('line', data => {
        console.error(data);
      });
    p.on('close', () => (result ? resolve(result) : reject()));
  });

// platformio core をインストール
const _install = () =>
  new Promise<void>((resolve, reject) => {
    const p = spawn(PYTHON, [GET_PLATFORMIO_PY]);
    p.on('error', e => {
      console.log('Fail to run python:' + e.message);
      reject(e.message);
    });
    p.on('close', code => (code == 0 ? resolve() : reject()));
    readline
      .createInterface({
        input: p.stderr,
        terminal: false,
      })
      .on('line', data => {
        console.error(data);
      });
    readline
      .createInterface({
        input: p.stdout,
        terminal: false,
      })
      .on('line', data => {
        console.log(data);
      });
  });

// 見つからなければインストール
const _checkAndInstall = async (): Promise<string> => {
  try {
    return await _getExe();
  } catch {
    // ignore
  }
  await _install();
  return _getExe();
};

// インストールしているプロミス
let installPromise: Promise<string> | null = null;
/**
 * PlatformIO の実行ファイルのパスを返します。
 * インストールされていない場合はインストールを行います
 */
export const getPlatformIO = () => {
  if (installPromise == null) installPromise = _checkAndInstall();
  return installPromise;
};

export const runPlatformIO = async (
  args: string[],
  stdoutWriter?: (line: string) => void,
  stderrWriter?: (ilne: string) => void
) => {
  const exe = await getPlatformIO();
  return new Promise<void>((resolve, reject) => {
    const p = spawn(exe, args);
    p.on('error', e => {
      reject(e.message);
    });
    readline
      .createInterface({
        input: p.stdout,
        terminal: false,
      })
      .on('line', data => {
        if (stdoutWriter) stdoutWriter(data);
      });
    readline
      .createInterface({
        input: p.stderr,
        terminal: false,
      })
      .on('line', data => {
        if (stderrWriter) stderrWriter(data);
      });
    p.on('close', code => (code == 0 ? resolve() : reject()));
  });
};

export const build = async (rapi: RendererApi, directory: string) => {
  const args = ['run', '-s', '-d', directory];
  rapi.cmd('pio', args);
  await runPlatformIO(args, rapi.log, rapi.log);
};

export const upload = async (rapi: RendererApi, directory: string, port: string) => {
  const args = ['run', '-d', directory, '--upload-port', port, '-t', 'upload'];
  rapi.cmd('pio', args);
  await runPlatformIO(args, rapi.log, rapi.log);
};
