/**
 * @~japanese
 * @file openEL.c
 * @brief		OpenEL 共有部
 * @Version 3.0.0
 *
 * @~english
 * @file openEL.c
 * @brief		OpenEL Common File
 * @Version 3.0.0
 */
/*

Copyright (c) 2017,2018 Japan Embedded Systems Technology Association(JASA)
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.
    Neither the name of the Association nor the names of its contributors 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 HOLDER 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.
*/

/* Includes */
/*---------------------------------------------------------------------------*/

#define OPENEL_SW_SURFACE_FRIEND 1
#include "openEL.h"

#include "stdio.h"

/* Macro (module scope) */
/*---------------------------------------------------------------------------*/


/* Variable (global scope) */
/*---------------------------------------------------------------------------*/

/* Variable (friend scope) */
/*---------------------------------------------------------------------------*/

struct elPort_st elPortTbl[EL_PORT_ID_MAX];

static int32_t elPortMst = 0; /**< ハンドル 割当て用 */

/* Variable (module scope) */
/*---------------------------------------------------------------------------*/

/* Function prototype (module scope) */
/*---------------------------------------------------------------------------*/

static int32_t elInit_old(uint32_t handle,EL_CMN_FNC_TBL_T fncTbl,uint32_t compoIndex);
static int32_t elGetNewHandle(void); /**< ポートIDの取得 */
static EL_CMN_FNC_TBL_T elGetComponent(int32_t vendorID, int32_t compoID);

/* Functions */
/*---------------------------------------------------------------------------*/
/** 
 * @~japanese
 * openELコンポーネントの初期化
 * @param[in]	vendorID
 * @param[in]	productID
 * @param[in]	instanceID
 * @return 0:正常 / -1以下:エラーコード
 *
 * @~english
 * Initialize the openEL component
 * @param[in]	vendorID
 * @param[in]	productID
 * @param[in]	component index
 * @return 0:OK / less than -1:error code
 */
enum ReturnCode HalInit(HALCOMPONENT_T *halComponent) {
	int32_t retVal;
	EL_CMN_FNC_TBL_T pFncTbl;
	int32_t hn;
	printf("HalInit():%x,%x,%x\n", halComponent->halComponent_core.halId.vendorId, halComponent->halComponent_core.halId.productId, halComponent->halComponent_core.halId.instanceId);

	pFncTbl = elGetComponent(halComponent->halComponent_core.halId.vendorId, halComponent->halComponent_core.halId.productId);

	if ( (hn = elGetNewHandle()) < 0 ) {
		return HAL_ERROR;
	}
	printf("hn = %d\n", hn);
	retVal = elInit_old(hn,pFncTbl,halComponent->halComponent_core.halId.instanceId);
	if (retVal < 0 ) {
		return HAL_ERROR;
	} else {
		halComponent->halComponent_core.handle = hn;
	}

	return HAL_OK;
}
/*---------------------------------------------------------------------------*/
/** OpenELデバイスの初期化 従来と同等
 * @param[in]	handle : ポートID
 * @param[in]	pFncTbl : ベンダーで準備された関数テーブル
 * @param[in]	phyicalhandle : ベンダーによる接続用handle
 * @param[in]	pCnfig : ベンダー準拠のコンフィグレーション情報
 * @return		0:エラーなし / 0:以外 設定できない
 */
int32_t elInit_old(uint32_t handle,EL_CMN_FNC_TBL_T fncTbl,uint32_t compoIndex) {
	if ( (handle > EL_PORT_ID_MAX) || (0 != elPortTbl[handle].physicalPortID) ) {
		return HAL_ERROR;
	}
	elPortTbl[handle].physicalPortID = compoIndex;
	elPortTbl[handle].fncTbl = fncTbl;
	return elPortTbl[handle].fncTbl.pGeneral->pFncInit(elPortTbl[handle].physicalPortID,handle);
}
/*---------------------------------------------------------------------------*/
/** OpenELデバイスのリセット */
enum ReturnCode HalReInit(HALCOMPONENT_T *halComponent) {
	int32_t rVal;
#if 1 == EL_SW_FAST
	uint32_t physicalhandle = elPortTbl[*halComponent->halComponent_core.handle].physicalhandle;
#else
	uint32_t physicalhandle;
	if ( 0 == (physicalhandle = elGetPhyicalPortID(halComponent->halComponent_core.handle,0)) ) {
		return HAL_ERROR;
	}
#endif
	rVal = elPortTbl[halComponent->halComponent_core.handle].fncTbl.pGeneral->pFncReInit(physicalhandle);
	return rVal;
}

/*---------------------------------------------------------------------------*/
/** OpenELデバイスの終了 */
enum ReturnCode HalFinalize(HALCOMPONENT_T *halComponent) {
	int32_t rVal;
#if 1 == EL_SW_FAST
	uint32_t physicalhandle = elPortTbl[halComponent->halComponent_core.handle].physicalhandle;
#else
	uint32_t physicalhandle;
	if ( 0 == (physicalhandle = elGetPhyicalPortID(halComponent->halComponent_core.handle,0)) ) {
		return HAL_ERROR;
	}
#endif

	rVal = elPortTbl[halComponent->halComponent_core.handle].fncTbl.pGeneral->pFncFinalize(physicalhandle);
	elPortTbl[halComponent->halComponent_core.handle].physicalPortID = 0; /* Clear physicalPortID */
	return rVal;
}

enum ReturnCode HalAddObserver(HALCOMPONENT_T *halComponent, HALOBSERVER_T *halObserver) {
	printf("HalAddObserver\n");
	halComponent->halComponent_core.observerList[0] = *halObserver;
	return HAL_OK;
}

enum ReturnCode HalRemoveObserver(HALCOMPONENT_T *halComponent, HALOBSERVER_T *halObserver) {
	printf("HalRemoveObserver\n");
	return HAL_OK;
}

enum ReturnCode HalGetProperty(HALCOMPONENT_T *halComponent, PROPERTY_T *property) {
	printf("HalGetProperty\n");
	return HAL_OK;
}

enum ReturnCode HalGetTime(HALCOMPONENT_T *halComponent, int32_t *time) {
	printf("HalGetTime\n");
	return HAL_OK;
}

enum ReturnCode HalEventTimerStartTimer(EVENTTIMER_T *eventTimer) {
	printf("HalEventTimerStartTimer\n");
	eventTimer->observerList[0].notify_timer(eventTimer);
	return HAL_OK;
}

enum ReturnCode HalEventTimerStopTimer(EVENTTIMER_T *eventTimer) {
	printf("HalEventTimerStopTimer\n");
	return HAL_OK;
}

enum ReturnCode HalEventTimerSetEventPeriod(EVENTTIMER_T *eventTimer, int32_t eventPeriod) {
	printf("HalEventTimerSetEventPeriod\n");
	return HAL_OK;
}

enum ReturnCode HalEventTimerAddObserver(EVENTTIMER_T *eventTimer, TIMEROBSERVER_T *timerObserver) {
	printf("HalEventTimerAddObserver\n");
	eventTimer->observerList[0] = *timerObserver;
	return HAL_OK;
}

enum ReturnCode HalEventTimerRemoveObserver(EVENTTIMER_T *eventTimer, TIMEROBSERVER_T *timerObserver) {
	printf("HalEventTimerRemoveObserver\n");
	return HAL_OK;
}

/*---------------------------------------------------------------------------*/
/** 物理handleに変換 */
int32_t elGetPhyicalPortID(uint32_t handle,uint32_t profileID) {
	uint32_t phyID;
	if ( handle <= EL_PORT_ID_MAX ) {
		phyID = elPortTbl[handle].physicalPortID;
//		printf("OK:phyID=%d\n", phyID);
	} else {
//		printf("NG:phyID=0\n");
		phyID = 0;
	}

	return phyID;
}

/*---------------------------------------------------------------------------*/
/** OpenEL handleの取得
 * @pram[in]	なし
 * @return		handle
 */
static int32_t elGetNewHandle(void) {
	if ( elPortMst < EL_PORT_ID_MAX ) {
		elPortMst++;
	} else {
		elPortMst = -1;
	}

	return elPortMst;
}

/*---------------------------------------------------------------------------*/
/** コンポーネント ファンクションテーブルの取得 */
EL_CMN_FNC_TBL_T elGetComponent(int32_t vendorID, int32_t compoID) {
	const EL_CMN_FNC_TBL_T noCompo = { (void *)0};
	struct elCompoTbl_st *pCompo = (struct elCompoTbl_st *)elCompoTbl;
	uint32_t idx = 0;

	while (idx++ < elSzCompoTbl ) {
		if ( (pCompo->vendorID == vendorID) && (pCompo->componentID == compoID) ) {
			return *(pCompo->pFncTbl);
		}
		pCompo++;
	}
	return noCompo;
}

/*---------------------------------------------------------------------------*/
