//go:build solaris && cgo

package system

import (
	"fmt"
	"unsafe"
)

// #cgo CFLAGS: -std=c99
// #cgo LDFLAGS: -lkstat
// #include <unistd.h>
// #include <stdlib.h>
// #include <stdio.h>
// #include <kstat.h>
// #include <sys/swap.h>
// #include <sys/param.h>
// struct swaptable *allocSwaptable(int num) {
//	struct swaptable *st;
//	struct swapent *swapent;
// 	st = (struct swaptable *)malloc(num * sizeof(swapent_t) + sizeof (int));
//	swapent = st->swt_ent;
//	for (int i = 0; i < num; i++,swapent++) {
//		swapent->ste_path = (char *)malloc(MAXPATHLEN * sizeof (char));
//	}
//	st->swt_n = num;
//	return st;
//}
// void freeSwaptable (struct swaptable *st) {
//	struct swapent *swapent = st->swt_ent;
//	for (int i = 0; i < st->swt_n; i++,swapent++) {
//		free(swapent->ste_path);
//	}
//	free(st);
// }
// swapent_t getSwapEnt(swapent_t *ent, int i) {
//	return ent[i];
// }
// int64_t getPpKernel() {
//	int64_t pp_kernel = 0;
//	kstat_ctl_t *ksc;
//	kstat_t *ks;
//	kstat_named_t *knp;
//	kid_t kid;
//
//	if ((ksc = kstat_open()) == NULL) {
//		return -1;
//	}
//	if ((ks = kstat_lookup(ksc, "unix", 0, "system_pages")) == NULL) {
//		return -1;
//	}
//	if (((kid = kstat_read(ksc, ks, NULL)) == -1) ||
//	    ((knp = kstat_data_lookup(ks, "pp_kernel")) == NULL)) {
//		return -1;
//	}
//	switch (knp->data_type) {
//	case KSTAT_DATA_UINT64:
//		pp_kernel = knp->value.ui64;
//		break;
//	case KSTAT_DATA_UINT32:
//		pp_kernel = knp->value.ui32;
//		break;
//	}
//	pp_kernel *= sysconf(_SC_PAGESIZE);
//	return (pp_kernel > 0 ? pp_kernel : -1);
// }
import "C"

// Get the system memory info using sysconf same as prtconf
func getTotalMem() int64 {
	pagesize := C.sysconf(C._SC_PAGESIZE)
	npages := C.sysconf(C._SC_PHYS_PAGES)
	return int64(pagesize * npages)
}

func getFreeMem() int64 {
	pagesize := C.sysconf(C._SC_PAGESIZE)
	npages := C.sysconf(C._SC_AVPHYS_PAGES)
	return int64(pagesize * npages)
}

// ReadMemInfo retrieves memory statistics of the host system and returns a
//
//	MemInfo type.
func ReadMemInfo() (*MemInfo, error) {
	ppKernel := C.getPpKernel()
	MemTotal := getTotalMem()
	MemFree := getFreeMem()
	SwapTotal, SwapFree, err := getSysSwap()

	if ppKernel < 0 || MemTotal < 0 || MemFree < 0 || SwapTotal < 0 ||
		SwapFree < 0 {
		return nil, fmt.Errorf("getting system memory info %w", err)
	}

	meminfo := &MemInfo{}
	// Total memory is total physical memory less than memory locked by kernel
	meminfo.MemTotal = MemTotal - int64(ppKernel)
	meminfo.MemFree = MemFree
	meminfo.SwapTotal = SwapTotal
	meminfo.SwapFree = SwapFree

	return meminfo, nil
}

func getSysSwap() (int64, int64, error) {
	var tSwap int64
	var fSwap int64
	var diskblksPerPage int64
	num, err := C.swapctl(C.SC_GETNSWP, nil)
	if err != nil {
		return -1, -1, err
	}
	st := C.allocSwaptable(num)
	_, err = C.swapctl(C.SC_LIST, unsafe.Pointer(st))
	if err != nil {
		C.freeSwaptable(st)
		return -1, -1, err
	}

	diskblksPerPage = int64(C.sysconf(C._SC_PAGESIZE) >> C.DEV_BSHIFT)
	for i := 0; i < int(num); i++ {
		swapent := C.getSwapEnt(&st.swt_ent[0], C.int(i))
		tSwap += int64(swapent.ste_pages) * diskblksPerPage
		fSwap += int64(swapent.ste_free) * diskblksPerPage
	}
	C.freeSwaptable(st)
	return tSwap, fSwap, nil
}
