// SPDX-FileCopyrightText: 2015-2018 oddcoder <ahmedsoliman@oddcoder.com>
// SPDX-FileCopyrightText: 2015-2018 thestr4ng3r <info@florianmaerkl.de>
// SPDX-FileCopyrightText: 2015-2018 courk <courk@courk.cc>
// SPDX-License-Identifier: LGPL-3.0-only

#include <rz_types.h>
#include <rz_analysis.h>
#include "../isa/pic/pic_highend.h"
#include "../isa/pic/pic_midrange.h"
#include "../isa/pic/pic_baseline.h"

static bool pic_init(void **user) {
	PicContext *ctx = RZ_NEW0(PicContext);
	if (!ctx) {
		return false;
	}
	ctx->init_done = false;
	ctx->pic_highend_mm = ht_su_new(HT_STR_CONST);
	for (int i = 0; i < 0x80; ++i) {
		const char *regname = pic_highend_regname(i);
		ht_su_insert(ctx->pic_highend_mm, regname, i);
	}
	for (int i = 0x80; i < 0x100; ++i) {
		const char *regname = pic_highend_regname(i);
		ht_su_insert(ctx->pic_highend_mm, regname, i + 0xf00);
	}
	*user = ctx;
	return true;
}

static bool pic_fini(void *user) {
	PicContext *ctx = (PicContext *)user;
	if (ctx) {
		ht_su_free(ctx->pic_highend_mm);
		RZ_FREE(ctx);
	}
	return true;
}

static int analysis_pic_op(
	RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr,
	const ut8 *buf, int len, RzAnalysisOpMask mask) {
	const char *cpu = rz_analysis_get_cpu(analysis);
	if (RZ_STR_ISEMPTY(cpu) || is_pic_highend(cpu)) {
		return pic_highend_op(analysis, op, addr, buf, len, mask);
	}

	if (is_pic_baseline_or_pic_midrange(cpu)) {
		if (!buf || len < 2) {
			op->type = RZ_ANALYSIS_OP_TYPE_ILL;
			return -1;
		}
		PicMidrangeOp x = { 0 };
		if (!(is_pic_baseline(cpu) ? pic_baseline_decode_op : pic_midrange_decode_op)(&x, addr, buf, len)) {
			return -1;
		}
		return pic_midrange_analysis_op(analysis, op, &x, mask);
	}
	return -1;
}

static char *analysis_pic_get_reg_profile(RzAnalysis *analysis) {
	const char *cpu = rz_analysis_get_cpu(analysis);
	if (RZ_STR_ISEMPTY(cpu) || is_pic_highend(cpu)) {
		return pic_highend_get_reg_profile(analysis);
	}

	if (is_pic_baseline_or_pic_midrange(rz_analysis_get_cpu(analysis))) {
		return pic_midrange_get_reg_profile(analysis);
	}
	return NULL;
}

static RzAnalysisILConfig *pic_il_config(RzAnalysis *analysis) {
	const char *cpu = rz_analysis_get_cpu(analysis);
	if (RZ_STR_ISEMPTY(cpu) || is_pic_highend(cpu)) {
		return pic_highend_il_config(analysis);
	}
	if (is_pic_baseline_or_pic_midrange(cpu)) {
		return pic_midrange_il_config(analysis);
	}
	return NULL;
}

static int pic_archinfo(RzAnalysis *analysis, RzAnalysisInfoType query) {
	const char *cpu = rz_analysis_get_cpu(analysis);
	if (RZ_STR_ISEMPTY(cpu) || is_pic_highend(cpu)) {
		switch (query) {
		case RZ_ANALYSIS_ARCHINFO_MIN_OP_SIZE: return 2;
		case RZ_ANALYSIS_ARCHINFO_MAX_OP_SIZE: return 4;
		case RZ_ANALYSIS_ARCHINFO_TEXT_ALIGN: return 2;
		case RZ_ANALYSIS_ARCHINFO_DATA_ALIGN: return 4;
		case RZ_ANALYSIS_ARCHINFO_CAN_USE_POINTERS: return 1;
		default: return -1;
		}
	}

	if (is_pic_baseline_or_pic_midrange(cpu)) {
		switch (query) {
		case RZ_ANALYSIS_ARCHINFO_MIN_OP_SIZE: return 2;
		case RZ_ANALYSIS_ARCHINFO_MAX_OP_SIZE: return 2;
		case RZ_ANALYSIS_ARCHINFO_TEXT_ALIGN: return 1;
		case RZ_ANALYSIS_ARCHINFO_DATA_ALIGN: return 1;
		case RZ_ANALYSIS_ARCHINFO_CAN_USE_POINTERS: return 1;
		default: return -1;
		}
	}
	return -1;
}

RzAnalysisPlugin rz_analysis_plugin_pic = {
	.name = "pic",
	.desc = "PIC analysis plugin",
	.license = "LGPL3",
	.arch = "pic",
	.bits = 16 | 32,
	.op = &analysis_pic_op,
	.init = pic_init,
	.fini = pic_fini,
	.il_config = pic_il_config,
	.get_reg_profile = &analysis_pic_get_reg_profile,
	.esil = true,
	.archinfo = pic_archinfo
};
