var os = require('os')
var fs = require('fs')
var p = require('path')
var exec = require('child_process').exec
var spawn = require('child_process').spawn
var helpers = require('./helpers')
var format = require('util').format
var PLATFORM = os.platform()

var stats = {
  history: {},
  cpu: null, // used to store cpu informations
  proc: function (pid, options, done) {
    var self = this

    if (this.cpu !== null) {
      fs.readFile('/proc/uptime', 'utf8', function (err, uptime) {
        if (err) {
          return done(err, null)
        }

        if (uptime === undefined) {
          console.error("[pidusage] We couldn't find uptime from /proc/uptime")
          self.cpu.uptime = os.uptime()
        } else {
          self.cpu.uptime = uptime.split(' ')[0]
        }

        return self.proc_calc(pid, options, done)
      })
    } else {
      helpers.cpu(function (err, cpu) {
        if (err) {
          return done(err, null)
        }

        self.cpu = cpu
        return self.proc_calc(pid, options, done)
      })
    }
  },
  proc_calc: function (pid, options, done) {
    pid = parseInt(pid, 10)
    var history = this.history[pid] ? this.history[pid] : {}
    var cpu = this.cpu
    var self = this

    // Arguments to path.join must be strings
    fs.readFile(p.join('/proc', '' + pid, 'stat'), 'utf8', function (err, infos) {
      if (err) {
        return done(err, null)
      }

      // https://github.com/arunoda/node-usage/commit/a6ca74ecb8dd452c3c00ed2bde93294d7bb75aa8
      // preventing process space in name by removing values before last ) (pid (name) ...)
      var index = infos.lastIndexOf(')')
      infos = infos.substr(index + 2).split(' ')

      // according to http://man7.org/linux/man-pages/man5/proc.5.html (index 0 based - 2)
      // In kernels before Linux 2.6, start was expressed in jiffies. Since Linux 2.6, the value is expressed in clock ticks
      var stat = {
        utime: parseFloat(infos[11]),
        stime: parseFloat(infos[12]),
        cutime: parseFloat(infos[13]),
        cstime: parseFloat(infos[14]),
        start: parseFloat(infos[19]) / cpu.clockTick,
        rss: parseFloat(infos[21])
      }

      // http://stackoverflow.com/questions/16726779/total-cpu-usage-of-an-application-from-proc-pid-stat/16736599#16736599
      var childrens = options.childrens ? stat.cutime + stat.cstime : 0
      var total = stat.stime - (history.stime || 0) + stat.utime - (history.utime || 0) + childrens

      total = total / cpu.clockTick

      // time elapsed between calls
      var seconds = history.uptime !== undefined ? cpu.uptime - history.uptime : stat.start - cpu.uptime
      seconds = Math.abs(seconds)
      seconds = seconds === 0 ? 1 : seconds // we sure can't divide through 0

      self.history[pid] = stat
      self.history[pid].uptime = cpu.uptime

      var cpuPercent = (total / seconds) * 100
      var memory = stat.rss * cpu.pagesize

      if (!options.advanced) {
        return done(null, {
          cpu: cpuPercent,
          memory: memory
        })
      }

      return done(null, {
        cpu: cpuPercent,
        memory: memory,
        time: stat.utime + stat.stime,
        start: stat.start
      })
    })
  },
  /**
   * Get pid informations through ps command
   * @param  {int}   pid
   * @return  {Function} done (err, stat)
   * on os x skip headers with pcpu=,rss=
   * on linux it could be --no-header
   * on solaris 11 can't figure out a way to do this properly so...
   */
  ps: function (pid, options, done) {
    pid = parseInt(pid, 10)
    var cmd = 'ps -o pcpu,rss -p '

    if (options.advanced) {
      cmd = 'ps -o pcpu,rss,time,start -p '

      if (PLATFORM === 'aix') {
        cmd = 'ps -o pcpu,rssize,time,start -p '
      }
    } else if (PLATFORM === 'aix') {
      cmd = 'ps -o pcpu,rssize -p ' // this one could work on other platforms see AIX section in man ps
    }

    exec(cmd + pid, function (err, stdout, stderr) {
      if (err) {
        return done(err, null)
      }

      stdout = stdout.split(os.EOL)[1]
      stdout = stdout.replace(/^\s+/, '').replace(/\s\s+/g, ' ').split(' ')

      var cpuPercent = parseFloat(stdout[0].replace(',', '.'))
      var memory = parseFloat(stdout[1]) * 1024

      if (!options.advanced) {
        return done(null, {
          cpu: cpuPercent,
          memory: memory
        })
      }

      return done(null, {
        cpu: cpuPercent,
        memory: memory,
        time: parseFloat(stdout[2]),
        start: parseFloat(stdout[3])
      })
    })
  },
  /**
   * This is really in a beta stage
   */
  win: function (pid, options, done) {
    pid = parseInt(pid, 10)
    var history = this.history[pid] ? this.history[pid] : {}
    // http://social.msdn.microsoft.com/Forums/en-US/469ec6b7-4727-4773-9dc7-6e3de40e87b8/cpu-usage-in-for-each-active-process-how-is-this-best-determined-and-implemented-in-an?forum=csharplanguage
    var args = 'PROCESS ' + pid + ' get workingsetsize,usermodetime,kernelmodetime'

    var wmic = spawn('wmic', args.split(' '), {detached: true})
    var stdout = ''
    var stderr = ''
    var self = this
    // Note: On Windows the returned value includes fractions of a second. Use Math.floor() to get whole seconds.
    var uptime = Math.floor(os.uptime())

    wmic.stdout.on('data', function (d) {
      stdout += d.toString()
    })

    wmic.stderr.on('data', function (d) {
      stderr += d.toString()
    })

    wmic.on('error', function (err) {
      console.error('[pidusage] Command "wmic ' + args + '" failed with error %s', err)
    })

    wmic.on('close', function (code) {
      stdout = stdout.trim()
      stderr = stderr.trim()

      if (!stdout || code !== 0) {
        var error = format('%s Wmic errored, please open an issue on https://github.com/soyuka/pidusage with this message.%s', new Date().toString(), os.EOL)
        error += format('Command was "wmic %s" %s System informations: %s - release: %s %s - type %s %s', args, os.EOL, os.EOL, os.release(), os.EOL, os.type(), os.EOL)
        stderr = error + (stderr ? format('Wmic reported the following error: %s.', stderr) : 'Wmic reported no errors (stderr empty).')
        stderr = format('%s%s%sWmic exited with code %d.', os.EOL, stderr, os.EOL, code)
        stderr = format('%s%sStdout was %s', stderr, os.EOL, stdout || 'empty')

        return done(new Error(stderr, null))
      }

      stdout = stdout.split(os.EOL)[1].replace(/\s\s+/g, ' ').split(' ')

      var stats = {
        kernelmodetime: parseFloat(stdout[0]),
        usermodetime: parseFloat(stdout[1])
      }

      var workingsetsize = parseFloat(stdout[2])

      // process usage since last call
      var total = stats.kernelmodetime - (history.kernelmodetime || 0) + stats.usermodetime - (history.usermodetime || 0)
      total = total / 10000000

      // time elapsed between calls
      var seconds = history.uptime !== undefined ? uptime - history.uptime : 0

      var cpu = 0

      if (seconds > 0) {
        cpu = (total / seconds) * 100
      }

      self.history[pid] = stats
      self.history[pid].uptime = uptime

      if (!options.advanced) {
        return done(null, {cpu: cpu, memory: workingsetsize})
      }

      return done(null, {
        cpu: cpu,
        memory: workingsetsize,
        time: stats.usermodetime + stats.kernelmodetime,
        start: seconds
      })
    })
    wmic.stdin.end()
  }
}

module.exports = stats
