/*
 * libptouch — printer status (ESC i S) and human-readable dump
 *
 * Author: knb
 * Email: knb@artif.org
 */

#include "libptouch_internal.h"

#include <stdio.h>
#include <string.h>

libptouch_err_t libptouch_get_status(libptouch_ctx *ctx, uint8_t *status)
{
	if (!ctx || !status) {
		if (ctx)
			ptouch_set_error(ctx, LIBPTOUCH_ERR_ARG, "null argument");
		return LIBPTOUCH_ERR_ARG;
	}
	if (!ctx->usb_handle || !ctx->bulk_out_ep || !ctx->bulk_in_ep) {
		ptouch_set_error(ctx, LIBPTOUCH_ERR_IO,
			  "USB not open or bulk endpoints missing");
		return LIBPTOUCH_ERR_IO;
	}

	libusb_device_handle *h = ctx->usb_handle;
	static const uint8_t init[] = { 0x1B, 0x40 };
	static const uint8_t req[] = { 0x1B, 0x69, 0x53 };

	int r = LIBUSB_ERROR_OTHER;
	for (int attempt = 0; attempt < 2; attempt++) {
		if (attempt > 0) {
			(void)libusb_clear_halt(h, ctx->bulk_in_ep);
			(void)libusb_clear_halt(h, ctx->bulk_out_ep);
		}
		r = ptouch_bulk_out(h, ctx->bulk_out_ep, init, 2, 5000u);
		if (r != 0) {
			ptouch_set_error_usb(ctx, r, "bulk OUT ESC @");
			return LIBPTOUCH_ERR_USB;
		}
		r = ptouch_bulk_out(h, ctx->bulk_out_ep, req, 3, 5000u);
		if (r != 0) {
			ptouch_set_error_usb(ctx, r, "bulk OUT ESC i S");
			return LIBPTOUCH_ERR_USB;
		}
		r = ptouch_bulk_in_exact(h, ctx->bulk_in_ep, status,
				  (int)LIBPTOUCH_STATUS_LENGTH, 5000u);
		if (r == 0)
			break;
		if (r != LIBUSB_ERROR_IO && r != LIBUSB_ERROR_PIPE)
			break;
	}
	if (r != 0) {
		ptouch_set_error_usb(ctx, r, "bulk IN status");
		return LIBPTOUCH_ERR_USB;
	}

	ptouch_set_error(ctx, LIBPTOUCH_OK, "");
	return LIBPTOUCH_OK;
}

static void fprint_model(FILE *fp, uint8_t code)
{
	const char *name = "unknown";
	switch (code) {
	case 0x6F:
		name = "PT-P900W";
		break;
	case 0x70:
		name = "PT-P950NW";
		break;
	case 0x71:
		name = "PT-P900";
		break;
	case 0x78:
		name = "PT-P910BT";
		break;
	case 0x68:
		name = "PT-P750W";
		break;
	case 0x76:
		name = "PT-P710BT";
		break;
	default:
		break;
	}
	fprintf(fp, "機種コード: 0x%02X ('%c') — %s\n", (unsigned)code,
		(code >= 32 && code < 127) ? (char)code : '?', name);
}

static void fprint_media_width(FILE *fp, uint8_t w, uint8_t len_byte)
{
	const char *desc = NULL;
	switch (w) {
	case 0x00:
		desc = "テープなし / 未装着";
		break;
	case 0x04:
		desc = "3.5 mm";
		break;
	case 0x06:
		desc = "6 mm";
		break;
	case 0x09:
		desc = "9 mm";
		break;
	case 0x0C:
		desc = "12 mm";
		break;
	case 0x12:
		desc = "18 mm";
		break;
	case 0x18:
		desc = "24 mm";
		break;
	case 0x24:
		desc = "36 mm";
		break;
	case 0x15:
		desc = "FLe 21 mm 幅（長さはメディア長バイト参照）";
		break;
	default:
		break;
	}
	if (desc)
		fprintf(fp, "メディア幅: 0x%02X — %s\n", (unsigned)w, desc);
	else
		fprintf(fp, "メディア幅: 0x%02X\n", (unsigned)w);
	if (w == 0x15 && len_byte != 0)
		fprintf(fp, "メディア長: 0x%02X (%u mm 相当の表記参照)\n",
			(unsigned)len_byte, (unsigned)len_byte);
}

static void fprint_media_kind(FILE *fp, uint8_t k)
{
	const char *desc = NULL;
	switch (k) {
	case 0x00:
		desc = "テープなし";
		break;
	case 0x01:
		desc = "ラミネートテープ";
		break;
	case 0x03:
		desc = "ノンラミネートテープ";
		break;
	case 0x04:
		desc = "ファブリックテープ";
		break;
	case 0x11:
		desc = "ヒートシュリンクチューブ (HS 2:1)";
		break;
	case 0x13:
		desc = "FLe テープ";
		break;
	case 0x14:
		desc = "フレキシブルＩＤテープ";
		break;
	case 0x15:
		desc = "サテンテープ";
		break;
	case 0x17:
		desc = "ヒートシュリンクチューブ (HS 3:1)";
		break;
	case 0xFF:
		desc = "非対応テープ";
		break;
	default:
		break;
	}
	if (desc)
		fprintf(fp, "テープ種類: 0x%02X — %s\n", (unsigned)k, desc);
	else
		fprintf(fp, "テープ種類: 0x%02X\n", (unsigned)k);
}

static void fprint_battery(FILE *fp, uint8_t b)
{
	const char *desc = NULL;
	switch (b) {
	case 0x00:
		desc = "フル";
		break;
	case 0x01:
		desc = "ハーフ";
		break;
	case 0x02:
		desc = "ロー";
		break;
	case 0x03:
		desc = "要充電";
		break;
	case 0x04:
		desc = "AC アダプター使用中";
		break;
	case 0xFF:
		desc = "不明";
		break;
	default:
		break;
	}
	if (desc)
		fprintf(fp, "電池残量: 0x%02X — %s\n", (unsigned)b, desc);
	else
		fprintf(fp, "電池残量: 0x%02X (PT-P910BT 等は別表参照)\n",
			(unsigned)b);
}

static void fprint_tape_color(FILE *fp, uint8_t c)
{
	const char *desc = NULL;
	switch (c) {
	case 0x01:
		desc = "白 (White)";
		break;
	case 0x02:
		desc = "その他 (Other)";
		break;
	case 0x03:
		desc = "透明 (Clear)";
		break;
	case 0x04:
		desc = "赤 (Red)";
		break;
	case 0x05:
		desc = "青 (Blue)";
		break;
	case 0x06:
		desc = "黄 (Yellow)";
		break;
	case 0x07:
		desc = "緑 (Green)";
		break;
	case 0x08:
		desc = "黒 (Black)";
		break;
	case 0x09:
		desc = "透明（文字白）";
		break;
	case 0x20:
		desc = "白（マット） (Matte White)";
		break;
	case 0x21:
		desc = "透明（マット） (Matte Clear)";
		break;
	case 0x22:
		desc = "銀（マット） (Matte Silver)";
		break;
	case 0x23:
		desc = "金（サテン） (Satin Gold)";
		break;
	case 0x24:
		desc = "銀（サテン） (Satin Silver)";
		break;
	case 0x30:
		desc = "青（D）";
		break;
	case 0x31:
		desc = "赤（D）";
		break;
	case 0x40:
		desc = "オレンジ（蛍光）";
		break;
	case 0x41:
		desc = "黄（蛍光）";
		break;
	case 0x50:
		desc = "ピンク（S）";
		break;
	case 0x51:
		desc = "グレー（S）";
		break;
	case 0x52:
		desc = "グリーン（S）";
		break;
	case 0x60:
		desc = "イエロー（F）";
		break;
	case 0x61:
		desc = "ピンク（F）";
		break;
	case 0x62:
		desc = "ブルー（F）";
		break;
	case 0x70:
		desc = "白（チューブ）";
		break;
	case 0x90:
		desc = "白（フレキ）";
		break;
	case 0x91:
		desc = "黄（フレキ）";
		break;
	case 0xF0:
		desc = "クリーニング";
		break;
	case 0xF1:
		desc = "ステンシル";
		break;
	case 0xFF:
		desc = "非対応";
		break;
	default:
		break;
	}
	if (desc)
		fprintf(fp, "テープ色: 0x%02X — %s\n", (unsigned)c, desc);
	else
		fprintf(fp, "テープ色: 0x%02X\n", (unsigned)c);
}

static void fprint_status_kind(FILE *fp, uint8_t s)
{
	const char *desc = NULL;
	switch (s) {
	case 0x00:
		desc = "印刷終了";
		break;
	case 0x01:
		desc = "エラー発生";
		break;
	case 0x02:
		desc = "IF モード終了";
		break;
	case 0x03:
		desc = "パワーオフ（未使用扱い）";
		break;
	case 0x04:
		desc = "通知";
		break;
	case 0x05:
		desc = "フェーズ変更";
		break;
	default:
		break;
	}
	if (desc)
		fprintf(fp, "ステータス種類: 0x%02X — %s\n", (unsigned)s, desc);
	else
		fprintf(fp, "ステータス種類: 0x%02X\n", (unsigned)s);
}

void libptouch_status_fprint(FILE *fp, const uint8_t *status)
{
	if (!fp || !status)
		return;

	fprintf(fp, "=== P-touch ステータス (32 バイト) ===\n");
	if (status[0] != 0x80u || status[1] != 0x20u)
		fprintf(fp,
			"※ 先頭マーク異常: [0]=0x%02X [1]=0x%02X (通常 80 20)\n",
			(unsigned)status[0], (unsigned)status[1]);

	fprintf(fp, "ヘッダ: 0x%02X 0x%02X\n", (unsigned)status[0],
		(unsigned)status[1]);
	fprintf(fp, "Brother コード: %c (0x%02X)\n",
		(status[2] >= 32 && status[2] < 127) ? (char)status[2] : '?',
		(unsigned)status[2]);
	fprint_model(fp, status[4]);
	fprintf(fp, "国別コード: %c\n",
		(status[5] >= 32 && status[5] < 127) ? (char)status[5] : '?');

	fprint_battery(fp, status[6]);

	if (status[7] != 0) {
		fprintf(fp, "拡張エラー: 0x%02X", (unsigned)status[7]);
		switch (status[7]) {
		case 0x10:
			fprintf(fp, " — FLE のテープエンド");
			break;
		case 0x1D:
			fprintf(fp, " — 高解像度/ドラフト印刷エラー");
			break;
		case 0x1E:
			fprintf(fp, " — アダプター抜き挿しエラー");
			break;
		case 0x21:
			fprintf(fp, " — 非対応メディアエラー");
			break;
		default:
			break;
		}
		fprintf(fp, "\n");
	} else {
		fprintf(fp, "拡張エラー: なし (0x00)\n");
	}

	fprintf(fp, "エラー情報1: 0x%02X\n", (unsigned)status[8]);
	if (status[8] & 0x01)
		fprintf(fp, "  - メディア無し\n");
	if (status[8] & 0x02)
		fprintf(fp, "  - メディア終了\n");
	if (status[8] & 0x04)
		fprintf(fp, "  - カッタージャム\n");
	if (status[8] & 0x08)
		fprintf(fp, "  - バッテリー弱\n");
	if (status[8] & 0x40)
		fprintf(fp, "  - 高圧アダプター\n");

	fprintf(fp, "エラー情報2: 0x%02X\n", (unsigned)status[9]);
	if (status[9] & 0x01)
		fprintf(fp, "  - メディア交換（メディア違い）\n");
	if (status[9] & 0x04)
		fprintf(fp, "  - 通信エラー\n");
	if (status[9] & 0x08)
		fprintf(fp, "  - 通信バッファーフル\n");
	if (status[9] & 0x10)
		fprintf(fp, "  - カバーオープン\n");
	if (status[9] & 0x20)
		fprintf(fp, "  - 高温エラー\n");
	if (status[9] & 0x40)
		fprintf(fp, "  - 先端検出エラー\n");
	if (status[9] & 0x80)
		fprintf(fp, "  - システムエラー\n");

	fprint_media_width(fp, status[10], status[17]);
	fprint_media_kind(fp, status[11]);
	fprintf(fp, "色数: 0x%02X  フォント/日本語フォント: 0x%02X / 0x%02X\n",
		(unsigned)status[12], (unsigned)status[13],
		(unsigned)status[14]);
	fprintf(fp, "モード: 0x%02X  濃度: 0x%02X\n", (unsigned)status[15],
		(unsigned)status[16]);

	fprint_status_kind(fp, status[18]);
	fprintf(fp, "フェーズ種類: 0x%02X  フェーズ番号: %02X %02X\n",
		(unsigned)status[19], (unsigned)status[20],
		(unsigned)status[21]);
	fprintf(fp, "通知番号: 0x%02X\n", (unsigned)status[22]);
	fprintf(fp, "拡張部バイト数: 0x%02X\n", (unsigned)status[23]);

	fprint_tape_color(fp, status[24]);
	fprintf(fp, "文字色: 0x%02X\n", (unsigned)status[25]);

	fprintf(fp, "生データ: ");
	for (unsigned i = 0; i < LIBPTOUCH_STATUS_LENGTH; i++)
		fprintf(fp, "%02X%s", (unsigned)status[i],
			i + 1 == LIBPTOUCH_STATUS_LENGTH ? "\n" : " ");
}
