package process

import (
	
	
	
	
	
	

	
	
	
)

var (
	invoke                 common.Invoker = common.Invoke{}
	ErrorNoChildren                       = errors.New("process does not have children")
	ErrorProcessNotRunning                = errors.New("process does not exist")
)

type Process struct {
	Pid            int32 `json:"pid"`
	name           string
	status         string
	parent         int32
	numCtxSwitches *NumCtxSwitchesStat
	uids           []int32
	gids           []int32
	groups         []int32
	numThreads     int32
	memInfo        *MemoryInfoStat
	sigInfo        *SignalInfoStat
	createTime     int64

	lastCPUTimes *cpu.TimesStat
	lastCPUTime  time.Time

	tgid int32
}

type OpenFilesStat struct {
	Path string `json:"path"`
	Fd   uint64 `json:"fd"`
}

type MemoryInfoStat struct {
	RSS    uint64 `json:"rss"`    // bytes
	VMS    uint64 `json:"vms"`    // bytes
	HWM    uint64 `json:"hwm"`    // bytes
	Data   uint64 `json:"data"`   // bytes
	Stack  uint64 `json:"stack"`  // bytes
	Locked uint64 `json:"locked"` // bytes
	Swap   uint64 `json:"swap"`   // bytes
}

type SignalInfoStat struct {
	PendingProcess uint64 `json:"pending_process"`
	PendingThread  uint64 `json:"pending_thread"`
	Blocked        uint64 `json:"blocked"`
	Ignored        uint64 `json:"ignored"`
	Caught         uint64 `json:"caught"`
}

type RlimitStat struct {
	Resource int32  `json:"resource"`
	Soft     int32  `json:"soft"` // TODO too small. needs to be uint64
	Hard     int32  `json:"hard"` // TODO too small. needs to be uint64
	Used     uint64 `json:"used"`
}

type IOCountersStat struct {
	ReadCount  uint64 `json:"readCount"`
	WriteCount uint64 `json:"writeCount"`
	ReadBytes  uint64 `json:"readBytes"`
	WriteBytes uint64 `json:"writeBytes"`
}

type NumCtxSwitchesStat struct {
	Voluntary   int64 `json:"voluntary"`
	Involuntary int64 `json:"involuntary"`
}

type PageFaultsStat struct {
	MinorFaults      uint64 `json:"minorFaults"`
	MajorFaults      uint64 `json:"majorFaults"`
	ChildMinorFaults uint64 `json:"childMinorFaults"`
	ChildMajorFaults uint64 `json:"childMajorFaults"`
}

// Resource limit constants are from /usr/include/x86_64-linux-gnu/bits/resource.h
// from libc6-dev package in Ubuntu 16.10
const (
	RLIMIT_CPU        int32 = 0
	RLIMIT_FSIZE      int32 = 1
	RLIMIT_DATA       int32 = 2
	RLIMIT_STACK      int32 = 3
	RLIMIT_CORE       int32 = 4
	RLIMIT_RSS        int32 = 5
	RLIMIT_NPROC      int32 = 6
	RLIMIT_NOFILE     int32 = 7
	RLIMIT_MEMLOCK    int32 = 8
	RLIMIT_AS         int32 = 9
	RLIMIT_LOCKS      int32 = 10
	RLIMIT_SIGPENDING int32 = 11
	RLIMIT_MSGQUEUE   int32 = 12
	RLIMIT_NICE       int32 = 13
	RLIMIT_RTPRIO     int32 = 14
	RLIMIT_RTTIME     int32 = 15
)

func ( Process) () string {
	,  := json.Marshal()
	return string()
}

func ( OpenFilesStat) () string {
	,  := json.Marshal()
	return string()
}

func ( MemoryInfoStat) () string {
	,  := json.Marshal()
	return string()
}

func ( RlimitStat) () string {
	,  := json.Marshal()
	return string()
}

func ( IOCountersStat) () string {
	,  := json.Marshal()
	return string()
}

func ( NumCtxSwitchesStat) () string {
	,  := json.Marshal()
	return string()
}

// Pids returns a slice of process ID list which are running now.
func () ([]int32, error) {
	return PidsWithContext(context.Background())
}

func ( context.Context) ([]int32, error) {
	,  := pidsWithContext()
	sort.Slice(, func(,  int) bool { return [] < [] })
	return , 
}

// NewProcess creates a new Process instance, it only stores the pid and
// checks that the process exists. Other method on Process can be used
// to get more information about the process. An error will be returned
// if the process does not exist.
func ( int32) (*Process, error) {
	 := &Process{Pid: }

	,  := PidExists()
	if  != nil {
		return , 
	}
	if ! {
		return , ErrorProcessNotRunning
	}
	_,  = .CreateTime()
	return , 
}

func ( int32) (bool, error) {
	return PidExistsWithContext(context.Background(), )
}

// Background returns true if the process is in background, false otherwise.
func ( *Process) () (bool, error) {
	return .BackgroundWithContext(context.Background())
}

func ( *Process) ( context.Context) (bool, error) {
	,  := .ForegroundWithContext()
	if  != nil {
		return false, 
	}
	return !, 
}

// If interval is 0, return difference from last call(non-blocking).
// If interval > 0, wait interval sec and return diffrence between start and end.
func ( *Process) ( time.Duration) (float64, error) {
	return .PercentWithContext(context.Background(), )
}

func ( *Process) ( context.Context,  time.Duration) (float64, error) {
	,  := .Times()
	if  != nil {
		return 0, 
	}
	 := time.Now()

	if  > 0 {
		.lastCPUTimes = 
		.lastCPUTime = 
		if  := common.Sleep(, );  != nil {
			return 0, 
		}
		,  = .Times()
		 = time.Now()
		if  != nil {
			return 0, 
		}
	} else {
		if .lastCPUTimes == nil {
			// invoked first time
			.lastCPUTimes = 
			.lastCPUTime = 
			return 0, nil
		}
	}

	 := runtime.NumCPU()
	 := (.Sub(.lastCPUTime).Seconds()) * float64()
	 := calculatePercent(.lastCPUTimes, , , )
	.lastCPUTimes = 
	.lastCPUTime = 
	return , nil
}

// IsRunning returns whether the process is still running or not.
func ( *Process) () (bool, error) {
	return .IsRunningWithContext(context.Background())
}

func ( *Process) ( context.Context) (bool, error) {
	,  := .CreateTimeWithContext()
	if  != nil {
		return false, 
	}
	,  := NewProcess(.Pid)
	if  == ErrorProcessNotRunning {
		return false, nil
	}
	,  := .CreateTimeWithContext()
	if  != nil {
		return false, 
	}
	return  == , nil
}

// CreateTime returns created time of the process in milliseconds since the epoch, in UTC.
func ( *Process) () (int64, error) {
	return .CreateTimeWithContext(context.Background())
}

func ( *Process) ( context.Context) (int64, error) {
	if .createTime != 0 {
		return .createTime, nil
	}
	,  := .createTimeWithContext()
	.createTime = 
	return .createTime, 
}

func calculatePercent(,  *cpu.TimesStat,  float64,  int) float64 {
	if  == 0 {
		return 0
	}
	 := .Total() - .Total()
	 := (( / ) * 100) * float64()
	return 
}

// MemoryPercent returns how many percent of the total RAM this process uses
func ( *Process) () (float32, error) {
	return .MemoryPercentWithContext(context.Background())
}

func ( *Process) ( context.Context) (float32, error) {
	,  := mem.VirtualMemory()
	if  != nil {
		return 0, 
	}
	 := .Total

	,  := .MemoryInfo()
	if  != nil {
		return 0, 
	}
	 := .RSS

	return (100 * float32() / float32()), nil
}

// CPU_Percent returns how many percent of the CPU time this process uses
func ( *Process) () (float64, error) {
	return .CPUPercentWithContext(context.Background())
}

func ( *Process) ( context.Context) (float64, error) {
	,  := .CreateTime()
	if  != nil {
		return 0, 
	}

	,  := .Times()
	if  != nil {
		return 0, 
	}

	 := time.Unix(0, *int64(time.Millisecond))
	 := time.Since().Seconds()
	if  <= 0 {
		return 0, nil
	}

	return 100 * .Total() / , nil
}

// Groups returns all group IDs(include supplementary groups) of the process as a slice of the int
func ( *Process) () ([]int32, error) {
	return .GroupsWithContext(context.Background())
}