/*
 * ptouch-print — CLI for libptouch
 *
 * Author: knb
 * Email: knb@artif.org
 */

#include "libptouch.h"

#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>

static void usage(const char *argv0)
{
	fprintf(stderr,
		"Usage: %s [options]\n"
		"  -f, --file PATH     入力ファイル\n"
		"  -w, --width DOTS    1bit ラスター時: 幅（ドット）\n"
		"  -H, --height DOTS   1bit ラスター時: 高さ（ドット）\n"
		"  -t, --threshold N PNG 二値化しきい値 0–255（既定 %u、PNG のみ）\n"
		"  -n, --dry-run       読み込みと check_raster のみ（USB なし）\n"
		"  -S, --status        USB 接続プリンタのステータス（テープ種・幅・色等）を表示して終了\n"
		"  -V, --version       バージョンを表示して終了\n"
		"  -h, --help          このヘルプ\n"
		"\n"
		"PNG の場合は幅・高さはファイルから取得（-w/-H 不要）。\n"
		"1bit packed ラスター（行優先）の場合は -f -w -H が必須。\n"
		"--status のときは -f は不要（他オプションは無視されます）。\n",
		argv0, (unsigned)LIBPTOUCH_PNG_DEFAULT_THRESHOLD);
}

/** パスが .png で終わる、または先頭 8 バイトが PNG シグネチャ */
static int input_is_png(const char *path)
{
	const char *dot = strrchr(path, '.');
	if (dot && strcasecmp(dot, ".png") == 0)
		return 1;

	FILE *fp = fopen(path, "rb");
	if (!fp)
		return 0;
	unsigned char sig[8];
	size_t n = fread(sig, 1, sizeof(sig), fp);
	fclose(fp);
	if (n != sizeof(sig))
		return 0;
	static const unsigned char png_magic[8] = { 137, 80,  78,  71,
						    13,  10, 26, 10 };
	return memcmp(sig, png_magic, sizeof(png_magic)) == 0;
}

static int read_file(const char *path, uint8_t **out, size_t *out_len)
{
	FILE *fp = fopen(path, "rb");
	if (!fp) {
		fprintf(stderr, "open %s: %s\n", path, strerror(errno));
		return -1;
	}
	if (fseek(fp, 0, SEEK_END) != 0) {
		fprintf(stderr, "fseek: %s\n", strerror(errno));
		fclose(fp);
		return -1;
	}
	long sz = ftell(fp);
	if (sz < 0) {
		fprintf(stderr, "ftell: %s\n", strerror(errno));
		fclose(fp);
		return -1;
	}
	rewind(fp);
	uint8_t *buf = malloc((size_t)sz);
	if (!buf) {
		fprintf(stderr, "malloc failed\n");
		fclose(fp);
		return -1;
	}
	size_t n = fread(buf, 1, (size_t)sz, fp);
	fclose(fp);
	if (n != (size_t)sz) {
		fprintf(stderr, "short read\n");
		free(buf);
		return -1;
	}
	*out = buf;
	*out_len = n;
	return 0;
}

int main(int argc, char **argv)
{
	const char *file = NULL;
	unsigned width = 0, height = 0;
	unsigned threshold = LIBPTOUCH_PNG_DEFAULT_THRESHOLD;
	int dry_run = 0;
	int has_threshold = 0;
	int want_status = 0;
	static struct option longopts[] = {
		{ "width", required_argument, NULL, 'w' },
		{ "height", required_argument, NULL, 'H' },
		{ "file", required_argument, NULL, 'f' },
		{ "threshold", required_argument, NULL, 't' },
		{ "dry-run", no_argument, NULL, 'n' },
		{ "status", no_argument, NULL, 'S' },
		{ "version", no_argument, NULL, 'V' },
		{ "help", no_argument, NULL, 'h' },
		{ NULL, 0, NULL, 0 },
	};

	int c;
	while ((c = getopt_long(argc, argv, "w:H:f:t:nhSV", longopts, NULL)) !=
	       -1) {
		switch (c) {
		case 'w':
			width = (unsigned)strtoul(optarg, NULL, 10);
			break;
		case 'H':
			height = (unsigned)strtoul(optarg, NULL, 10);
			break;
		case 'f':
			file = optarg;
			break;
		case 't':
			threshold = (unsigned)strtoul(optarg, NULL, 10);
			if (threshold > 255u) {
				fprintf(stderr, "-t must be 0..255\n");
				return 2;
			}
			has_threshold = 1;
			break;
		case 'n':
			dry_run = 1;
			break;
		case 'S':
			want_status = 1;
			break;
		case 'V':
			printf("ptouch-print %s\n", LIBPTOUCH_VERSION_STRING);
			return 0;
		case 'h':
			usage(argv[0]);
			return 0;
		default:
			usage(argv[0]);
			return 2;
		}
	}

	if (want_status) {
		if (file || width || height || dry_run || has_threshold)
			fprintf(stderr,
				"warning: options other than --status are ignored\n");

		libptouch_ctx *sctx = libptouch_create();
		if (!sctx) {
			fprintf(stderr, "libptouch_create failed\n");
			return 1;
		}
		libptouch_err_t se = libptouch_open_usb(sctx);
		if (se != LIBPTOUCH_OK) {
			fprintf(stderr, "open_usb: %s\n", libptouch_strerror(sctx));
			libptouch_destroy(sctx);
			return 1;
		}
		uint8_t st[LIBPTOUCH_STATUS_LENGTH];
		se = libptouch_get_status(sctx, st);
		if (se != LIBPTOUCH_OK) {
			fprintf(stderr, "get_status: %s\n", libptouch_strerror(sctx));
			libptouch_close(sctx);
			libptouch_destroy(sctx);
			return 1;
		}
		libptouch_status_fprint(stdout, st);
		libptouch_close(sctx);
		libptouch_destroy(sctx);
		return 0;
	}

	if (!file) {
		fprintf(stderr, "-f is required (or use --status)\n");
		usage(argv[0]);
		return 2;
	}

	int png = input_is_png(file);
	if (png) {
		if (width != 0 || height != 0)
			fprintf(stderr,
				"warning: -w/-H ignored for PNG (using image size)\n");
	} else {
		if (width == 0 || height == 0) {
			fprintf(stderr,
				"1bit raster requires -w and -H (or use a PNG file)\n");
			usage(argv[0]);
			return 2;
		}
		if (has_threshold)
			fprintf(stderr,
				"warning: -t applies to PNG only (ignored)\n");
	}

	libptouch_ctx *ctx = libptouch_create();
	if (!ctx) {
		fprintf(stderr, "libptouch_create failed\n");
		return 1;
	}

	uint8_t *data = NULL;
	size_t data_len = 0;
	libptouch_raster_params_t params = { 0, 0, 0 };
	libptouch_err_t e;

	if (png) {
		libptouch_png_options_t opt = { .threshold = (uint8_t)threshold };
		e = libptouch_png_file_to_raster(ctx, file,
						 has_threshold ? &opt : NULL,
						 &data, &data_len, &params);
		if (e != LIBPTOUCH_OK) {
			fprintf(stderr, "png_file_to_raster: %s\n",
				libptouch_strerror(ctx));
			libptouch_destroy(ctx);
			return 1;
		}
	} else {
		if (read_file(file, &data, &data_len) != 0) {
			libptouch_destroy(ctx);
			return 1;
		}
		params.width_dots = width;
		params.height_dots = height;
		params.margin_mm = 0;
	}

	e = libptouch_check_raster(ctx, data, data_len, &params);
	if (e != LIBPTOUCH_OK) {
		fprintf(stderr, "check_raster: %s\n",
			libptouch_strerror(ctx));
		libptouch_destroy(ctx);
		if (png)
			libptouch_free_raster(data);
		else
			free(data);
		return 1;
	}

	if (dry_run) {
		printf("dry-run OK: %zu bytes, %ux%u dots\n", data_len,
		       (unsigned)params.width_dots,
		       (unsigned)params.height_dots);
		libptouch_destroy(ctx);
		if (png)
			libptouch_free_raster(data);
		else
			free(data);
		return 0;
	}

	e = libptouch_open_usb(ctx);
	if (e != LIBPTOUCH_OK) {
		fprintf(stderr, "open_usb: %s\n", libptouch_strerror(ctx));
		libptouch_destroy(ctx);
		if (png)
			libptouch_free_raster(data);
		else
			free(data);
		return 1;
	}

	e = libptouch_print_raster(ctx, data, data_len, &params);
	if (e != LIBPTOUCH_OK) {
		fprintf(stderr, "print_raster: %s\n",
			libptouch_strerror(ctx));
		libptouch_close(ctx);
		libptouch_destroy(ctx);
		if (png)
			libptouch_free_raster(data);
		else
			free(data);
		return 1;
	}

	libptouch_close(ctx);
	libptouch_destroy(ctx);
	if (png)
		libptouch_free_raster(data);
	else
		free(data);
	return 0;
}
