#include "../VDMX.h"

#include "support/util.h"
#include "bk/bkgraph.h"

table_VDMX *otfcc_readVDMX(const otfcc_Packet packet, const otfcc_Options *options) {
	table_VDMX *vdmx = NULL;
	FOR_TABLE(OTFCC_CHR('V','D','M','X'), table) {
		if (table.length < 6) goto FAIL;
		uint16_t version = read_16u(table.data + 0);
		uint16_t numRatios = read_16u(table.data + 4);
		if (table.length < 6 + 6 * numRatios) goto FAIL;

		vdmx = table_iVDMX.create();
		vdmx->version = version;
		for (shapeid_t g = 0; g < numRatios; g++) {
			const size_t ratioRangeOffset = 6 + 4 * g;
			const size_t offsetOffset = 6 + 4 * numRatios + 2 * g;
			vdmx_RatioRange r;
			vdmx_iRatioRange.init(&r);
			r.bCharset = read_8u(table.data + ratioRangeOffset + 0);
			r.xRatio = read_8u(table.data + ratioRangeOffset + 1);
			r.yStartRatio = read_8u(table.data + ratioRangeOffset + 2);
			r.yEndRatio = read_8u(table.data + ratioRangeOffset + 3);

			uint16_t groupOffset = read_16u(table.data + offsetOffset);
			uint16_t recs = read_16u(table.data + groupOffset + 0);
			for (uint16_t j = 0; j < recs; j++) {
				uint16_t yPelHeight = read_16u(table.data + groupOffset + 4 + j * 6 + 0);
				int16_t yMax = read_16s(table.data + groupOffset + 4 + j * 6 + 2);
				int16_t yMin = read_16s(table.data + groupOffset + 4 + j * 6 + 4);
				vdmx_iGroup.push(&r.records, (vdmx_Record){
				                                 .yPelHeight = yPelHeight,
				                                 .yMax = yMax,
				                                 .yMin = yMin,
				                             });
			}
			vdmx_iRatioRangeList.push(&vdmx->ratios, r);
		}
		return vdmx;
	FAIL:
		logWarning("Table 'VDMX' corrupted.\n");
		table_iVDMX.free(vdmx);
		vdmx = NULL;
	}
	return vdmx;
}

void otfcc_dumpVDMX(const table_VDMX *vdmx, json_value *root, const otfcc_Options *options) {
	if (!vdmx) return;
	loggedStep("VDMX") {
		json_value *_vdmx = json_object_new(2);
		json_object_push(_vdmx, "version", json_integer_new(vdmx->version));

		json_value *_ratios = json_array_new(vdmx->ratios.length);
		json_object_push(_vdmx, "ratios", _ratios);
		foreach (vdmx_RatioRange *rr, vdmx->ratios) {
			json_value *_rr = json_object_new(5);
			json_array_push(_ratios, _rr);
			json_object_push(_rr, "bCharset", json_integer_new(rr->bCharset));
			json_object_push(_rr, "xRatio", json_integer_new(rr->xRatio));
			json_object_push(_rr, "yStartRatio", json_integer_new(rr->yStartRatio));
			json_object_push(_rr, "yEndRatio", json_integer_new(rr->yEndRatio));

			json_value *_records = json_array_new(rr->records.length);
			json_object_push(_rr, "records", _records);
			foreach (vdmx_Record *r, rr->records) {
				json_value *_r = json_object_new(3);
				json_array_push(_records, _r);
				json_object_push(_r, "yPelHeight", json_integer_new(r->yPelHeight));
				json_object_push(_r, "yMax", json_integer_new(r->yMax));
				json_object_push(_r, "yMin", json_integer_new(r->yMin));
			}
		}
		json_object_push(root, "VDMX", _vdmx);
	}
}

table_VDMX *otfcc_parseVDMX(const json_value *root, const otfcc_Options *options) {
	json_value *_vdmx = NULL;
	if (!(_vdmx = json_obj_get_type(root, "VDMX", json_object))) return NULL;
	table_VDMX *vdmx = table_iVDMX.create();
	loggedStep("VDMX") {
		vdmx->version = json_obj_getnum(_vdmx, "version");
		json_value *_ratios = json_obj_get_type(_vdmx, "ratios", json_array);
		for (size_t j = 0; j < _ratios->u.array.length; j++) {
			json_value *_rr = _ratios->u.array.values[j];
			if (!_rr || _rr->type != json_object) continue;
			vdmx_RatioRange r;
			vdmx_iRatioRange.init(&r);
			r.bCharset = json_obj_getnum(_rr, "bCharset");
			r.xRatio = json_obj_getnum(_rr, "xRatio");
			r.yStartRatio = json_obj_getnum(_rr, "yStartRatio");
			r.yEndRatio = json_obj_getnum(_rr, "yEndRatio");
			json_value *_records = json_obj_get_type(_rr, "records", json_array);
			if (!_records) {
				vdmx_iRatioRange.dispose(&r);
				continue;
			}
			for (size_t j = 0; j < _records->u.array.length; j++) {
				json_value *_r = _records->u.array.values[j];
				if (!_r || _r->type != json_object) continue;
				vdmx_iGroup.push(&r.records, (vdmx_Record){
				                                 .yPelHeight = json_obj_getnum(_r, "yPelHeight"),
				                                 .yMax = json_obj_getnum(_r, "yMax"),
				                                 .yMin = json_obj_getnum(_r, "yMin"),
				                             });
			}
			vdmx_iRatioRangeList.push(&vdmx->ratios, r);
		}
	}
	return vdmx;
}

caryll_Buffer *otfcc_buildVDMX(const table_VDMX *vdmx, const otfcc_Options *options) {
	if (!vdmx || !vdmx->ratios.length) return NULL;
	bk_Block *root = bk_new_Block(b16, vdmx->version,       // Version
	                              b16, vdmx->ratios.length, // numRecs
	                              b16, vdmx->ratios.length, // numRatios
	                              bkover);
	foreach (vdmx_RatioRange *rr, vdmx->ratios) {
		bk_push(root,                // RatioRange
		        b8, rr->bCharset,    // bCharset
		        b8, rr->xRatio,      // bCharset
		        b8, rr->yStartRatio, // bCharset
		        b8, rr->yEndRatio,   // bCharset
		        bkover);
	}
	foreach (vdmx_RatioRange *rr, vdmx->ratios) {
		uint16_t startsz = 0xFFFF;
		uint16_t endsz = 0;
		foreach (vdmx_Record *r, rr->records) {
			if (startsz > r->yPelHeight) startsz = r->yPelHeight;
			if (endsz < r->yPelHeight) endsz = r->yPelHeight;
		}
		bk_Block *group = bk_new_Block(b16, rr->records.length, // recs
		                               b8, startsz,             //
		                               b8, endsz,               //
		                               bkover);
		foreach (vdmx_Record *r, rr->records) {
			bk_push(group,              //
			        b16, r->yPelHeight, //
			        b16, r->yMax,       //
			        b16, r->yMin,       //
			        bkover);
		}
		bk_push(root, p16, group, bkover);
	}
	return bk_build_Block_noMinimize(root);
}
