/****************************************************************************/
/*
 * MODULE              JN-AN-1162 JenNet-IP Smart Home
 *
 * DESCRIPTION         IlluminanceStatus MIB Implementation
 */
/****************************************************************************/
/*
 * This software is owned by NXP B.V. and/or its supplier and is protected
 * under applicable copyright laws. All rights are reserved. We grant You,
 * and any third parties, a license to use this software solely and
 * exclusively on NXP products [NXP Microcontrollers such as JN5168, JN5164].
 * You, and any third parties must reproduce the copyright and warranty notice
 * and any other legend of ownership on each copy or partial copy of the
 * software.
 *
 * 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.
 *
 * Copyright NXP B.V. 2014. All rights reserved
 */
/****************************************************************************/

/****************************************************************************/
/***        Include files                                                 ***/
/****************************************************************************/
/* Standard includes */
#include <string.h>
/* SDK includes */
#include <jendefs.h>
/* Hardware includes */
#include <AppHardwareApi.h>
#include <PeripheralRegs.h>
/* Stack includes */
#include <Api.h>
#include <AppApi.h>
#include <JIP.h>
#include <6LP.h>
#include <AccessFunctions.h>
/* JenOS includes */
#include <dbg.h>
#include <dbg_uart.h>
#include <os.h>
#include <pdm.h>
/* Application device includes */
#include "DeviceDefs.h"
#include "Node.h"
#include "Address.h"
#include "MibSensor.h"
#include "MibIlluminanceStatus.h"
#include "MibIlluminanceConfig.h"
#include "MibIlluminanceControl.h"
#include "DriverIlluminance.h"

/****************************************************************************/
/***        Macro Definitions                                             ***/
/****************************************************************************/
/* Data access */
#define PS_MIB_ILLUMINANCE_CONFIG   ((tsMibIlluminanceConfig *)  psMibIlluminanceStatus->pvMibIlluminanceConfig)
#define PS_MIB_ILLUMINANCE_CONTROL  ((tsMibIlluminanceControl *) psMibIlluminanceStatus->pvMibIlluminanceControl)

/****************************************************************************/
/***        Type Definitions                                              ***/
/****************************************************************************/

/****************************************************************************/
/***        Local Function Prototypes                                     ***/
/****************************************************************************/

/****************************************************************************/
/***        Exported Variables                                            ***/
/****************************************************************************/

/****************************************************************************/
/***        Local Variables                                               ***/
/****************************************************************************/
PRIVATE tsMibIlluminanceStatus  *psMibIlluminanceStatus;

/****************************************************************************/
/***        Exported Functions                                            ***/
/****************************************************************************/

/****************************************************************************
 *
 * NAME: MibIlluminanceStatus_vInit
 *
 * DESCRIPTION:
 * Initialises data
 *
 ****************************************************************************/
PUBLIC void MibIlluminanceStatus_vInit(thJIP_Mib         	  hMibIlluminanceStatusInit,
                               		 tsMibIlluminanceStatus *psMibIlluminanceStatusInit,
                               		 void  				    *pvMibIlluminanceConfigInit,
                               		 void 				    *pvMibIlluminanceControlInit)
{
    /* Debug */
    DBG_vPrintf(DEBUG_MIB_ILLUMINANCE_STATUS, "\n%sMibIlluminanceStatus_vInit() {%d}", acDebugIndent, sizeof(tsMibIlluminanceStatus));

    /* Valid data pointer ? */
    if (psMibIlluminanceStatusInit != (tsMibIlluminanceStatus *) NULL)
    {
        /* Take copy of pointer to data */
        psMibIlluminanceStatus = psMibIlluminanceStatusInit;
        /* Take a copy of the MIB handle */
        psMibIlluminanceStatus->hMib = hMibIlluminanceStatusInit;

        /* Take a copy of the other MIB pointers */
		psMibIlluminanceStatus->pvMibIlluminanceConfig  = pvMibIlluminanceConfigInit;
		psMibIlluminanceStatus->pvMibIlluminanceControl = pvMibIlluminanceControlInit;

        /* Note the sensor paramters */
        psMibIlluminanceStatus->sTemp.u8SensorType 	  = DriverIlluminance_u8Type();
        psMibIlluminanceStatus->sTemp.u16LuxMin 	  = DriverIlluminance_u16LuxMin();
        psMibIlluminanceStatus->sTemp.u16LuxMax 	  = DriverIlluminance_u16LuxMax();
        psMibIlluminanceStatus->sTemp.u16LuxTolerance = DriverIlluminance_u16LuxTolerance();
    }

	/* Take a local copy of the mode */
	psMibIlluminanceStatus->u8Mode = PS_MIB_ILLUMINANCE_CONTROL->sPerm.u8Mode;
	/* Is illuminance sensor enabled ? */
	if (VAR_VAL_ILLUMINANCE_CONTROL_MODE_OFF != psMibIlluminanceStatus->u8Mode)
	{
		/* Initialise driver */
		DriverIlluminance_bInit();
		/* Start to take next reading */
		DriverIlluminance_bStart();
	}
	/* Valid illuminance LEDs ? */
	#if (DEVICE_OUTPUT_MASK_ILLUMINANCE_STATUS_LOW != 0 && DEVICE_OUTPUT_MASK_ILLUMINANCE_STATUS_HIGH != 0)
	{
		/* Configure as outputs */
		vAHI_DioSetDirection(0, (DEVICE_OUTPUT_MASK_ILLUMINANCE_STATUS_LOW | DEVICE_OUTPUT_MASK_ILLUMINANCE_STATUS_HIGH));
		/* Turn LEDs off (by turning output on) the tick function will turn them on as appropriate */
		vAHI_DioSetOutput((DEVICE_OUTPUT_MASK_ILLUMINANCE_STATUS_LOW | DEVICE_OUTPUT_MASK_ILLUMINANCE_STATUS_HIGH), 0);
	}
	#endif
}

/****************************************************************************
 *
 * NAME: MibIlluminanceStatus_vRegister
 *
 * DESCRIPTION:
 * Registers MIB
 *
 ****************************************************************************/
PUBLIC void MibIlluminanceStatus_vRegister(void)
{
    teJIP_Status eStatus;

    /* Debug */
    DBG_vPrintf(DEBUG_MIB_ILLUMINANCE_STATUS, "\n%sMibIlluminanceStatus_vRegister()", acDebugIndent);
    Node_vDebugIndent(DEBUG_MIB_ILLUMINANCE_STATUS);

	/* Start refresh timer */
	psMibIlluminanceStatus->u16StateRefreshTimer = PS_MIB_ILLUMINANCE_CONFIG->sPerm.u16StateRefresh;

    /* Register MIB */
    eStatus = eJIP_RegisterMib(psMibIlluminanceStatus->hMib);
    /* Debug */
    DBG_vPrintf(DEBUG_MIB_ILLUMINANCE_STATUS, "\n%seJIP_RegisterMib(IlluminanceStatus)=%d", acDebugIndent, eStatus);
    Node_vDebugOutdent(DEBUG_MIB_ILLUMINANCE_STATUS);
}

/****************************************************************************
 *
 * NAME: MibIlluminanceStatus_vTick
 *
 * DESCRIPTION:
 * Timer function
 *
 ****************************************************************************/
PUBLIC void MibIlluminanceStatus_vTick(void)
{
	/* Do we need to transmit the occupancy status ? */
	if (psMibIlluminanceStatus->bTxState)
	{
		/* Clear flag */
		psMibIlluminanceStatus->bTxState = FALSE;
		/* Restart refresh timer */
		psMibIlluminanceStatus->u16StateRefreshTimer = PS_MIB_ILLUMINANCE_CONFIG->sPerm.u16StateRefresh;
		/* Is the network running and up ? */
		if (Node_u32StackState() == NODE_STACK_STATE_RUNNING && Node_bUp())
		{
			/* Is a valid MIB specified to transmit to ? */
			if (PS_MIB_ILLUMINANCE_CONFIG->sPerm.u32StateMibId != 0)
			{
				teJIP_Status eResult;
				ts6LP_SockAddr s6LP_SockAddr;

				/* Form address */
				s6LP_SockAddr.sin6_family   = E_6LP_PF_INET6;
				s6LP_SockAddr.sin6_flowinfo = 0;
				s6LP_SockAddr.sin6_port     = JIP_DEFAULT_PORT;
				s6LP_SockAddr.sin6_scope_id = 0;
				memcpy(&s6LP_SockAddr.sin6_addr, &PS_MIB_ILLUMINANCE_CONFIG->sPerm.sStateAddress, sizeof(in6_addr));
				/* Transmit update */
				eResult = eJIP_Remote_Mib_Set(&s6LP_SockAddr, 0, PS_MIB_ILLUMINANCE_CONFIG->sPerm.u32StateMibId, PS_MIB_ILLUMINANCE_CONFIG->sPerm.u8StateVarIdx, E_JIP_VAR_TYPE_UINT8, &psMibIlluminanceStatus->sTemp.u8TargetStatus, 1);
				/* Debug */
				DBG_vPrintf(DEBUG_MIB_ILLUMINANCE_STATUS, "\n%sMibIlluminanceStatus_vTick() { eJIP_Remote_Mib_Set(0x%x, %d, %d) = %d }",
					acDebugIndent,
					PS_MIB_ILLUMINANCE_CONFIG->sPerm.u32StateMibId, PS_MIB_ILLUMINANCE_CONFIG->sPerm.u8StateVarIdx, psMibIlluminanceStatus->sTemp.u8TargetStatus,
					eResult);

				/* UART ? */
				#ifdef UART_H_INCLUDED
				{
					/* Initialise UART */
					UART_vChar(' ');
					UART_vChar('I');
					UART_vChar('=');
					UART_vChar('0'+eResult);
					UART_vChar(' ');
				}
				#endif
			}
		}
	}
	/* Are there any variable notifications pending ? */
	else if ((psMibIlluminanceStatus->u32NotifyChanged & VAR_MASK_ILLUMINANCE_STATUS) != 0)
	{
		/* Use common function to output notifications */
		Node_vJipNotifyChanged(psMibIlluminanceStatus->hMib, &psMibIlluminanceStatus->u32NotifyChanged, VAR_MASK_ILLUMINANCE_STATUS, VAR_COUNT_ILLUMINANCE_STATUS);
	}
}

/****************************************************************************
 *
 * NAME: MibIlluminanceStatus_vAppTimer100ms
 *
 * DESCRIPTION:
 * Timer function
 *
 ****************************************************************************/
PUBLIC void MibIlluminanceStatus_vAppTimer100ms(void)
{
	/* Has the mode been turned on ? */
	if (VAR_VAL_ILLUMINANCE_CONTROL_MODE_OFF == psMibIlluminanceStatus->u8Mode &&
	    VAR_VAL_ILLUMINANCE_CONTROL_MODE_OFF != PS_MIB_ILLUMINANCE_CONTROL->sPerm.u8Mode)
	{
		/* Take a local copy of the mode */
		psMibIlluminanceStatus->u8Mode = PS_MIB_ILLUMINANCE_CONTROL->sPerm.u8Mode;
		/* Initialise driver */
		DriverIlluminance_bInit();
		/* Start to take next reading */
		DriverIlluminance_bStart();
	}
	/* Has the mode been turned off ? */
	else if (VAR_VAL_ILLUMINANCE_CONTROL_MODE_OFF != psMibIlluminanceStatus->u8Mode &&
	         VAR_VAL_ILLUMINANCE_CONTROL_MODE_OFF == PS_MIB_ILLUMINANCE_CONTROL->sPerm.u8Mode)
	{
		/* Take a local copy of the mode */
		psMibIlluminanceStatus->u8Mode = PS_MIB_ILLUMINANCE_CONTROL->sPerm.u8Mode;
		/* Place the sensor into sleep mode */
		DriverIlluminance_bPowerDown();
		/* Valid illuminance LEDs ? */
		#if (DEVICE_OUTPUT_MASK_ILLUMINANCE_STATUS_LOW != 0 && DEVICE_OUTPUT_MASK_ILLUMINANCE_STATUS_HIGH != 0)
		{
			/* Turn LEDs off (by turning output on) the tick function will turn them on as appropriate */
			vAHI_DioSetOutput((DEVICE_OUTPUT_MASK_ILLUMINANCE_STATUS_LOW | DEVICE_OUTPUT_MASK_ILLUMINANCE_STATUS_HIGH), 0);
		}
		#endif
		/* Note sensor is disabled */
		psMibIlluminanceStatus->sTemp.u8TargetStatus = VAR_VAL_ILLUMINANCE_STATUS_TARGET_STATUS_DISABLED;
		/* Transmit occupied state */
		MibIlluminanceStatus_vQueueTxState(TRUE);
		/* Need to notify for the TargetStatus variable */
		psMibIlluminanceStatus->u32NotifyChanged |= (1 << VAR_IX_ILLUMINANCE_STATUS_TARGET_STATUS);
		/* Set invalid light level */
		psMibIlluminanceStatus->sTemp.u16LuxCurrent = 0xffff;
		/* Need to notify for the LuxCurrent variable */
		psMibIlluminanceStatus->u32NotifyChanged |= (1 << VAR_IX_ILLUMINANCE_STATUS_LUX_CURRENT);
	}

	/* Is the illuminance sensor enabled ? */
	if (VAR_VAL_ILLUMINANCE_CONTROL_MODE_OFF != psMibIlluminanceStatus->u8Mode)
	{
		/* Light sensor reading ready ? */
		//#warning Change this function name?
		if (DriverIlluminance_bTick())
		{
			uint16 u16Lux;

			/* Read result */
			u16Lux = DriverIlluminance_u16Lux();
			/* Start next conversion */
			DriverIlluminance_bStart();
			/* Valid result ? */
			if (u16Lux >= DriverIlluminance_u16LuxMin() &&
				u16Lux <= DriverIlluminance_u16LuxMax())
			{
				/* Increment readings */
				psMibIlluminanceStatus->u32Reading++;
			    DBG_vPrintf(TRUE, "\nI: %d=%d", psMibIlluminanceStatus->u32Reading, u16Lux);
				/* Different value to previous reading or target has changed ? */
				//if (psMibIlluminanceStatus->sTemp.u16LuxCurrent != u16Lux)
				{
					uint16 u16LuxBandMin;
					uint16 u16LuxBandMax;
					uint8   u8TargetStatus;

					/* Note new value */
					psMibIlluminanceStatus->sTemp.u16LuxCurrent = u16Lux;
					/* Flag that the value has changed */
					psMibIlluminanceStatus->bLuxCurrentChanged = TRUE;

					/* Calculate minimum level of band */
					if (PS_MIB_ILLUMINANCE_CONTROL->sPerm.u16LuxTarget >= (PS_MIB_ILLUMINANCE_CONTROL->sPerm.u16LuxBand / 2))
						u16LuxBandMin = PS_MIB_ILLUMINANCE_CONTROL->sPerm.u16LuxTarget - (PS_MIB_ILLUMINANCE_CONTROL->sPerm.u16LuxBand / 2);
					else
						u16LuxBandMin = 0;
					/* Calculate maximum level of band */
					if (PS_MIB_ILLUMINANCE_CONTROL->sPerm.u16LuxTarget <= 0xffff - (PS_MIB_ILLUMINANCE_CONTROL->sPerm.u16LuxBand / 2))
						u16LuxBandMax = PS_MIB_ILLUMINANCE_CONTROL->sPerm.u16LuxTarget + (PS_MIB_ILLUMINANCE_CONTROL->sPerm.u16LuxBand / 2);
					else
						u16LuxBandMax = 0xffff;

					/* Is light level too low ? */
					if (psMibIlluminanceStatus->sTemp.u16LuxCurrent < u16LuxBandMin)
					{
						/* Note light level is too low */
						u8TargetStatus = VAR_VAL_ILLUMINANCE_STATUS_TARGET_STATUS_LOW;
						/* Valid illuminance LEDs ? */
						#if (DEVICE_OUTPUT_MASK_ILLUMINANCE_STATUS_LOW != 0 && DEVICE_OUTPUT_MASK_ILLUMINANCE_STATUS_HIGH != 0)
						{
							/* Turn low LED on, high LED off */
							vAHI_DioSetOutput(DEVICE_OUTPUT_MASK_ILLUMINANCE_STATUS_LOW, DEVICE_OUTPUT_MASK_ILLUMINANCE_STATUS_HIGH);
						}
						#endif
					}
					/* Is light level too high ? */
					else if (psMibIlluminanceStatus->sTemp.u16LuxCurrent > u16LuxBandMax)
					{
						/* Note light level is too high */
						u8TargetStatus = VAR_VAL_ILLUMINANCE_STATUS_TARGET_STATUS_HIGH;
						/* Valid illuminance LEDs ? */
						#if (DEVICE_OUTPUT_MASK_ILLUMINANCE_STATUS_LOW != 0 && DEVICE_OUTPUT_MASK_ILLUMINANCE_STATUS_HIGH != 0)
						{
							/* Turn High LED on, low LED off */
							vAHI_DioSetOutput(DEVICE_OUTPUT_MASK_ILLUMINANCE_STATUS_HIGH, DEVICE_OUTPUT_MASK_ILLUMINANCE_STATUS_LOW);
						}
						#endif
					}
					/* Light level is ok ? */
					else
					{
						/* Note light level is ok */
						u8TargetStatus = VAR_VAL_ILLUMINANCE_STATUS_TARGET_STATUS_OK;
						/* Valid illuminance LEDs ? */
						#if (DEVICE_OUTPUT_MASK_ILLUMINANCE_STATUS_LOW != 0 && DEVICE_OUTPUT_MASK_ILLUMINANCE_STATUS_HIGH != 0)
						{
							/* Turn LEDs on (by turning output off) */
							vAHI_DioSetOutput(0, (DEVICE_OUTPUT_MASK_ILLUMINANCE_STATUS_LOW | DEVICE_OUTPUT_MASK_ILLUMINANCE_STATUS_HIGH));
						}
						#endif
					}

					/* Different value to previous reading ? */
					if (psMibIlluminanceStatus->sTemp.u8TargetStatus != u8TargetStatus)
					{
						/* Note new value */
						psMibIlluminanceStatus->sTemp.u8TargetStatus = u8TargetStatus;
						/* Transmit occupied state */
						MibIlluminanceStatus_vQueueTxState(TRUE);
						/* Need to notify for the TargetStatus variable */
						psMibIlluminanceStatus->u32NotifyChanged |= (1 << VAR_IX_ILLUMINANCE_STATUS_TARGET_STATUS);
					}
				}
			}
		}
		/* Plenty of time on refresh timer ? */
		if (psMibIlluminanceStatus->u16StateRefreshTimer > DEVICE_WAKE_TIMER_PERIOD_10MS)
		{
			/* Decrement refresh timer */
			psMibIlluminanceStatus->u16StateRefreshTimer -= DEVICE_WAKE_TIMER_PERIOD_10MS;
		}
		/* Refresh timer about to expire ? */
		else if (psMibIlluminanceStatus->u16StateRefreshTimer > 0)
		{
			/* Debug */
			DBG_vPrintf(DEBUG_MIB_ILLUMINANCE_STATUS, "\n%sMibIlluminanceStatus_vAppTimer100ms(Refresh)", acDebugIndent);
			/* Zero refresh timer */
			psMibIlluminanceStatus->u16StateRefreshTimer = 0;
			/* Transmit occupied state */
			MibIlluminanceStatus_vQueueTxState(FALSE);
		}
	}
}

/****************************************************************************
 *
 * NAME: MibIlluminanceStatus_vQueueTxState
 *
 * DESCRIPTION:
 * Queues a occupancy state transmission
 *
 ****************************************************************************/
PUBLIC void MibIlluminanceStatus_vQueueTxState(bool_t bImmediate)
{
	/* Is the network up and we have a valid MIB to transmit to ? */
	if (Node_bUp() && PS_MIB_ILLUMINANCE_CONFIG->sPerm.u32StateMibId != 0)
	{
		/* End device build ? */
		#ifdef MK_BLD_NODE_TYPE_END_DEVICE
		{
			/* Debug */
			DBG_vPrintf(DEBUG_MIB_ILLUMINANCE_STATUS, "\n%sMibIlluminanceStatus_vQueueTxState(); { bTxState = TRUE }", acDebugIndent);
			/* Is the stack not running ? */
			if (Node_u32StackState() != NODE_STACK_STATE_RUNNING)
			{
				/* Immediate transmission ? */
				if (bImmediate)
				{
					/* Resume running the stack */
					Node_v6lpResume();
				}
			}
			/* Flag bulb control transmission */
			psMibIlluminanceStatus->bTxState = TRUE;
		}
		/* Coordinator or router build ? */
		#else
		{
			/* Stack is running ? */
			if (Node_u32StackState() == NODE_STACK_STATE_RUNNING)
			{
				/* Debug */
				DBG_vPrintf(DEBUG_MIB_ILLUMINANCE_STATUS, "\n%sMibIlluminanceStatus_vQueueTxState(); { bTxState = TRUE }", acDebugIndent);
				/* Flag bulb control transmission */
				psMibIlluminanceStatus->bTxState = TRUE;
			}
		}
		#endif
	}

	/* Debug */
	Node_vDebugOutdent(DEBUG_MIB_ILLUMINANCE_STATUS);
}

/****************************************************************************
 *
 * NAME: MibIlluminanceStatus_vResume
 *
 * DESCRIPTION
 * Resume running after a wake
 *
 ****************************************************************************/
#ifdef MK_BLD_NODE_TYPE_END_DEVICE
PUBLIC void MibIlluminanceStatus_vResume(void)
{
	/* Open bus */
	DriverIlluminance_vOpen();
}
#endif

/****************************************************************************
 *
 * NAME: MibIlluminanceStatus_vSleep
 *
 * DESCRIPTION
 * Stop running before a sleep
 *
 ****************************************************************************/
#ifdef MK_BLD_NODE_TYPE_END_DEVICE
PUBLIC void MibIlluminanceStatus_vSleep(void)
{
	/* Close bus */
	DriverIlluminance_vClose();
}
#endif

/****************************************************************************/
/***        END OF FILE                                                   ***/
/****************************************************************************/
