#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <unistd.h>
#include <mntent.h>
#include <sys/statvfs.h>
#include "human.h"
#include "config.h"

char a_flag;
char h_flag;
char *t_flag;
off_t block = 1024;

int print(const struct mntent *me) {

	struct statvfs disk;
	if (!strcmp(me->mnt_fsname, "none"))
		return 0;

	if (statvfs(me->mnt_dir, &disk) < 0)
		return 1;

	if (!a_flag)
		if (!strcmp(me->mnt_fsname, "/dev/root") || disk.f_blocks == 0)
			return 0;

	if (t_flag && strcmp(t_flag, me->mnt_type))
		return 0;

	off_t bs = disk.f_frsize / block;
	off_t total = disk.f_blocks * bs;
	off_t avail = disk.f_bfree * bs;
	off_t used = total - avail;
	off_t capacity = (used * 100 + 1) / (avail + used + 1);

	if (h_flag) {
		char total_s[MU_HUMAN_BUF_SIZE + 1];
		char used_s[MU_HUMAN_BUF_SIZE + 1];
		char avail_s[MU_HUMAN_BUF_SIZE + 1];

		snprintf(total_s, sizeof(total_s), "%s", mu_humansize(total * block, block));
		snprintf(used_s, sizeof(used_s), "%s", mu_humansize(used * block, block));
		snprintf(avail_s, sizeof(avail_s), "%s", mu_humansize(avail * block, block));

		printf("%-20s %12s %10s %9s %9s %3jd%% %s\n", me->mnt_fsname, me->mnt_type, total_s, used_s, avail_s, capacity, me->mnt_dir);
	}

	else
		printf("%-20s %12s %12jd %12jd %12jd %12jd%% %s\n", me->mnt_fsname, me->mnt_type, total, used, avail, capacity, me->mnt_dir);

	return 0;
}

int main(int argc, char **argv) {

	int opt;
	while ((opt = getopt(argc, argv, "hHat:")) != -1) {
		switch (opt) {
			case 'h':
				h_flag = 1;
				break;

			case 'H':
				h_flag = 1;
				block = 1000;
				break;

			case 'a':
				a_flag = 1;
				break;

			case 't':
				t_flag = optarg;
				break;

			default:
				puts("df [hHat]\n\t-h Human readable (1024)\n\t-a Show all\n\t-H Human readable (1000)\n\t-t TYPE Print only mounts of this type");
				return 0;
		}
	}

	argv += optind;
	argc -= optind;

	FILE *fp = setmntent("/proc/mounts", "r");
	if (fp == NULL) {
		fprintf(stderr, "df: %s\n", strerror(errno));
		return 1;
	}

	if (h_flag)
		puts("Filesystem                   Type       Size      Used Available Use% Mounted on");

	else
		puts("Filesystem                   Type         Size         Used    Available          Use% Mounted on");


	int ret = 0;

	struct mntent *me;
	while ((me = getmntent(fp)) != NULL) {
		if (argc) {
			for (int i = 0; i < argc; i++) {
				if (!strcmp(argv[i], me->mnt_fsname) || !strcmp(argv[i], me->mnt_dir))
					if (print(me))
						ret = 1;
			}
		}

		else
			print(me);
	}

	endmntent(fp);
	return ret;
}