#!/usr/bin/env node
/**
 * make_fixture_loader
 * Generates a script that loads the test fixtures into the in-browser filesystem
 * USING the in-browser filesystem.
 */
import * as path from 'path';
import * as fs from 'fs';

const fixturesPath = 'test/fixtures/files';
const files: string[] = []
const dirs = ['tmp']
const DEBUG = false
let uid = 0

// Used to print debug messages.
function debugPrint(...args: string[]) {
  if (DEBUG) console.log.apply(console, args)
}

// Sanity check
if (!fs.existsSync(path.resolve('.', 'src', 'core', 'browserfs.ts'))) {
  throw new Error('FixtureLoaderMaker must be run from the BrowserFS root!');
}

debugPrint('Opening load_fixtures.ts...');
const outfile = fs.openSync(path.resolve('.', 'test', 'fixtures', 'load_fixtures.ts'), 'w+');

// path0, data0
// mkdir occurs one by one

// Checks if the directory is in the listing of unique directories.
// If not, it recursively checks its parent before adding itself to the
// list. This maintains the invariant that no child directory occurs in the list
// before its parent directory.
function tryAddToDirs(dir: string) {
  if (dir !== '.' && dirs.indexOf(dir) < 0) {
    tryAddToDirs(path.dirname(dir));
    debugPrint(`Adding ${dir} to list of directories to create...`);
    dirs.push(dir)
  }
}

// Recursively sort all files into directories / files
const processDirs = [fixturesPath]
while (processDirs.length > 0) {
  const workingDir = processDirs.pop()!;
  // Check if we need to make this directory, or any of its parents.
  tryAddToDirs(workingDir);
  const workingList = fs.readdirSync(workingDir);
  workingList.forEach((item) => {
    const itemPath = `${workingDir}/${item}`;
    const itemStat = fs.statSync(itemPath);
    if (itemStat.isFile()) {
      debugPrint(`Adding ${itemPath} to list of files to create...`);
      files.push(itemPath.replace(/\\/g, '/'));
    } else {
      processDirs.push(itemPath);
    }
  });
}

// Generate the file!

// Header
fs.writeSync(outfile, `
  // Generated by scripts/make_fixture_loader.ts
  // Used by unit testing only. Preloads needed testing files.
  import * as BrowserFS from '../../src/index';
  const fs = BrowserFS.BFSRequire('fs');
  export default function (): void {
    let nextDir: number;
    let dirs = ${JSON.stringify(dirs)};
    let mcb = function(err?: NodeJS.ErrnoException): void {
      if (err && err.code !== 'EEXIST') throw err;
      nextDir++;
      if (nextDir === dirs.length) {
        __fixturesAddFiles();
      } else {
        fs.mkdir(dirs[nextDir],mcb);
      }
    };
    let fcb = function(p: string, writtenData: string) {
      return function(err?: NodeJS.ErrnoException) {
        if (err) throw err;
        fs.readFile(p, {encoding:"base64"}, function(err: NodeJS.ErrnoException, readData: string) {
          if (err) throw err;
          if (writtenData != readData) throw new Error('Read data for '+p+' does not match written data:\\n'+readData+'\\n!=\\n'+writtenData);
        });
      };
    };

    let __fixturesAddFiles = function() {

  `);
files.forEach((file) => {
  const id = uid++;
  const data = fs.readFileSync(file);
  const datab64 = data.toString('base64');
  fs.writeSync(outfile, `
      let p${id} = "${file}";
      let d${id} = "${datab64}";
      fs.writeFile(p${id}, d${id}, {encoding: "base64"}, fcb(p${id}, d${id}));
  `);
});
fs.writeSync(outfile, `
  };

  // Begin loading fixtures.
  if (fs.getRootFS().isReadOnly()) { return; }
  nextDir = -1;
  // Ensure that root directory works.
  fs.exists('/', function(doesExist) {
    if (!doesExist) throw new Error("Invalid filesystem: Root does not exist.");
    mcb();
  });
};
`);

fs.closeSync(outfile);
