package process

import (
	"fmt"
	"io/ioutil"
	"log"
	"strconv"
	"strings"
)

type ProcList map[int]string

func NewProcList() *ProcList {
	pl := make(ProcList)
	return &pl
}

func (pl ProcList) Refresh(print bool) error {
	pids, err := getPIDs()
	if err != nil {
		return err
	}

	for i := len(pids) - 1; i >= 0; i-- {
		pid := pids[i]
		_, ok := pl[pid]
		if !ok {
			cmd, err := getCmd(pid)
			if err != nil {
				cmd = "???" // process probably terminated
			}
			uid, err := getUID(pid)
			if err != nil {
				uid = "???"
			}
			if print {
				log.Printf("\x1b[31;1mCMD: UID=%-4s PID=%-6d | %s\x1b[0m\n", uid, pid, cmd)
			}
			pl[pid] = cmd
		}
	}

	return nil
}

func getPIDs() ([]int, error) {
	proc, err := ioutil.ReadDir("/proc")
	if err != nil {
		return nil, fmt.Errorf("opening proc dir: %v", err)
	}

	pids := make([]int, 0)
	for _, f := range proc {
		if f.IsDir() {
			name := f.Name()
			pid, err := strconv.Atoi(name)
			if err != nil {
				continue // not a pid
			}
			pids = append(pids, pid)
		}
	}
	return pids, nil
}

func getCmd(pid int) (string, error) {
	cmdPath := fmt.Sprintf("/proc/%d/cmdline", pid)
	cmd, err := ioutil.ReadFile(cmdPath)
	if err != nil {
		return "", err
	}
	for i := 0; i < len(cmd); i++ {
		if cmd[i] == 0 {
			cmd[i] = 32
		}
	}
	return string(cmd), nil
}

func getUID(pid int) (string, error) {
	statPath := fmt.Sprintf("/proc/%d/status", pid)
	stat, err := ioutil.ReadFile(statPath)
	if err != nil {
		return "", err
	}
	lines := strings.Split(string(stat), "\n")
	if len(lines) < 9 {
		return "", fmt.Errorf("no uid information")
	}
	uidL := strings.Split(lines[8], "\t")
	if len(uidL) < 2 {
		return "", fmt.Errorf("uid line read incomplete")
	}
	return uidL[1], nil
}
