/**
 *
 * \file
 *
 * \brief NMC1500 IoT OTA Interface.
 *
 * Copyright (c) 2016-2017 Atmel Corporation. All rights reserved.
 *
 * \asf_license_start
 *
 * \page License
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. 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.
 *
 * 3. The name of Atmel may not be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
 *
 * \asf_license_stop
 *
 */



/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
INCLUDES
*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
#include "common/include/nm_common.h"
#include "driver/include/m2m_types.h"
#include "driver/include/m2m_ota.h"
#include "driver/source/m2m_hif.h"
/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
MACROS
*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/

/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
DATA TYPES
*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
static tpfOtaUpdateCb gpfOtaUpdateCb = NULL;
static tpfOtaNotifCb  gpfOtaNotifCb = NULL;


/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
FUNCTION PROTOTYPES
*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
/**
*	@fn			m2m_wifi_cb(uint8 u8OpCode, uint16 u16DataSize, uint32 u32Addr, uint8 grp)
*	@brief		WiFi call back function
*	@param [in]	u8OpCode
*					HIF Opcode type.
*	@param [in]	u16DataSize
*					HIF data length.
*	@param [in]	u32Addr
*					HIF address.
*	@param [in]	grp
*					HIF group type.
*	@author
*	@date
*	@version	1.0
*/
static void m2m_ota_cb(uint8 u8OpCode, uint16 u16DataSize, uint32 u32Addr)
{
#ifdef ARDUINO
	(void)u16DataSize; // Silence "unused" warning
#endif
	sint8 ret = M2M_SUCCESS;
	if(u8OpCode == M2M_OTA_RESP_NOTIF_UPDATE_INFO)
	{
		tstrOtaUpdateInfo strOtaUpdateInfo;
		m2m_memset((uint8*)&strOtaUpdateInfo,0,sizeof(tstrOtaUpdateInfo));
		ret = hif_receive(u32Addr,(uint8*)&strOtaUpdateInfo,sizeof(tstrOtaUpdateInfo),0);
		if(ret == M2M_SUCCESS)
		{
			if(gpfOtaNotifCb)
				gpfOtaNotifCb(&strOtaUpdateInfo);
		}
	}
	else if (u8OpCode == M2M_OTA_RESP_UPDATE_STATUS)
	{
		tstrOtaUpdateStatusResp strOtaUpdateStatusResp;
		m2m_memset((uint8*)&strOtaUpdateStatusResp,0,sizeof(tstrOtaUpdateStatusResp));
		ret = hif_receive(u32Addr, (uint8*) &strOtaUpdateStatusResp,sizeof(tstrOtaUpdateStatusResp), 0);
		if(ret == M2M_SUCCESS)
		{
			if(gpfOtaUpdateCb)
				gpfOtaUpdateCb(strOtaUpdateStatusResp.u8OtaUpdateStatusType,strOtaUpdateStatusResp.u8OtaUpdateStatus);
		}
	}
	else
	{
		M2M_ERR("Invaild OTA resp %d ?\n",u8OpCode);
	}

}
/*!
@fn	\
	NMI_API sint8  m2m_ota_init(tpfOtaUpdateCb pfOtaUpdateCb, tpfOtaNotifCb pfOtaNotifCb);

@brief
	Initialize the OTA layer.

@param [in]	pfOtaUpdateCb
				OTA Update callback function

@param [in]	pfOtaNotifCb
				OTA notify callback function

@return
	The function SHALL return 0 for success and a negative value otherwise.
*/
NMI_API sint8  m2m_ota_init(tpfOtaUpdateCb pfOtaUpdateCb, tpfOtaNotifCb pfOtaNotifCb)
{
	sint8 ret = M2M_SUCCESS;

	if(pfOtaUpdateCb){
		gpfOtaUpdateCb = pfOtaUpdateCb;
	}else{
		M2M_ERR("Invaild Ota update cb\n");
	}
	if(pfOtaNotifCb){
		gpfOtaNotifCb = pfOtaNotifCb;
	}else{
		M2M_ERR("Invaild Ota notify cb\n");
	}

	hif_register_cb(M2M_REQ_GROUP_OTA,m2m_ota_cb);

	return ret;
}
/*!
@fn	\
	NMI_API sint8  m2m_ota_notif_set_url(uint8 * u8Url);

@brief
	Set the OTA url

@param [in]	u8Url
			 The url server address

@return
	The function SHALL return 0 for success and a negative value otherwise.
*/
NMI_API sint8  m2m_ota_notif_set_url(uint8 * u8Url)
{
	sint8 ret = M2M_SUCCESS;
	uint16 u16UrlSize = m2m_strlen(u8Url) + 1;
	/*Todo: we may change it to data pkt but we need to give it higer priority
			but the priorty is not implemnted yet in data pkt
	*/
	ret = hif_send(M2M_REQ_GROUP_OTA,M2M_OTA_REQ_NOTIF_SET_URL,u8Url,u16UrlSize,NULL,0,0);
	return ret;

}

/*!
@fn	\
	NMI_API sint8  m2m_ota_notif_check_for_update(void);

@brief
	check for ota update

@return
	The function SHALL return 0 for success and a negative value otherwise.
*/
NMI_API sint8  m2m_ota_notif_check_for_update(void)
{
	sint8 ret = M2M_SUCCESS;
	ret = hif_send(M2M_REQ_GROUP_OTA,M2M_OTA_REQ_NOTIF_CHECK_FOR_UPDATE,NULL,0,NULL,0,0);
	return ret;
}

/*!
@fn	\
	NMI_API sint8 m2m_ota_notif_sched(uint32 u32Period);

@brief
	Schedule OTA update

@param [in]	u32Period
	Period in days

@return
	The function SHALL return 0 for success and a negative value otherwise.
*/
NMI_API sint8 m2m_ota_notif_sched(uint32 u32Period)
{
#ifdef ARDUINO
	(void)u32Period; // Silence "unused" warning
#endif
	sint8 ret = M2M_SUCCESS;
	ret = hif_send(M2M_REQ_GROUP_OTA,M2M_OTA_REQ_NOTIF_CHECK_FOR_UPDATE,NULL,0,NULL,0,0);
	return ret;
}

/*!
@fn	\
	NMI_API sint8 m2m_ota_start_update(uint8 * u8DownloadUrl);

@brief
	Request OTA start update using the downloaded url

@param [in]	u8DownloadUrl
		The download firmware url, you get it from device info

@return
	The function SHALL return 0 for success and a negative value otherwise.

*/
NMI_API sint8 m2m_ota_start_update(uint8 * u8DownloadUrl)
{
	sint8 ret = M2M_SUCCESS;
	uint16 u16DurlSize = m2m_strlen(u8DownloadUrl) + 1;
	/*Todo: we may change it to data pkt but we need to give it higer priority
			but the priorty is not implemnted yet in data pkt
	*/
	ret = hif_send(M2M_REQ_GROUP_OTA,M2M_OTA_REQ_START_FW_UPDATE,u8DownloadUrl,u16DurlSize,NULL,0,0);
	return ret;
}
/*!
@fn	\
	NMI_API sint8 m2m_ota_start_update_crt(uint8 * u8DownloadUrl);

@brief
	Request OTA start for the Cortus app image.

@param [in]	u8DownloadUrl
		The cortus application image url.

@return
	The function SHALL return 0 for success and a negative value otherwise.

*/
NMI_API sint8 m2m_ota_start_update_crt(uint8 * u8DownloadUrl)
{
	sint8 ret = M2M_SUCCESS;
	uint16 u16DurlSize = m2m_strlen(u8DownloadUrl) + 1;
	ret = hif_send(M2M_REQ_GROUP_OTA,M2M_OTA_REQ_START_CRT_UPDATE,u8DownloadUrl,u16DurlSize,NULL,0,0);
	return ret;
}


/*!
@fn	\
	NMI_API sint8 m2m_ota_rollback(void);

@brief
	Request OTA Rollback image

@return
	The function SHALL return 0 for success and a negative value otherwise.
*/
NMI_API sint8 m2m_ota_rollback(void)
{
	sint8 ret = M2M_SUCCESS;
	ret = hif_send(M2M_REQ_GROUP_OTA,M2M_OTA_REQ_ROLLBACK_FW,NULL,0,NULL,0,0);
	return ret;
}
/*!
@fn	\
	NMI_API sint8 m2m_ota_rollback_crt(void);

@brief
	Request Cortus application OTA Rollback image

@return
	The function SHALL return 0 for success and a negative value otherwise.
*/
NMI_API sint8 m2m_ota_rollback_crt(void)
{
	sint8 ret = M2M_SUCCESS;
	ret = hif_send(M2M_REQ_GROUP_OTA,M2M_OTA_REQ_ROLLBACK_CRT,NULL,0,NULL,0,0);
	return ret;
}

/*!
@fn	\
	NMI_API sint8 m2m_ota_abort(void);

@brief
	Request OTA Abort

@return
	The function SHALL return 0 for success and a negative value otherwise.
*/
NMI_API sint8 m2m_ota_abort(void)
{
	sint8 ret = M2M_SUCCESS;
	ret = hif_send(M2M_REQ_GROUP_OTA,M2M_OTA_REQ_ABORT,NULL,0,NULL,0,0);
	return ret;
}


/*!
@fn	\
	NMI_API sint8 m2m_ota_switch_firmware(void);

@brief
	Switch to the upgraded Firmware

@return
	The function SHALL return 0 for success and a negative value otherwise.
*/
NMI_API sint8 m2m_ota_switch_firmware(void)
{
	sint8 ret = M2M_SUCCESS;
	ret = hif_send(M2M_REQ_GROUP_OTA,M2M_OTA_REQ_SWITCH_FIRMWARE,NULL,0,NULL,0,0);
	return ret;
}
/*!
@fn	\
	NMI_API sint8 m2m_ota_switch_crt(void);

@brief
	Switch to the upgraded cortus application.

@return
	The function SHALL return 0 for success and a negative value otherwise.
*/
NMI_API sint8 m2m_ota_switch_crt(void)
{
	sint8 ret = M2M_SUCCESS;
	ret = hif_send(M2M_REQ_GROUP_OTA,M2M_OTA_REQ_SWITCH_CRT_IMG,NULL,0,NULL,0,0);
	return ret;
}

/*!
@fn	\
	NMI_API sint8 m2m_ota_get_firmware_version(tstrM2mRev * pstrRev);

@brief
	Get the OTA Firmware version.

@return
	The function SHALL return 0 for success and a negative value otherwise.
*/
NMI_API sint8 m2m_ota_get_firmware_version(tstrM2mRev * pstrRev)
{
	sint8 ret = M2M_SUCCESS;
	ret = hif_chip_wake();
	if(ret == M2M_SUCCESS)
	{
    	ret = nm_get_ota_firmware_info(pstrRev);
		hif_chip_sleep();
	}
	return ret;
}
#if 0
#define M2M_OTA_FILE	"../../../m2m_ota.dat"
NMI_API sint8 m2m_ota_test(void)
{
	uint32 page  = 0;
	uint8 buffer[1500];
	uint32 u32Sz = 0;
	sint8 ret = M2M_SUCCESS;
	FILE *fp =NULL;
	fp = fopen(M2M_OTA_FILE,"rb");
	if(fp)
	{
		fseek(fp, 0L, SEEK_END);
		u32Sz = ftell(fp);
		fseek(fp, 0L, SEEK_SET);

		while(u32Sz > 0)
		{
			{
				page = (rand()%1400);

				if((page<100)||(page>1400)) page  = 1400;
			}

			if(u32Sz>page)
			{
				u32Sz-=page;
			}
			else
			{
				page = u32Sz;
				u32Sz = 0;
			}
			printf("page %d\n", (int)page);
			fread(buffer,page,1,fp);
			ret = hif_send(M2M_REQ_GROUP_OTA,M2M_OTA_REQ_TEST|M2M_REQ_DATA_PKT,NULL,0,(uint8*)&buffer,page,0);
			if(ret != M2M_SUCCESS)
			{
				M2M_ERR("\n");
			}
			nm_bsp_sleep(1);
		}

	}
	else
	{
		M2M_ERR("nO err\n");
	}
	return ret;
}
#endif

