'use strict';

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _bluebird = require('bluebird');

var _bluebird2 = _interopRequireDefault(_bluebird);

var _path = require('path');

var _path2 = _interopRequireDefault(_path);

var _minimatch = require('minimatch');

var _minimatch2 = _interopRequireDefault(_minimatch);

var _fsp = require('./fsp');

var _fsp2 = _interopRequireDefault(_fsp);

var _lock = require('./lock');

var _lock2 = _interopRequireDefault(_lock);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function joinWith(dir) {
  return function (file) {
    return _path2.default.join(dir, file);
  };
}

/**
 * @class
 */

var File = function () {
  function File(pathname) {
    _classCallCheck(this, File);

    this._dir = process.cwd();
    this._pathname = pathname;
  }

  _createClass(File, [{
    key: '_getStatsSync',
    value: function _getStatsSync() {
      return _fsp2.default.statSync(this._pathname);
    }
  }, {
    key: '_getStats',
    value: function _getStats() {
      return _fsp2.default.statAsync(this._pathname);
    }
  }, {
    key: '_isHiddenFile',
    value: function _isHiddenFile() {
      return (/^\./.test(_path2.default.basename(this._pathname))
      );
    }
  }, {
    key: '_isHiddenDirectory',
    value: function _isHiddenDirectory() {
      return (/(^|\/)\.[^\/\.]/g.test(this._pathname)
      );
    }
  }, {
    key: '_depth',
    value: function _depth(pathname) {
      return pathname.split(_path2.default.sep).length - 1;
    }
  }, {
    key: '_access',
    value: function _access(permission) {
      var hasPermission = true;

      return _fsp2.default.accessAsync(this._pathname, permission).catch(function () {
        return hasPermission = false;
      }).then(function () {
        return hasPermission;
      });
    }
  }, {
    key: '_checkAsyncStats',
    value: function _checkAsyncStats(type) {
      return this._getStats().then(function (stats) {
        return stats[type]();
      });
    }

    /**
     * Synchronously determine if pathname is a directory
     *
     * @instance
     * @memberOf File
     * @method
     * isDirectorySync
     * @return boolean
     * @example
     * import File from 'file-js';
     *
     * const file = File.create('myDirectory');
     * if (file.isDirectorySync()) {
     *    console.log('processing directory');
     * }
     */

  }, {
    key: 'isDirectorySync',
    value: function isDirectorySync() {
      return this._getStatsSync().isDirectory();
    }

    /**
     * Synchronously determine if pathname is a socket
     *
     * @instance
     * @memberOf File
     * @method
     * isSocketSync
     * @return boolean
     * @example
     * import File from 'file-js';
     *
     * const file = File.create('mysocket');
     * if (file.isSocketSync()) {
     *    console.log('processing socket');
     * }
     */

  }, {
    key: 'isSocketSync',
    value: function isSocketSync() {
      return this._getStatsSync().isSocket();
    }

    /**
     * Synchronously determine if pathname is a file
     *
     * @instance
     * @memberOf File
     * @method
     * isFileSync
     * @return boolean
     * @example
     * import File from 'file-js';
     *
     * const file = File.create('myDirectory');
     * if (file.isFileSync()) {
     *    console.log('processing file');
     * }
     */

  }, {
    key: 'isFileSync',
    value: function isFileSync() {
      return this._getStatsSync().isFile();
    }

    /**
     * Determine if pathname is a directory
     *
     * @instance
     * @memberOf File
     * @method
     * isDirectory
     * @return If the Promise fulfils, the fulfilment value is
     * a boolean indicating if the pathname is a directory
     * @example
     * import File from 'file-js';
     *
     * const file = File.create('myDirectory');
     * file.isDirectory((isDirectory) => {
     *   console.log(isDirectory);
     * });
     *
     */

  }, {
    key: 'isDirectory',
    value: function isDirectory() {
      return this._checkAsyncStats('isDirectory');
    }

    /**
     * Determine if pathname is a Socket
     *
     * @instance
     * @memberOf File
     * @method
     * isSocket
     * @return If the Promise fulfils, the fulfilment value is
     * a boolean indicating if the pathname is a Socket
     * @example
     * import File from 'file-js';
     *
     * const file = File.create('mySocket');
     * file.isSocket((isSocket) => {
     *   console.log(isSocket);
     * });
     *
     */

  }, {
    key: 'isSocket',
    value: function isSocket() {
      return this._checkAsyncStats('isSocket');
    }

    /**
     * Determine if pathname is a file
     *
     * @instance
     * @memberOf File
     * @method
     * isDirectory
     * @return If the Promise fulfils, the fulfilment value is
     * a boolean indicating if the pathname is a file
     * @example
     * import File from 'file-js';
     *
     * const file = File.create('myDirectory');
     * file.isFile((isFile) => {
     *   console.log(isFile);
     * });
     */

  }, {
    key: 'isFile',
    value: function isFile() {
      return this._checkAsyncStats('isFile');
    }

    /**
     * Synchronously determine if pathname is a hidden file
     *
     * @instance
     * @memberOf File
     * @method
     * isHiddenSync
     * @return boolean
     * @example
     * import File from 'file-js';
     *
     * const file = File.create('./myHiddenFile');
     * if (file.isHiddenSync()) {
     *    console.log('processing hidden file');
     * }
     */

  }, {
    key: 'isHiddenSync',
    value: function isHiddenSync() {
      if (!this.isDirectorySync()) {
        return this._isHiddenFile();
      }
      return this._isHiddenDirectory();
    }

    /**
     * Determine if pathname is a file
     *
     * @instance
     * @memberOf File
     * @method
     * isDirectory
     * @return If the Promise fulfils, the fulfilment value is
     * a boolean indicating if the pathname is a file
     * @example
     * import File from 'file-js';
     *
     * const file = File.create('myDirectory');
     * file.isFile((isFile) => {
     *   console.log(isFile);
     * });
     */

  }, {
    key: 'isHidden',
    value: function isHidden() {
      var _this = this;

      this.isDirectory().then(function (isDirectory) {
        if (!isDirectory) {
          return _this._isHiddenFile();
        }
        return _this._isHiddenDirectory();
      });
    }

    /**
     *  Renames the abstract pathname
     *
     * @instance
     * @memberOf File
     * @param {string|File} pathname - pathname either as a string or File instance
     * @method
     * rename
     * @return If the Promise fulfils, the fulfilment value is undefined
     * @example
     * import File from 'file-js';
     *
     * const original = File.create('fileA');
     * const renameTo = File.create('fileB');
     * original
     *  .rename(renameTo)
     *  .then(() => {
     *     console.log(original.getName()) // prints fileA
     *  });
     */

  }, {
    key: 'rename',
    value: function rename(pathname) {
      var _this2 = this;

      var newname = pathname instanceof File ? pathname.getName() : pathname;

      return _fsp2.default.renameAsync(this._pathname, newname).then(function () {
        _this2._pathname = newname;
      });
    }

    /**
     * Synchronously get list of files, if pathname is a directory
     *
     * @instance
     * @memberOf File
     * @method
     * getListSync
     * @return array of files
     * @example
     * import File from 'file-js';
     *
     * const file = File.create('./myHiddenFile');
     * const files = file.getListSync();
     * console.log(files);
     */

  }, {
    key: 'getListSync',
    value: function getListSync() {
      var _this3 = this;

      if (this.isDirectorySync()) {
        return _fsp2.default.readdirSync(this._pathname).map(function (file) {
          return _path2.default.join(_this3._pathname, file);
        });
      }
      return null;
    }

    /**
     * Get list of file objects, if pathname is a directory
     *
     * @instance
     * @memberOf File
     * @method
     * getList
     * @param {string=} glob - file glob
     * @return a promise. If the Promise fulfils, the fulfilment value is
     * a list of pathnames
     * @example
     * import File from 'file-js';
     *
     * // get all json files
     * const file = File.create('./myDirectory');
     * file.getFiles('*.json')
     * .then((jsonFiles) => {
     *    console.log(jsonFiles);
     * });
     */

  }, {
    key: 'getList',
    value: function getList(glob) {
      return this.getFiles(glob).then(function (list) {
        if (!list) return [];

        return list.map(function (pathname) {
          return pathname.getName();
        });
      });
    }

    /**
     * Get list of file objects, if pathname is a directory
     *
     * @instance
     * @memberOf File
     * @param {string=} glob - file glob
     * @method
     * getFiles
     * @return a promise. If the Promise fulfils, the fulfilment value is
     * a list of File objects
     * @example
     * import File from 'file-js';
     *
     * // get last modified time of all json files
     * const file = File.create('./myDirectory');
     * file.getFiles('*.json')
     * .then((jsonFiles) => {
     *    console.log(jsonFiles.map(file => file.lastModifiedSync()));
     * });
     */

  }, {
    key: 'getFiles',
    value: function getFiles(glob) {
      if (!this.isDirectory()) return _bluebird2.default.resolve(null);

      var results = _fsp2.default.readdirAsync(this._pathname).map(joinWith(this._pathname)).then(function (list) {
        if (!list) return _bluebird2.default.resolve(null);

        return list.map(function (pathname) {
          return File.create(pathname);
        });
      });

      if (glob) return results.filter(function (file) {
        return file.isMatch(glob);
      });

      return results;
    }

    /**
     * Synchronously get list of file objects, if pathname is a directory
     *
     * @instance
     * @memberOf File
     * @method
     * getFileSync
     * @return array of files
     * @example
     * import File from 'file-js';
     *
     * const file = File.create('./myHiddenFile');
     * const files = file.getFileSync();
     * console.log(files);
     */

  }, {
    key: 'getFilesSync',
    value: function getFilesSync(glob) {
      if (this.isDirectorySync()) {
        var files = this.getListSync().map(function (pathname) {
          return File.create(pathname);
        });

        if (glob) return files.filter(function (file) {
          return file.isMatch(glob);
        });

        return files;
      }
      return null;
    }

    /**
     * Synchronously caculate the depth of a directory
     *
     * @instance
     * @memberOf File
     * @method
     * getDepthSync
     * @return boolean
     * @example
     * import File from 'file-js';
     *
     * const file = File.create('myDirectory');
     * console.log(file.getDepthSync());
     */

  }, {
    key: 'getDepthSync',
    value: function getDepthSync() {
      if (!this.isDirectorySync()) {
        return this._depth(_path2.default.dirname(this._pathname));
      }
      return this._depth(this._pathname);
    }

    /**
     * Returns the pathname as a string
     *
     * @instance
     * @memberOf File
     * @method
     * getName
     * @return String
     * @example
     * import File from 'file-js';
     *
     * const file = File.create('myDirectory');
     * console.log(file.getName());
     */

  }, {
    key: 'getName',
    value: function getName() {
      return this._pathname;
    }

    /**
     * Returns the absolutePath
     *
     * @instance
     * @memberOf File
     * @method
     * getAbsolutePath
     * @return String
     * @example
     * import File from 'file-js';
     *
     * const file = File.create('myFile');
     * console.log(file.getAbsolutePath());
     */

  }, {
    key: 'getAbsolutePath',
    value: function getAbsolutePath() {
      if (_path2.default.isAbsolute(this._pathname)) {
        return this._pathname;
      }
      return [this._dir, this._pathname].join(_path2.default.sep);
    }

    /**
     * Returns the canonical path
     *
     * @instance
     * @memberOf File
     * @method
     * getCanonicalPath
     * @return String
     * @example
     * import File from 'file-js';
     *
     * const file = File.create('myFile');
     * console.log(file.getCanonicalPath());
     */

  }, {
    key: 'getCanonicalPath',
    value: function getCanonicalPath() {
      return _path2.default.normalize(this.getAbsolutePath());
    }

    /**
     * Returns the file extension.
     *
     * @instance
     * @memberOf File
     * @method
     * getPathExtension
     * @return String
     * @example
     * import File from 'file-js';
     *
     * const file = File.create('./tmp.sh');
     * console.log(file.getPathExtension()); // sh
     */

  }, {
    key: 'getPathExtension',
    value: function getPathExtension() {
      return _path2.default.extname(this._pathname).substring(1);
    }
  }, {
    key: 'isMatch',
    value: function isMatch(globPattern) {
      var glob = new _minimatch2.default.Minimatch(globPattern, {
        matchBase: true
      });
      return glob.match(this._pathname);
    }
  }, {
    key: 'lastModifiedSync',
    value: function lastModifiedSync() {
      return this._getStatsSync()['mtime'];
    }
  }, {
    key: 'lastAccessedSync',
    value: function lastAccessedSync() {
      return this._getStatsSync()['atime'];
    }
  }, {
    key: 'lastChangedSync',
    value: function lastChangedSync() {
      return this._getStatsSync()['ctime'];
    }
  }, {
    key: 'sizeSync',
    value: function sizeSync() {
      return this._getStatsSync().size;
    }
  }, {
    key: 'isWritable',
    value: function isWritable() {
      return this._access(_fsp2.default.W_OK);
    }
  }, {
    key: 'isReadable',
    value: function isReadable() {
      return this._access(_fsp2.default.R_OK);
    }
  }, {
    key: 'isExecutable',
    value: function isExecutable() {
      return this._access(_fsp2.default.X_OK);
    }
  }, {
    key: 'delete',
    value: function _delete() {
      return _fsp2.default.unlinkAsync(this._pathname);
    }

    /**
     * Locks the pathname
     *
     * @instance
     * @memberOf File
     * @method
     * withLock
     * @return returning value of function
     * @example
     * import File from 'file-js';
     *
     * const file = File.create('myFile');
     * file.with(() => {
     *   if (file.isFileSync()) {
     *     file.delete();
     *   } 
     * });
     */

  }, {
    key: 'withLock',
    value: function withLock(fn) {
      var _this4 = this;

      return _lock2.default.lockAsync(this._pathname).then(function () {
        return fn();
      }).finally(function () {
        _lock2.default.unlockAsync(_this4._pathname);
      });
    }

    /**
     * Static factory method to create an instance of File
     *
     * @static
     * @memberOf File
     * @method
     * create
     * @return File instance
     * @example
     * import File from 'file-js';
     *
     * const file = File.create();
     */

  }], [{
    key: 'create',
    value: function create(filename) {
      return new File(filename);
    }
  }]);

  return File;
}();

module.exports.create = File.create;