#!/usr/bin/env coffee
# FixtureLoaderMaker
# Generates a script that loads the test fixtures into the in-browser filesystem
# USING the in-browser filesystem.
"use strict"
path = require 'path'
fs = require 'fs'

fixturesPath = 'test/fixtures/files'
outfile = null
files = []
dirs = ['tmp']
DEBUG = false
uid = 0

# Used to print debug messages.
debugPrint = (args...) -> if DEBUG then console.log.apply console, args

# Sanity check
unless fs.existsSync(path.resolve('.', 'src', 'core', 'browserfs.ts'))
  throw new Error 'FixtureLoaderMaker must be run from the BrowserFS root!'

debugPrint 'Opening load_fixtures.js...'
outfile = fs.openSync(path.resolve('.', 'test', 'fixtures', 'load_fixtures.js'), '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.
tryAddToDirs = (dir) ->
  return if dir is '.'
  if dirs.indexOf(dir) < 0
    tryAddToDirs path.dirname(dir)
    debugPrint "Adding #{dir} to list of directories to create..."
    dirs.push(dir)
  return

# Recursively sort all files into directories / files
processDirs = [fixturesPath]
while processDirs.length > 0
  workingDir = processDirs.pop()
  # Check if we need to make this directory, or any of its parents.
  tryAddToDirs workingDir
  workingList = fs.readdirSync workingDir
  for item in workingList
    itemPath = "#{workingDir}/#{item}"
    itemStat = fs.statSync itemPath
    if itemStat.isFile()
      debugPrint "Adding #{itemPath} to list of files to create..."
      files.push itemPath
    else
      processDirs.push itemPath

# Generate the file!

# Header
fs.writeSync outfile, """
  // Generated by tools/FixtureLoaderMaker.coffee
  // Used by unit testing only. Preloads needed testing files.
  module.exports = function (){
    "use strict";
    var nextDir;
    var dirs = #{JSON.stringify(dirs)};
    var fs = require('fs');
    var mcb = function(err) {
      if (err && err.code !== 'EEXIST') throw err;
      nextDir++;
      if (nextDir === dirs.length) {
        __fixturesAddFiles();
      } else {
        fs.mkdir(dirs[nextDir],mcb);
      }
    };
    var fcb = function(p, writtenData) {
      return function(err) {
        if (err) throw err;
        fs.readFile(p, {encoding:"base64"}, function(err, readData) {
          if (err) throw err;
          if (writtenData != readData) throw new Error('Read data for '+p+' does not match written data:\\n'+readData+'\\n!=\\n'+writtenData);
        });
      };
    };

    var __fixturesAddFiles = function() {

  """
for file in files
  id = uid++
  data = fs.readFileSync file
  datab64 = data.toString 'base64'
  fs.writeSync outfile, """
      var p#{id} = "#{file}";
      var 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;
