const { FsManager, FileName, SaveFileRequest } = require("../lib/fm/index.js")
const { Filesystem, MkdirRequest, RmdirRequest, Path, Stat } = require( "../lib/filesystem/index.js")

const {
  nullCheck,
  getValidatedPath,
} = require('./internal/fs_utils');
const {
  parseFileMode
} = require('./internal/validators');


let fm = new FsManager

let fsM = new Filesystem

async function readFile(name) {
  let i = new FileName()
  i.setFilepath(name)
  i.setType("file")

  let content = []
  try {
    await fm.GetFile(i, (fd) => {
      content.push(Buffer.from(fd.getData()))
    })
  } catch(e) {
    throw e
  }
  return Buffer.concat(content)
}

async function writeFile(file, data) {
  let i = new SaveFileRequest()
  i.setFilepath(file)
  i.setData(Buffer.from(data))

  try {
    const resp = await fm.SaveFile(i)
    const err = resp.getErr()
    if (err) {
      throw(err)
    }
  } catch (e) {
    throw(e)
  }
}


function mkdir(path, options, callback) {
  let mode = 0o777;
  let recursive = false;
  if (typeof options === 'function') {
    callback = options;
  } else if (typeof options === 'number' || typeof options === 'string') {
    mode = options;
  } else if (options) {
    if (options.recursive !== undefined)
      recursive = options.recursive;
    if (options.mode !== undefined)
      mode = options.mode;
  }
  path = getValidatedPath(path);
  if (typeof recursive !== 'boolean')
    throw new ERR_INVALID_ARG_TYPE('options.recursive', 'boolean', recursive);

  const req = new MkdirRequest()
  req.setName(path)
  req.setMode(mode)
  req.setRecursive(recursive);
  fsM.Mkdir(req)
    .then(()=>callback(null), (err)=>{callback(err)})
}

function rmdir(path, options, callback) {
  if (typeof options === 'function') {
    callback = options;
    options = undefined;
  }

  path = getValidatedPath(path);

  const req = new RmdirRequest()
  req.setName(path)
  if (options && options.recursive) {
    req.setRecursive(true);
  }

  fsM.Rmdir(req)
    .then(()=>callback(null), (err)=>{callback(err)})
}


const  ModeDir        = 0x80000000; // d: is a directory
const  ModeAppend     = 0x40000000; // a: append-only
const  ModeExclusive  = 0x20000000; // l: exclusive use
const  ModeTemporary  = 0x10000000; // T: temporary file; Plan 9 only
const  ModeSymlink    = 0x8000000;  // L: symbolic link
const  ModeDevice     = 0x4000000;  // D: device file
const  ModeNamedPipe  = 0x2000000;  // p: named pipe (FIFO)
const  ModeSocket     = 0x1000000;  // S: Unix domain socket
const  ModeSetuid     = 0x800000;   // u: setuid
const  ModeSetgid     = 0x400000;   // g: setgid
const  ModeCharDevice = 0x200000;   // c: Unix character device, when ModeDevice is set
const  ModeSticky     = 0x100000;   // t: sticky
const  ModeIrregular  = 0x8f280000; // ?: non-regular file; nothing else is known about this file
  // Mask for the type bits. For regular files, none will be set.
const  ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice | ModeCharDevice | ModeIrregular;
const  ModePerm = 0o777; // Unix permission bits


// modeled on the nodejs' fs.Stats
class Stats {
  constructor(raw) {
    const mtime = raw.modtime
    this.mtimeMS = this.ctimeMS = Date.parse(mtime);
    this.mtime = this.ctime = mtime;

    const now = new Date();
    this.atimeMS = now.getTime();
    this.atime = now.toUTCString();

    this.mode = raw.mode;

    this.size = raw.size;

    // doesn't support now.
    this.dev =
      this.rdev =
      this.blksize =
      this.blocks =
      this.ino =
      this.nlink =
      this.uid =
      this.gid = -1;
  }

  isDirectory() { return (this.mode&ModeDir) != 0 }
  isFIFO() { return (this.mode&ModeNamedPipe) != 0 }
  isFile() { return (this.mode&ModeType) == 0 }
  isSocket() { return (this.mode&ModeSocket) != 0 }
  isSymbolicLink() { return (this.mode&ModeSymlink) != 0 }
}

async function stat(path) {
  let p = new Path();
  p.setPath(getValidatedPath(path))
  return new Stats((await fsM.Stat(p)).toObject())
}


const { promisify } = require("util");

const constants = require("./fs_constants.js");
let all = {
  constants,

  mkdir: promisify(mkdir),
  rmdir: promisify(rmdir),

  rm: promisify(rmdir),

  stat: promisify(async (path, opt, cb) => {
    if (typeof cb !== 'function') {
      cb = opt
      opt = null;
    }

    try {
      cb(null, todo("fs.Stats", await stat(path)))
    } catch (e) {
      cb(e, null);
    }
  }),

  readFile: promisify((name, opt, callback) => {
    callback = getCallback(opt, callback)

    let d = null;
    if (typeof opt === 'string') {
      d = new TextDecoder(opt)
    }

    readFile(name).then((content) => {
      if (d) {
        callback(null, d.decode(content))
      } else {
        callback(null, content)
      }

    }).catch((err) => {
      callback(err, null)
    })

  }),

  writeFile: promisify((name, data, opt, callback) => {
    callback = getCallback(opt, callback)
    writeFile(name, data).then(()=>{
        callback(null);
    }).catch(callback);
  })
}

function getCallback(opt, callback) {
    if (typeof callback !== 'function') {
      callback = opt
      opt = {}
    }
    if (typeof opt === 'string') {
    }
  return callback
}

let { todo } = require("./internal/util.js")

module.exports = all
