 log      = log4js.getLogger("[#{config.name}]")
 {exec}   = require "child_process"
 {spawn}  = require "child_process"
-nodePath = require 'path'
-
-safeForUser = (username, path)-> path?.indexOf("/Users/#{username}/#{config.baseMountDir}/") is 0
-escapePath  = (path, keepSpaces = no)->
-  if path
-    path = nodePath.normalize path.replace(/[^a-zA-Z0-9\/\-. ]/g, '')
-    if keepSpaces then return path
-    path.replace(/\\/g, '')
-        .replace(/\s/g, '\\ ')
+
+# Utilities
+{normalizeUserPath,
+ safeForUser,
+ escapePath,
+ makedirp,
+ slugify,
+ chownr,
+ getIds,
+ KodingError,
+ AuthorizationError} = require '../applications/utils.coffee'
 
 mounter =
 
-  # this module provides functions for mount remote FTP and SSH servers with fuse-curlftpfs and fuse-sshfs
-
-  # usage:
-  #
-  # MOUNT methods
-  #    options =
-  #      type: <sting> # this value doesn't require for mounting. It is only for config files (can be "ssh" or "ftp")
-  #      username: <string> # Koding account name
-  #      remoteuser: <string> # user on ftp or ssh server
-  #      remotepass: <sting> # plain text password for ftp/ssh user. Note: for ssh it can be ommited if ssh key exists, use sshkey: <bool> for this
-  #      remotehost: <string> # remote SSH/FTP hostname (FQDN)
-  #      sshkey: <bool> # set true for ssh mounts if ssh key exists
-  #
-  #   mountFtpDrive options,(err,res)->
-  #   mountSshDrive options,(err,res)->
-  #
-
-  # UMOUNT methods
-  #   options =
-  #      username: <string> # Koding account name
-  #      remotehost: <string> # remote SSH/FTP hostname (FQDN)
-  #
-  #   umountDrive options,(err,res)->
-
-  # read all mounts related to user
-  #   options =
-  #      username: <string> # Koding account name
-  #
-  #   readMountInfo options,(err,res)->
-
-
+  mountDrive : (options, callback)->
+
+    # mount already registered FTP/SSH remote to the users home directory
+
+    # options
+    #   remotetype : String # If its not provided we will try to guess from config
+    #   remotehost : String # remote FTP/SFTP hostname
+    #   remotepass : String # plain text password for ftp/ssh user.
+    #   storepass  : Bool   # store password in config, default false
+
+    # return
+    #   callback error
+    #   callback null, result (information message)
+
+    {username, remotehost, remotepass, remotetype} = options
+
+    @readMountInfo options, (err,res)=>
+      if err?
+        callback err
+      else
+        mounts = [mount for mount in res when mount.remotehost is remotehost][0]
+
+        if mounts.length is 0 or res.length is 0
+          callback "No such remote registered with '#{remotehost}' name."
+          return no
+
+        if mounts.length > 1 and remotetype
+          mounts = [mount for mount in mounts when mount.remotetype is remotetype][0]
+          if mounts.length > 1
+            callback "Duplicate entries for same remote, ask Koding admins for help."
+            return no
+
+        mount = mounts[0]
+
+        mount.username  = options.username
+        mount.storepass = options.storepass
+
+        switch mount.remotetype
+          when "ftp"
+            console.log "Calling....."
+            @mountFtpDrive mount, callback
+
+  # Safe
+  mountFtpDrive : (options, callback)->
+
+    # mount FTP to the users home directory
+
+    # options
+    #   remoteuser : String # user on ftp server
+    #   remotepass : String # plain text password for ftp user.
+    #   remotehost : String # remote FTP hostname (FQDN)
+    #   storepass  : Bool   # store password in config, default false
+
+    # return
+    #   callback error
+    #   callback null, result (information message)
+
+    options.remoteuser or= 'anonymous'
+    options.remotepass or= 'anonymous'
+    options.storepass    = yes if options.remotepass is 'anonymous'
+    options.remotetype   = 'ftp'
+
+    {username, remoteuser, remotepass, remotehost, storepass} = options
+
+    unless remotehost
+      callback "Remote host is not provided"
+      return no
+
+    options.mountpoint = escapePath path.join config.usersPath, username, config.baseMountDir, remotehost
+
+    unless safeForUser username, options.mountpoint
+      console.error "User [#{username}] is trying to make something bad: ", options.mountpoint
+      callback "You are not authorized to do this."
+      return no
+
+    # fetch user ID for curlftfs command
+    @spawnWrapper '/usr/bin/id', ['-u', username], (err,res)=>
+      if err?
+        log.error error = "[ERROR] Can't find user ID: #{err}"
+        callback error
+      else
+        uid = res.trim()
+        log.info "[OK] user ID for user #{username} is #{uid}"
+        ftpfsopts = "#{config.ftpfs.opts},uid=#{uid},gid=#{uid},fsname=#{remotehost},user=#{remoteuser}:#{remotepass}"
+        @createMountpoint options, (err,res)=>
+          if err
+            callback err
+          else
+            @spawnWrapper config.ftpfs.curlftpfs,['-o', ftpfsopts, remotehost, options.mountpoint] , (err, res)=>
+              if err?
+                log.error error = "[ERROR] couldn't mount remote FTP server #{remotehost}: #{err}"
+                callback error
+              else
+                @remountVE options, (err,res)=>
+                  if err?
+                    callback err
+                  else
+                    @updateMountCfg options,(err,res)->
+                    callback null,res
+
+  # Safe
+  umountDrive : (options, callback)->
+
+    # umount FTP from user's home directory
+
+    # return
+    #   callback error
+    #   callback null, result (information message)
+
+    # options =
+    #   username   : String # koding username
+    #   remotehost : String # FTP server address
+
+    {username, remotehost} = options
+
+    unless remotehost
+      callback "Remote host is not given"
+      return no
+
+    options.mountpoint = escapePath path.join config.usersPath, username, config.baseMountDir, remotehost
+
+    unless safeForUser username, options.mountpoint
+      console.error "User [#{username}] is trying to make something bad: ", options.mountpoint
+      callback "You are not authorized to do this."
+      return no
+
+    @spawnWrapper '/bin/umount', [options.mountpoint],(err, res)=>
+      if err?
+        log.error error = "[ERROR] can't umount #{options.mountpoint}: #{err}"
+        callback error
+      else
+        log.info info = "[OK] directory #{options.mountpoint} umounted"
+        @remountVE options,(err,res)=>
+          if err
+            callback err
+          else
+            # @removeMount options,(err,res)->
+            callback null,res
+
+  # Safe
   readMountInfo: (options, callback)->
-     # this method will return user's mounts from mount config /Users/<username>/.mounts
-
-     # return
-     #   callback error
-     #   callback null, array of objects
-
-     # options =
-     #   username : String # koding username
-
-     {username} = options
-
-     cfg = path.join config.usersPath, username, config.usersMountsFile
-
-     fs.readFile cfg,(err,data)->
-       if err?
-         log.error error = "[ERROR] Couldn't read config file #{cfg}: #{err.message}"
-         callback error
-       else
-         try
-           currentConf = JSON.parse data
-           log.info "[OK] config #{cfg} successfully parsed"
-           callback null, currentConf
-         catch error
-           log.error error = "[ERROR] Couldn't parse config #{cfg}: #{error}"
-           callback error
-
-
+
+    # Return user's mounts from mount config /Users/<username>/.mounts
+
+    # return
+    #   callback error
+    #   callback null, array of objects
+
+    # options =
+    #   username : String # Koding account name
+
+    {username} = options
+
+    cfg = path.join config.usersPath, username, config.usersMountsFile
+
+    fs.readFile cfg,(err,data)->
+      if err?
+        log.error error = "[ERROR] Couldn't read config file #{cfg}: #{err.message}"
+        callback error
+      else
+        try
+          currentConf = JSON.parse data
+          log.info "[OK] config #{cfg} successfully parsed"
+          callback null, currentConf
+        catch error
+          log.error error = "[ERROR] Couldn't parse config #{cfg}: #{error}"
+          callback error
+
+  # Safe
   updateMountCfg : (options, callback)->
 
-     # this method update user's mount config file  /Users/<username>/.mounts
-
-
-     # return
-     #   callback error
-     #   callback null, result (information message)
-
-     # options =
-     #   username : String # koding username
-     #   type : String # type of mount (ftp,ssh)
-     #   remoteuser : String # FTP username
-     #   remotehost : String # FTP server address
-     #   mountpoint : String # mountpoint for remount drive
-
-     {username, type, remoteuser, remotehost, mountpoint} = options
-
-     newConf =
-       type: type
-       remotehost: remotehost
-       remoteuser: remoteuser
-       mountpoint: mountpoint
-
-     cfg = path.join config.usersPath, username, config.usersMountsFile
-
-     fs.stat cfg ,(err,stats)=>
-       if stats
-         @readMountInfo options, (err, res)->
-           if err?
-             callback err
-           else
-             trigger = 0
-             for obj in res
-               if _.isEqual obj, newConf
-                 trigger += 1
-                 break
-             if trigger is 0
-               res.push newConf
-               jsonConf = JSON.stringify res
-               fs.writeFile cfg, jsonConf, 'utf-8',(err)->
-                 if err?
-                   log.error error = "[error] couldn't update config #{cfg}: #{err.message}"
-                   callback error
-                 else
-                   log.info info = "[ok] config successfully updated"
-                   callback null, info
-             else
-               log.warn warn = "[WARN] config for #{remotehost} already exists"
-               callback null, warn
-       else
-         jsonConf = JSON.stringify [newConf]
-         fs.writeFile cfg, jsonConf, 'utf-8',(err)->
-           if err?
-             log.error error = "[error] couldn't update config #{cfg}: #{err.message}"
-             callback error
-           else
-             fs.chmod cfg,'0600',(err)->
-             log.info info = "[ok] config successfully created"
-             callback null, info
-
-
-  cleanMountConf: (options, callback)->
+    # this method update user's mount config file  /Users/<username>/.mounts
+
+    # return
+    #   callback error
+    #   callback null, result (information message)
+
+    # options =
+    #   username : String # koding username
+    #   remotetype : String # remotetype of mount (ftp,ssh)
+    #   remoteuser : String # FTP username
+    #   remotehost : String # FTP server address
+    #   mountpoint : String # mountpoint for remount drive
+    #   storepass  : Bool   # store password in config, default false
+
+    {username, remotetype, remoteuser, remotehost, mountpoint, storepass} = options
+
+    newConf = {remotetype, remotehost, remoteuser, mountpoint}
+
+    if storepass
+      newConf.remotepass = options.remotepass
+
+    cfg = escapePath path.join config.usersPath, username, config.usersMountsFile
+
+    unless safeForUser username, cfg
+      console.error "User [#{username}] is trying to make something bad: ", cfg
+      callback "You are not authorized to do this."
+      return no
+
+    console.log newConf
+
+    fs.stat cfg, (err,stats)=>
+      if stats
+        @readMountInfo options, (err, res)->
+          if err?
+            callback err
+          else
+            trigger = 0
+            for obj in res
+              if _.isEqual obj, newConf
+                trigger += 1
+                break
+            if trigger is 0
+              res.push newConf
+              jsonConf = JSON.stringify res
+              fs.writeFile cfg, jsonConf, 'utf-8',(err)->
+                if err?
+                  log.error error = "[error] couldn't update config #{cfg}: #{err.message}"
+                  callback error
+                else
+                  log.info info = "[ok] config successfully updated"
+                  callback null, info
+            else
+              log.warn warn = "[WARN] config for #{remotehost} already exists"
+              callback null, warn
+      else
+        jsonConf = JSON.stringify [newConf]
+        fs.writeFile cfg, jsonConf, 'utf-8',(err)->
+          if err?
+            log.error error = "[error] couldn't update config #{cfg}: #{err.message}"
+            callback error
+          else
+            fs.chmod cfg,'0600',(err)->
+            log.info info = "[ok] config successfully created"
+            callback null, info
+
+  removeMount: (options, callback)->
 
     # this method will remove mount config related to remote host from user's mount config
 
-     # return
-     #   callback error
-     #   callback null, result (information message)
-
+    # return
+    #   callback error
+    #   callback null, result (information message)
 
     # options =
     #   remotehost : String # remote server address
-    #   username : String # koding username
-
-    {remotehost,username} = options
+    #   username   : String # koding username
+
+    {remotehost, username} = options
 
     cfg = path.join config.usersPath, username, config.usersMountsFile
 

          else
            log.info info = "[OK] config successfully updated"
            callback null, info

  spawnWrapper : (command, args , callback)->
    wrapper = spawn command,args

        log.info info = "[OK] command executed successfully output: #{res}"
        callback null, res

  # Safe
  createMountpoint : (options, callback)->

    # create mount point for remote resource
    # /Users/<username>/RemoteDrive/<remote_hostname>

    # return
    #   callback error
    #   callback null, result (information message)

    # options =
    #  mountpoint : String # mountpoint for remount drive

    {mountpoint, username} = options

    unless mountpoint
      callback "Mountpoint is not provided"
      return no

    unless safeForUser username, mountpoint
      console.error "User [#{username}] is trying to make something bad: ", mountpoint
      callback "You are not authorized to do this."
      return no

    fs.stat mountpoint, (err,stats)->
      if stats
        log.info info = "[OK] #{mountpoint} already exists"
        callback null, info
      else
        mkdirp mountpoint, 0o0755, (err)->
          if err?
            log.error error = "[ERROR] Couldn't create mountpoint #{mountpoint}: #{err.message}"
            callback error
          else
            chownr {username, path:mountpoint}, (err)->
              unless err
                log.info info = "[OK] mountpoint #{mountpoint} created"
                callback null, info
              else
                log.error "An error occured:", err
                callback err


module.exports = mounter