// SPDX-License-Identifier: GPL-2.0

#define _FILE_OFFSET_BITS       64
#define __USE_FILE_OFFSET64
#define _XOPEN_SOURCE 600
#define _DEFAULT_SOURCE

#include <stdio.h>
#include <inttypes.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <locale.h>
#include <limits.h>
#include <assert.h>
#include <blkid/blkid.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/fs.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <uuid/uuid.h>

#include "bcache.h"
#include "lib.h"
#include "bitwise.h"
#include "zoned.h"
#include "features.h"
#include "list.h"

int show_bdevs_detail(void)
{
	struct list_head head;
	struct dev *devs, *n;

	INIT_LIST_HEAD(&head);
	int ret;

	ret = list_bdevs(&head);
	if (ret != 0) {
		fprintf(stderr, "Failed to list devices\n");
		return ret;
	}
	printf("Name\t\tUuid\t\t\t\t\tCset_Uuid\t\t\t\tType\t\t\tState");
	printf("\t\t\tBname\t\tAttachToDev\tAttachToCset\n");
	list_for_each_entry_safe(devs, n, &head, dev_list) {
		printf("%s\t%s\t%s\t%lu", devs->name, devs->uuid,
		       devs->cset, devs->version);
		switch (devs->version) {
			// These are handled the same by the kernel
		case BCACHE_SB_VERSION_CDEV:
		case BCACHE_SB_VERSION_CDEV_WITH_UUID:
		case BCACHE_SB_VERSION_CDEV_WITH_FEATURES:
			printf(" (cache)");
			break;
			// The second adds data offset supporet
		case BCACHE_SB_VERSION_BDEV:
		case BCACHE_SB_VERSION_BDEV_WITH_OFFSET:
		case BCACHE_SB_VERSION_BDEV_WITH_FEATURES:
			printf(" (data)");
			break;
		default:
			printf(" (unknown)");
			break;
		}
		printf("\t\t%-16s", devs->state);
		printf("\t%-16s", devs->bname);
		char attachdev[30];

		if (strlen(devs->attachuuid) == 36) {
			cset_to_devname(&head, devs->cset, attachdev);
		} else if (devs->version == BCACHE_SB_VERSION_CDEV
			   || devs->version == BCACHE_SB_VERSION_CDEV_WITH_UUID
			   || devs->version ==
			   BCACHE_SB_VERSION_CDEV_WITH_FEATURES) {
			strcpy(attachdev, BCACHE_NO_SUPPORT);
		} else {
			strcpy(attachdev, BCACHE_ATTACH_ALONE);
		}
		printf("%-16s", attachdev);
		printf("%s", devs->attachuuid);
		putchar('\n');
	}
	free_dev(&head);
	return 0;
}


int show_bdevs(void)
{
	struct list_head head;
	struct dev *devs, *n;

	INIT_LIST_HEAD(&head);
	int ret;

	ret = list_bdevs(&head);
	if (ret != 0) {
		fprintf(stderr, "Failed to list devices\n");
		return ret;
	}

	printf("Name\t\tType\t\tState\t\t\tBname\t\tAttachToDev\n");
	list_for_each_entry_safe(devs, n, &head, dev_list) {
		printf("%s\t%lu", devs->name, devs->version);
		switch (devs->version) {
			// These are handled the same by the kernel
		case BCACHE_SB_VERSION_CDEV:
		case BCACHE_SB_VERSION_CDEV_WITH_UUID:
		case BCACHE_SB_VERSION_CDEV_WITH_FEATURES:
			printf(" (cache)");
			break;

			// The second adds data offset supporet
		case BCACHE_SB_VERSION_BDEV:
		case BCACHE_SB_VERSION_BDEV_WITH_OFFSET:
		case BCACHE_SB_VERSION_BDEV_WITH_FEATURES:
			printf(" (data)");
			break;

		default:
			printf(" (unknown)");
			break;
		}

		printf("\t%-16s", devs->state);
		printf("\t%-16s", devs->bname);

		char attachdev[30];

		if (strlen(devs->attachuuid) == 36) {
			cset_to_devname(&head, devs->cset, attachdev);
		} else if (devs->version == BCACHE_SB_VERSION_CDEV
			   || devs->version == BCACHE_SB_VERSION_CDEV_WITH_UUID
			   || devs->version ==
			   BCACHE_SB_VERSION_CDEV_WITH_FEATURES) {
			strcpy(attachdev, BCACHE_NO_SUPPORT);
		} else {
			strcpy(attachdev, BCACHE_ATTACH_ALONE);
		}
		printf("%s", attachdev);
		putchar('\n');
	}
	free_dev(&head);
	return 0;
}

int detail_single(char *devname)
{
	struct bdev bd;
	struct cdev cd;
	int type = 1;
	int ret;

	ret = detail_dev(devname, &bd, &cd, &type);
	if (ret != 0) {
		fprintf(stderr, "Failed to detail device\n");
		return ret;
	}
	if (type == BCACHE_SB_VERSION_BDEV ||
	    type == BCACHE_SB_VERSION_BDEV_WITH_OFFSET ||
	    type == BCACHE_SB_VERSION_BDEV_WITH_FEATURES) {
		printf("sb.magic\t\t%s\n", bd.base.magic);
		printf("sb.first_sector\t\t%" PRIu64 "\n",
		       bd.base.first_sector);
		printf("sb.csum\t\t\t%" PRIX64 "\n", bd.base.csum);
		printf("sb.version\t\t%" PRIu64, bd.base.version);
		printf(" [backing device]\n");
		putchar('\n');
		printf("dev.label\t\t");
		if (*bd.base.label)
			print_encode(bd.base.label);
		else
			printf("(empty)");
		putchar('\n');
		printf("dev.uuid\t\t%s\n", bd.base.uuid);
		printf("dev.sectors_per_block\t%u\n"
		       "dev.sectors_per_bucket\t%u\n",
		       bd.base.sectors_per_block,
		       bd.base.sectors_per_bucket);
		printf("dev.data.first_sector\t%u\n"
		       "dev.data.cache_mode\t%d",
		       bd.first_sector, bd.cache_mode);
		switch (bd.cache_mode) {
		case CACHE_MODE_WRITETHROUGH:
			printf(" [writethrough]\n");
			break;
		case CACHE_MODE_WRITEBACK:
			printf(" [writeback]\n");
			break;
		case CACHE_MODE_WRITEAROUND:
			printf(" [writearound]\n");
			break;
		case CACHE_MODE_NONE:
			printf(" [no caching]\n");
			break;
		default:
			putchar('\n');
		}
		printf("dev.data.cache_state\t%u", bd.cache_state);
		switch (bd.cache_state) {
		case BDEV_STATE_NONE:
			printf(" [detached]\n");
			break;
		case BDEV_STATE_CLEAN:
			printf(" [clean]\n");
			break;
		case BDEV_STATE_DIRTY:
			printf(" [dirty]\n");
			break;
		case BDEV_STATE_STALE:
			printf(" [inconsistent]\n");
			break;
		default:
			putchar('\n');
		}

		putchar('\n');
		printf("cset.uuid\t\t%s\n", bd.base.cset);
	} else if (type == BCACHE_SB_VERSION_CDEV ||
		   type == BCACHE_SB_VERSION_CDEV_WITH_UUID ||
		   type == BCACHE_SB_VERSION_CDEV_WITH_FEATURES) {
		printf("sb.magic\t\t%s\n", cd.base.magic);
		printf("sb.first_sector\t\t%" PRIu64 "\n",
		       cd.base.first_sector);
		printf("sb.csum\t\t\t%" PRIX64 "\n", cd.base.csum);
		printf("sb.version\t\t%" PRIu64, cd.base.version);
		printf(" [cache device]\n");
		print_cache_set_supported_feature_sets(&cd.base.sb);
		putchar('\n');
		printf("dev.label\t\t");
		if (*cd.base.label)
			print_encode(cd.base.label);
		else
			printf("(empty)");
		putchar('\n');
		printf("dev.uuid\t\t%s\n", cd.base.uuid);
		printf("dev.sectors_per_block\t%u\n"
		       "dev.sectors_per_bucket\t%u\n",
		       cd.base.sectors_per_block,
		       cd.base.sectors_per_bucket);
		printf("dev.cache.first_sector\t%u\n"
		       "dev.cache.cache_sectors\t%ju\n"
		       "dev.cache.total_sectors\t%ju\n"
		       "dev.cache.ordered\t%s\n"
		       "dev.cache.discard\t%s\n"
		       "dev.cache.pos\t\t%u\n"
		       "dev.cache.replacement\t%d",
		       cd.first_sector,
		       cd.cache_sectors,
		       cd.total_sectors,
		       cd.ordered ? "yes" : "no",
		       cd.discard ? "yes" : "no", cd.pos, cd.replacement);
		switch (cd.replacement) {
		case CACHE_REPLACEMENT_LRU:
			printf(" [lru]\n");
			break;
		case CACHE_REPLACEMENT_FIFO:
			printf(" [fifo]\n");
			break;
		case CACHE_REPLACEMENT_RANDOM:
			printf(" [random]\n");
			break;
		default:
			putchar('\n');
		}

		putchar('\n');
		printf("cset.uuid\t\t%s\n", cd.base.cset);
	} else {
		return 1;
	}
	return 0;
}
