/*
** HDL4SE: 软件Verilog综合仿真平台
** Copyright (C) 2021-2021, raoxianhong<raoxianhong@163.net>
** LCOM: 轻量级组件对象模型
** Copyright (C) 2021-2021, raoxianhong<raoxianhong@163.net>
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
**
** * Redistributions of source code must retain the above copyright notice,
**   this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright notice,
**   this list of conditions and the following disclaimer in the documentation
**   and/or other materials provided with the distribution.
** * The name of the author may be used to endorse or promote products
**   derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
** THE POSSIBILITY OF SUCH DAMAGE.
*/

/*
* hdl4se_binop.c
  修改记录：
	202105230844: rxh, initial version
	202105241522：rxh, 增加Detector接口
*/
#include "stdlib.h" 
#include "stdio.h"
#include "string.h"
#include "object.h"
#include "dlist.h"
#include "bignumber.h"
#include "hdl4secell.h"

/*
#define BINOP_ADD 0
#define BINOP_SUB 1
#define BINOP_MUL 2
#define BINOP_DIV 3
#define BINOP_EQ 4
#define BINOP_NE 5
#define BINOP_LT 6
#define BINOP_LE 7
#define BINOP_GE 8
#define BINOP_GT 9
#define BINOP_AND 10
#define BINOP_OR 11
#define BINOP_XOR 12
*/
#define BINOP_LAST BINOP_XOR
#define INCOUNT 2
/* 
hdl4se_binop
instance parameter: "8, 9, 1, 4", 
inputwidth0=8, inputwidth1=9, outputwidth=1, unop=BINOP_EQ 
*/
typedef struct _sHDL4SEBinop {
	OBJECT_HEADER
	INTERFACE_DECLARE(IHDL4SEUnit)
	HDL4SEUNIT_VARDECLARE
	INTERFACE_DECLARE(IHDL4SEDetector)
	HDL4SEDETECTOR_VARDECLARE
	DLIST_VARDECLARE
	
	IHDL4SEModule** parent;
	char* name;

	IHDL4SEUnit** wire_in[INCOUNT];
	int wire_in_index[INCOUNT];
	int in_width[INCOUNT];
	IBigNumber** in_data[INCOUNT];

	IBigNumber** out_data;
	int out_width;
	int op;
	int datavalid;
}sHDL4SEBinop;

OBJECT_FUNCDECLARE(hdl4se_binop, CLSID_HDL4SE_BINOP);
HDL4SEUNIT_FUNCDECLARE(hdl4se_binop, CLSID_HDL4SE_BINOP, sHDL4SEBinop);
HDL4SEDETECTOR_FUNCDECLARE(hdl4se_binop, CLSID_HDL4SE_BINOP, sHDL4SEBinop);
DLIST_FUNCIMPL(hdl4se_binop, CLSID_HDL4SE_BINOP, sHDL4SEBinop);

OBJECT_FUNCIMPL(hdl4se_binop, sHDL4SEBinop, CLSID_HDL4SE_BINOP);


QUERYINTERFACE_BEGIN(hdl4se_binop, CLSID_HDL4SE_BINOP)
QUERYINTERFACE_ITEM(IID_HDL4SEUNIT, IHDL4SEUnit, sHDL4SEBinop)
QUERYINTERFACE_ITEM(IID_HDL4SEDETECTOR, IHDL4SEDetector, sHDL4SEBinop)
QUERYINTERFACE_ITEM(IID_DLIST, IDList, sHDL4SEBinop)
QUERYINTERFACE_END

static const char* hdl4se_binopModuleInfo()
{
	return "1.0.0-20210523.0844 HDL4SE BINOP cell";
}

static int hdl4se_binopCreate(const PARAMITEM* pParams, int paramcount, HOBJECT* pObject)
{
	sHDL4SEBinop* pobj;
	int i;
	pobj = (sHDL4SEBinop*)malloc(sizeof(sHDL4SEBinop));
	if (pobj == NULL)
		return -1;
	*pObject = 0;
	HDL4SEUNIT_VARINIT(pobj, CLSID_HDL4SE_BINOP);
	INTERFACE_INIT(IHDL4SEUnit, pobj, hdl4se_binop, hdl4se_unit);
	INTERFACE_INIT(IHDL4SEDetector, pobj, hdl4se_binop, hdl4se_detector);
	DLIST_VARINIT(pobj, hdl4se_binop);

	for (i = 0; i < INCOUNT; i++) {
		pobj->wire_in[i] = NULL;
		pobj->wire_in_index[i] = 0;
		pobj->in_data[i] = NULL;
		pobj->in_width[i] = 0;
	}

	pobj->out_data = NULL;

	pobj->name = NULL;
	pobj->out_width = 0;
	pobj->datavalid = 0;
	pobj->op = -1;
	pobj->parent = NULL;

	for (i = 0; i < paramcount; i++) {
		if (pParams[i].name == PARAMID_HDL4SE_UNIT_NAME) {
			if (pobj->name != NULL)
				free(pobj->name);
			pobj->name = strdup(pParams[i].pvalue);
		} 
		else if (pParams[i].name == PARAMID_HDL4SE_UNIT_PARENT) {
			pobj->parent = (IHDL4SEModule **)pParams[i].pvalue;
		}
		else if (pParams[i].name == PARAMID_HDL4SE_UNIT_INSTANCE_PARAMETERS) {
			IBigNumber** temp = bigintegerCreate(32);
			if (temp != NULL) {
				const char* nstr;
				const char* lstr;
				int j;
				lstr = (const char*)pParams[i].pvalue;
				for (j = 0; j < INCOUNT; j++) {
					if (0 == objectCall3(temp, AssignStr, lstr, &nstr, 0)) {
						objectCall1(temp, GetInt32, &pobj->in_width[j]);
					}
					else {
						return EIID_INVALIDPARAM;
					}
					lstr = nstr;
				}
				if (0 == objectCall3(temp, AssignStr, lstr, &nstr, 0)) {
					objectCall1(temp, GetInt32, &pobj->out_width);
				}
				else {
					return EIID_INVALIDPARAM;
				}
				lstr = nstr;
				if (0 == objectCall3(temp, AssignStr, lstr, &nstr, 0)) {
					objectCall1(temp, GetInt32, &pobj->op);
				}
				else {
					return EIID_INVALIDPARAM;
				}
				lstr = nstr;
				objectRelease(temp);
			}
		}
	}
	for (i = 0; i < INCOUNT; i++) {
		if (pobj->in_width[i] <= 0)
			return EIID_INVALIDPARAM;
		pobj->in_data[i] = bigintegerCreate(pobj->in_width[i]);
	}
	if (pobj->out_width <= 0)
		return EIID_INVALIDPARAM;
	if (pobj->op < UNOP_NEG || pobj->op > BINOP_LAST)
		return EIID_INVALIDPARAM;

	pobj->out_data = bigintegerCreate(pobj->out_width);

	/* 返回生成的对象 */
	OBJECT_RETURN_GEN(hdl4se_binop, pobj, pObject, CLSID_HDL4SE_BINOP);
	return EIID_OK;
}

static void hdl4se_binopDestroy(HOBJECT object)
{
	sHDL4SEBinop* pobj;
	int i;
	pobj = (sHDL4SEBinop*)objectThis(object);
	if (pobj->name != NULL)
		free(pobj->name);
	for (i = 0; i < INCOUNT; i++) {
		objectRelease(pobj->wire_in[i]);
		objectRelease(pobj->in_data[i]);
	}
	objectRelease(pobj->out_data);
	memset(pobj, 0, sizeof(sHDL4SEBinop));
	free(pobj);
}

static int hdl4se_binopValid(HOBJECT object)
{
	sHDL4SEBinop* pobj;
	pobj = (sHDL4SEBinop*)objectThis(object);
	return 1;
}

static int hdl4se_binop_hdl4se_unit_Connect(HOBJECT object, int index, HOBJECT from, int fromindex) 
{
	sHDL4SEBinop* pobj;
	IHDL4SEUnit** unit = NULL;
	pobj = (sHDL4SEBinop*)objectThis(object);
	if (index >= 0 && index < INCOUNT) {
		if (0 == objectQueryInterface(from, IID_HDL4SEUNIT, (void**)&unit)) {
			objectRelease(pobj->wire_in[index]);
			pobj->wire_in[index] = unit;
			pobj->wire_in_index[index] = fromindex;
			return 0;
		}
	}
	return -1;
}

static int hdl4se_binop_hdl4se_unit_GetValue(HOBJECT object, int index, int width, IBigNumber ** value)
{
	int i;
	sHDL4SEBinop* pobj;
	pobj = (sHDL4SEBinop*)objectThis(object);
	if (pobj->datavalid == 0) {
		for (i = 0; i < INCOUNT; i++)
			objectCall3(pobj->wire_in[i], GetValue, pobj->wire_in_index[i], pobj->in_width[i], pobj->in_data[i]);
		switch (pobj->op) {
		case BINOP_ADD:
			objectCall2(pobj->out_data, Add, pobj->in_data[0], pobj->in_data[1]);
		break;
		case BINOP_SUB:
			objectCall2(pobj->out_data, Sub, pobj->in_data[0], pobj->in_data[1]);
			break;
		case BINOP_MUL:
			objectCall2(pobj->out_data, Mul, pobj->in_data[0], pobj->in_data[1]);
			break;
		case BINOP_DIV:
			objectCall2(pobj->out_data, Div, pobj->in_data[0], pobj->in_data[1]);
			break;
		case BINOP_EQ:
			objectCall1(pobj->out_data, AssignInt32, 0);
			if (objectCall1(pobj->in_data[0], IsEQ, pobj->in_data[1]))
				objectCall1(pobj->out_data, AssignInt32, 1);
			break;
		case BINOP_NE:
			objectCall1(pobj->out_data, AssignInt32, 1);
			if (objectCall1(pobj->in_data[0], IsEQ, pobj->in_data[1]))
				objectCall1(pobj->out_data, AssignInt32, 0);
			break;
		case BINOP_LT:
			objectCall1(pobj->out_data, AssignInt32, 0);
			if (objectCall1(pobj->in_data[0], IsLT, pobj->in_data[1]))
				objectCall1(pobj->out_data, AssignInt32, 1);
			break;
		case BINOP_LE:
			objectCall1(pobj->out_data, AssignInt32, 0);
			if (objectCall1(pobj->in_data[0], IsLE, pobj->in_data[1]))
				objectCall1(pobj->out_data, AssignInt32, 1);
			break;
		case BINOP_GE:
			objectCall1(pobj->out_data, AssignInt32, 1);
			if (objectCall1(pobj->in_data[0], IsLT, pobj->in_data[1]))
				objectCall1(pobj->out_data, AssignInt32, 0);
			break;
		case BINOP_GT:
			objectCall1(pobj->out_data, AssignInt32, 1);
			if (objectCall1(pobj->in_data[0], IsLE, pobj->in_data[1]))
				objectCall1(pobj->out_data, AssignInt32, 0);
			break;
		case BINOP_AND:
			objectCall2(pobj->out_data, And, pobj->in_data[0], pobj->in_data[1]);
			break;
		case BINOP_OR:
			objectCall2(pobj->out_data, Or, pobj->in_data[0], pobj->in_data[1]);
			break;
		case BINOP_XOR:
			objectCall2(pobj->out_data, Xor, pobj->in_data[0], pobj->in_data[1]);
			break;
		}
		pobj->datavalid = 1;
	}
	objectCall1(value, Assign, pobj->out_data);
	return 0;
}

static int hdl4se_binop_hdl4se_unit_ClkTick(HOBJECT object)
{
	sHDL4SEBinop* pobj;
	pobj = (sHDL4SEBinop*)objectThis(object);
	return 0;
}

static int hdl4se_binop_hdl4se_unit_Setup(HOBJECT object)
{
	sHDL4SEBinop* pobj;
	pobj = (sHDL4SEBinop*)objectThis(object);
	pobj->datavalid = 0;
	return 0;
}

static int hdl4se_binop_hdl4se_detector_GetName(HOBJECT object, const char** pname)
{
	sHDL4SEBinop* pobj;
	pobj = (sHDL4SEBinop*)objectThis(object);
	*pname = pobj->name;
	return 0;
}

static int hdl4se_binop_hdl4se_detector_GetSignalCount(HOBJECT object)
{
	sHDL4SEBinop* pobj;
	pobj = (sHDL4SEBinop*)objectThis(object);
	return 1;
}

static int hdl4se_binop_hdl4se_detector_GetSignalInfo(HOBJECT object, int index, const char** pname, int * width)
{
	sHDL4SEBinop* pobj;
	pobj = (sHDL4SEBinop*)objectThis(object);
	*pname = "out";
	*width = pobj->out_width;
	return 0;
}

static int hdl4se_binop_hdl4se_detector_GetSignalValue(HOBJECT object, int index, IBigNumber** value)
{
	sHDL4SEBinop* pobj;
	pobj = (sHDL4SEBinop*)objectThis(object);
	return hdl4se_binop_hdl4se_unit_GetValue(object, 2, pobj->out_width, value);
}

static int hdl4se_binop_hdl4se_detector_GetUnitCount(HOBJECT object)
{
	sHDL4SEBinop* pobj;
	pobj = (sHDL4SEBinop*)objectThis(object);
	return 0;
}

static int hdl4se_binop_hdl4se_detector_GetUnit(HOBJECT object, int index, HOBJECT* unit)
{
	sHDL4SEBinop* pobj;
	pobj = (sHDL4SEBinop*)objectThis(object);
	return 0;
}

