/****************************************************************************/
/*
 * MODULE              JN-AN-1162 JenNet-IP Smart Home
 *
 * DESCRIPTION         IlluminanceControl 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 "MibIlluminanceControl.h"
#include "MibIlluminanceStatus.h"
#include "MibIlluminanceScene.h"
#include "MibGroup.h"

/****************************************************************************/
/***        Macro Definitions                                             ***/
/****************************************************************************/
/* Data access */
#define PS_MIB_ILLUMINANCE_STATUS ((tsMibIlluminanceStatus *) psMibIlluminanceControl->pvMibIlluminanceStatus)
#define PS_MIB_ILLUMINANCE_SCENE  ((tsMibIlluminanceScene *)  psMibIlluminanceControl->pvMibIlluminanceScene)

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

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

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

/****************************************************************************/
/***        Local Variables                                               ***/
/****************************************************************************/
PRIVATE tsMibIlluminanceControl *psMibIlluminanceControl;  /* MIB data */

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

/****************************************************************************
 *
 * NAME: MibIlluminanceControl_vInit
 *
 * DESCRIPTION:
 * Initialises data
 *
 ****************************************************************************/
PUBLIC void MibIlluminanceControl_vInit(thJIP_Mib        hMibIlluminanceControlInit,
                         	   tsMibIlluminanceControl *psMibIlluminanceControlInit,
                         	   void					   *pvMibIlluminanceStatusInit,
                         	   void 				   *pvMibIlluminanceSceneInit)
{
    /* Debug */
    DBG_vPrintf(DEBUG_MIB_ILLUMINANCE_CONTROL, "\n%sMibIlluminanceControl_vInit() {%d}", acDebugIndent, sizeof(tsMibIlluminanceControl));
    Node_vDebugIndent(DEBUG_MIB_ILLUMINANCE_CONTROL);

    /* Valid data pointer ? */
    if (psMibIlluminanceControlInit != (tsMibIlluminanceControl *) NULL)
    {
        /* Take copy of pointer to data */
        psMibIlluminanceControl = psMibIlluminanceControlInit;
        /* Take a copy of the MIB handle */
        psMibIlluminanceControl->hMib = hMibIlluminanceControlInit;

        /* Take a copy of the illuminance scene MIB pointer */
		psMibIlluminanceControl->pvMibIlluminanceStatus = pvMibIlluminanceStatusInit;
		psMibIlluminanceControl->pvMibIlluminanceScene  = pvMibIlluminanceSceneInit;

        /* Load Dio mib data */
        (void) PDM_eLoadRecord(&psMibIlluminanceControl->sDesc,
							   (uint16)(MIB_ID_ILLUMINANCE_CONTROL & 0xFFFF),
                               (void *) &psMibIlluminanceControl->sPerm,
                               sizeof(psMibIlluminanceControl->sPerm),
                               FALSE);
		/* Debug */
		DBG_vPrintf(DEBUG_MIB_ILLUMINANCE_CONTROL, "\n%sPDM_eLoadRecord(IlluminanceControl) = %d", acDebugIndent, psMibIlluminanceControl->sDesc.eState);

		/* Building Group MIB ? */
		#if MK_BLD_MIB_GROUP
		{
			/* Was the group's MIB PDM not recovered ? */
			if (MibGroup_ePdmStateInit() != PDM_RECOVERY_STATE_RECOVERED)
			{
				in6_addr sAddr;

				/* Build "All Illuminance Sensors" group address */
				Address_vBuildGroup(&sAddr, NULL, (uint16)(MIB_ID_ILLUMINANCE_CONTROL & 0xffff));
				/* Make a dummy call to get it in the group address array */
				bJIP_GroupCallback(E_JIP_GROUP_JOIN, &sAddr);
			}
		}
		#endif
    }

    /* Debug */
	Node_vDebugOutdent(DEBUG_MIB_ILLUMINANCE_CONTROL);

}

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

    /* Debug */
    DBG_vPrintf(DEBUG_MIB_ILLUMINANCE_CONTROL, "\n%sMibIlluminanceControl_vRegister()", acDebugIndent);
    Node_vDebugIndent(DEBUG_MIB_ILLUMINANCE_CONTROL);

    /* Register MIB */
    eStatus = eJIP_RegisterMib(psMibIlluminanceControl->hMib);
    /* Debug */
    DBG_vPrintf(DEBUG_MIB_ILLUMINANCE_CONTROL, "\n%seJIP_RegisterMib(IlluminanceControl)=%d", acDebugIndent, eStatus);

    /* Make sure permament data is saved */
    PDM_vSaveRecord(&psMibIlluminanceControl->sDesc);
    /* Debug */
    DBG_vPrintf(DEBUG_MIB_ILLUMINANCE_CONTROL, "\n%sPDM_vSaveRecord(IlluminanceControl) = %d", acDebugIndent, psMibIlluminanceControl->sDesc.eState);
    /* Debug */
    Node_vDebugOutdent(DEBUG_MIB_ILLUMINANCE_CONTROL);
}

/****************************************************************************
 *
 * NAME: MibIlluminanceControl_eSetMode
 *
 * DESCRIPTION:
 * Mode set data callback
 *
 ****************************************************************************/
PUBLIC teJIP_Status MibIlluminanceControl_eSetMode(uint8 u8Val, void *pvCbData)
{
	teJIP_Status eReturn;

	/* Call standard function */
	eReturn = eSetUint8(u8Val, pvCbData);

    /* Make sure permament data is saved */
    PDM_vSaveRecord(&psMibIlluminanceControl->sDesc);

    /* Debug */
    DBG_vPrintf(DEBUG_MIB_ILLUMINANCE_CONTROL, "\n%sMibIlluminanceControl_eSetMode(%d) = %d { PDM_vSaveRecord(IlluminanceControl) = %d }", acDebugIndent, u8Val, eReturn, psMibIlluminanceControl->sDesc.eState);
	return eReturn;
}

/****************************************************************************
 *
 * NAME: MibIlluminanceControl_eSetSceneId
 *
 * DESCRIPTION:
 * Generic set data callback
 *
 ****************************************************************************/
PUBLIC teJIP_Status MibIlluminanceControl_eSetSceneId(uint16 u16Val, void *pvCbData)
{
    teJIP_Status eReturn = E_JIP_ERROR_BAD_VALUE;
    uint8        u8Scene;

    /* Valid scene ? */
    if (u16Val > 0)
    {
        /* Assume scenes are disabled */
        eReturn = E_JIP_ERROR_DISABLED;
        /* Scene MIB available ? */
        if (PS_MIB_ILLUMINANCE_SCENE != NULL)
        {
            /* Try to find scene */
            u8Scene = MibIlluminanceScene_u8FindSceneId(u16Val);
            /* Found valid scene ? */
            if (u8Scene < MIB_ILLUMINANCE_SCENE_SCENES)
            {
				uint16 u16LuxTarget;
				uint16 u16LuxBand;
                /* Call standard function */
                eReturn = eSetUint16(u16Val, pvCbData);
                /* Copy the values from the scene */
                u16LuxTarget = PS_MIB_ILLUMINANCE_SCENE->sPerm.asSceneTable[u8Scene].u16LuxTarget;
                u16LuxBand   = PS_MIB_ILLUMINANCE_SCENE->sPerm.asSceneTable[u8Scene].u16LuxBand;
                /* Limit the scene values */
				if      (u16LuxTarget < PS_MIB_ILLUMINANCE_STATUS->sTemp.u16LuxMin) u16LuxTarget = PS_MIB_ILLUMINANCE_STATUS->sTemp.u16LuxMin;
				else if (u16LuxTarget > PS_MIB_ILLUMINANCE_STATUS->sTemp.u16LuxMax) u16LuxTarget = PS_MIB_ILLUMINANCE_STATUS->sTemp.u16LuxMax;
				if      (u16LuxBand   < MIB_ILLUMINANCE_CONTROL_LUX_BAND_MIN) 		u16LuxBand   = MIB_ILLUMINANCE_CONTROL_LUX_BAND_MIN;
				else if (u16LuxBand   > MIB_ILLUMINANCE_CONTROL_LUX_BAND_MAX) 		u16LuxBand   = MIB_ILLUMINANCE_CONTROL_LUX_BAND_MAX;
                /* New value for target ? */
                if (psMibIlluminanceControl->sPerm.u16LuxTarget != u16LuxTarget)
                {
	                /* Store new value */
    	            psMibIlluminanceControl->sPerm.u16LuxTarget = u16LuxTarget;
					/* Notify any devices with traps set ? */
                	psMibIlluminanceControl->u32NotifyChanged |= (1<<VAR_IX_ILLUMINANCE_CONTROL_LUX_TARGET);
	                /* Make sure permament data is saved */
    	            psMibIlluminanceControl->bSaveRecord = TRUE;
				}
                /* New value for band ? */
                if (psMibIlluminanceControl->sPerm.u16LuxBand != u16LuxBand)
                {
	                /* Store new value */
    	            psMibIlluminanceControl->sPerm.u16LuxBand = u16LuxBand;
					/* Notify any devices with traps set ? */
                	psMibIlluminanceControl->u32NotifyChanged |= (1<<VAR_IX_ILLUMINANCE_CONTROL_LUX_BAND);
	                /* Make sure permament data is saved */
    	            psMibIlluminanceControl->bSaveRecord = TRUE;
				}
            }
            /* Couldn't find scene ? */
            else
            {
                /* Return bad value */
                eReturn = E_JIP_ERROR_BAD_VALUE;
            }
        }
    }

    return eReturn;
}

/****************************************************************************
 *
 * NAME: MibIlluminanceControl_eSetUint16
 *
 * DESCRIPTION:
 * Generic set data callback
 *
 ****************************************************************************/
PUBLIC teJIP_Status MibIlluminanceControl_eSetUint16(uint16 u16Val, void *pvCbData)
{
	teJIP_Status eReturn;

	/* Call standard function */
	eReturn = eSetUint16(u16Val, pvCbData);

	/* Make sure permament data is saved */
	psMibIlluminanceControl->bSaveRecord = TRUE;

	return eReturn;
}

/****************************************************************************
 *
 * NAME: MibIlluminanceControl_eSetLuxAdjust
 *
 * DESCRIPTION:
 * LuxAdjust set data callback
 *
 ****************************************************************************/
PUBLIC teJIP_Status MibIlluminanceControl_eSetLuxAdjust(uint8 u8Val, void *pvCbData)
{
	teJIP_Status eReturn;

	/* End device */
	#ifdef MK_BLD_NODE_TYPE_END_DEVICE
	{
		/* Return disabled */
		eReturn = E_JIP_ERROR_DISABLED;
	}
	/* Not an end device ? */
	#else
	{
		/* Return bad value is out of range */
		eReturn = E_JIP_ERROR_BAD_VALUE;
		/* Value in range? */
		if (u8Val <= VAR_VAL_ILLUMINANCE_CONTROL_LUX_ADJUST_BAND_WIDEN)
		{
			/* Call standard function */
			eReturn = eSetUint8(u8Val, pvCbData);
		}
	}
	#endif

	return eReturn;
}

/****************************************************************************
 *
 * NAME: MibIlluminanceControl_eSetLuxTargetChange
 *
 * DESCRIPTION:
 * LuxTargetChange set data callback
 *
 ****************************************************************************/
PUBLIC teJIP_Status MibIlluminanceControl_eSetLuxTargetChange(int16 i16Val, void *pvCbData)
{
	teJIP_Status   eReturn;
	int32		 i32LuxTarget;

	/* Call standard function */
	eReturn = eSetUint16(i16Val, pvCbData);
	/* Calculate new lux target */
	i32LuxTarget = (int32) psMibIlluminanceControl->sPerm.u16LuxTarget + (int32) i16Val;
	/* Limit target */
	if      (i32LuxTarget < (int32) PS_MIB_ILLUMINANCE_STATUS->sTemp.u16LuxMin) i32LuxTarget = (int32) PS_MIB_ILLUMINANCE_STATUS->sTemp.u16LuxMin;
	else if (i32LuxTarget > (int32) PS_MIB_ILLUMINANCE_STATUS->sTemp.u16LuxMax) i32LuxTarget = (int32) PS_MIB_ILLUMINANCE_STATUS->sTemp.u16LuxMax;
	/* Value has changed ? */
	if ((int32) psMibIlluminanceControl->sPerm.u16LuxTarget != i32LuxTarget)
	{
		/* Update to new value */
		psMibIlluminanceControl->sPerm.u16LuxTarget = (uint16) i32LuxTarget;
		/* Update any traps */
        psMibIlluminanceControl->u32NotifyChanged |= (1<<VAR_IX_ILLUMINANCE_CONTROL_LUX_TARGET);
	}

	return eReturn;
}

/****************************************************************************
 *
 * NAME: MibIlluminanceControl_eSetLuxBandChange
 *
 * DESCRIPTION:
 * LuxBandChange set data callback
 *
 ****************************************************************************/
PUBLIC teJIP_Status MibIlluminanceControl_eSetLuxBandChange(int16 i16Val, void *pvCbData)
{
	teJIP_Status   eReturn;
	int32		 i32LuxBand;

	/* Call standard function */
	eReturn = eSetUint16(i16Val, pvCbData);
	/* Calculate new lux target */
	i32LuxBand = (int32) psMibIlluminanceControl->sPerm.u16LuxBand + (int32) i16Val;
	/* Limit target */
	if      (i32LuxBand < (int32) MIB_ILLUMINANCE_CONTROL_LUX_BAND_MIN) i32LuxBand = (int32) MIB_ILLUMINANCE_CONTROL_LUX_BAND_MIN;
	else if (i32LuxBand > (int32) MIB_ILLUMINANCE_CONTROL_LUX_BAND_MAX) i32LuxBand = (int32) MIB_ILLUMINANCE_CONTROL_LUX_BAND_MAX;
	/* Value has changed ? */
	if ((int32) psMibIlluminanceControl->sPerm.u16LuxBand != i32LuxBand)
	{
		/* Update to new value */
		psMibIlluminanceControl->sPerm.u16LuxBand = (uint16) i32LuxBand;
		/* Update any traps */
        psMibIlluminanceControl->u32NotifyChanged |= (1<<VAR_IX_ILLUMINANCE_CONTROL_LUX_BAND);
	}

	return eReturn;
}

/****************************************************************************
 *
 * NAME: MibIlluminanceControl_vTick
 *
 * DESCRIPTION:
 * Called once per 10ms
 *
 ****************************************************************************/
PUBLIC void MibIlluminanceControl_vTick(void)
{
	/* End device build ? */
	#ifndef MK_BLD_NODE_TYPE_END_DEVICE
	{
		/* Do we need to adjust the settings */
		if (VAR_VAL_ILLUMINANCE_CONTROL_LUX_ADJUST_NONE != psMibIlluminanceControl->sTemp.u8LuxAdjust)
		{
			bool_t bLuxTargetEnd = FALSE;
			bool_t bLuxBandEnd = FALSE;

			/* Which mode are we in ? */
			switch (psMibIlluminanceControl->sTemp.u8LuxAdjust)
			{
				/* Decreasing LuxTarget ? */
				case VAR_VAL_ILLUMINANCE_CONTROL_LUX_ADJUST_TARGET_DOWN:
				{
					/* Current target is comfortably above minimum ? */
					if (psMibIlluminanceControl->sPerm.u16LuxTarget > PS_MIB_ILLUMINANCE_STATUS->sTemp.u16LuxMin + MIB_ILLUMINANCE_CONTROL_LUX_TARGET_ADJUST)
					{
						/* Decrease lux target */
						psMibIlluminanceControl->sPerm.u16LuxTarget -= MIB_ILLUMINANCE_CONTROL_LUX_TARGET_ADJUST;
					}
					/* Current target is close to minimum ? */
					else if (psMibIlluminanceControl->sPerm.u16LuxTarget > PS_MIB_ILLUMINANCE_STATUS->sTemp.u16LuxMin)
					{
						/* Decrease to minimum */
						psMibIlluminanceControl->sPerm.u16LuxTarget = PS_MIB_ILLUMINANCE_STATUS->sTemp.u16LuxMin;
						/* End adjusting LuxTarget */
						bLuxTargetEnd = TRUE;
					}
					/* Current target is at minimum ? */
					else
					{
						/* End adjusting LuxTarget */
						bLuxTargetEnd = TRUE;
					}
				}
				break;

				/* Increasing LuxTarget ? */
				case VAR_VAL_ILLUMINANCE_CONTROL_LUX_ADJUST_TARGET_UP:
				{
					/* Current target is comfortably below maximum ? */
					if (psMibIlluminanceControl->sPerm.u16LuxTarget < PS_MIB_ILLUMINANCE_STATUS->sTemp.u16LuxMax - MIB_ILLUMINANCE_CONTROL_LUX_TARGET_ADJUST)
					{
						/* Increase lux target */
						psMibIlluminanceControl->sPerm.u16LuxTarget += MIB_ILLUMINANCE_CONTROL_LUX_TARGET_ADJUST;
					}
					/* Current target is close to maximum ? */
					else if (psMibIlluminanceControl->sPerm.u16LuxTarget < PS_MIB_ILLUMINANCE_STATUS->sTemp.u16LuxMax)
					{
						/* Increase to maximum */
						psMibIlluminanceControl->sPerm.u16LuxTarget = PS_MIB_ILLUMINANCE_STATUS->sTemp.u16LuxMax;
						/* End adjusting LuxTarget */
						bLuxTargetEnd = TRUE;
					}
					/* Current target is at minimum ? */
					else
					{
						/* End adjusting LuxTarget */
						bLuxTargetEnd = TRUE;
					}
				}
				break;

				/* Decreasing LuxBand ? */
				case VAR_VAL_ILLUMINANCE_CONTROL_LUX_ADJUST_BAND_NARROW:
				{
					/* Current target is comfortably above minimum ? */
					if (psMibIlluminanceControl->sPerm.u16LuxBand > MIB_ILLUMINANCE_CONTROL_LUX_BAND_MIN + MIB_ILLUMINANCE_CONTROL_LUX_BAND_ADJUST)
					{
						/* Decrease lux target */
						psMibIlluminanceControl->sPerm.u16LuxBand -= MIB_ILLUMINANCE_CONTROL_LUX_BAND_ADJUST;
					}
					/* Current target is close to minimum ? */
					else if (psMibIlluminanceControl->sPerm.u16LuxBand > MIB_ILLUMINANCE_CONTROL_LUX_BAND_MIN)
					{
						/* Decrease to minimum */
						psMibIlluminanceControl->sPerm.u16LuxBand = MIB_ILLUMINANCE_CONTROL_LUX_BAND_MIN;
						/* End adjusting LuxBand */
						bLuxBandEnd = TRUE;
					}
					/* Current target is at minimum ? */
					else
					{
						/* End adjusting LuxBand */
						bLuxBandEnd = TRUE;
					}
				}
				break;

				/* Increasing LuxBand ? */
				case VAR_VAL_ILLUMINANCE_CONTROL_LUX_ADJUST_BAND_WIDEN:
				{
					/* Current target is comfortably below maximum ? */
					if (psMibIlluminanceControl->sPerm.u16LuxBand < MIB_ILLUMINANCE_CONTROL_LUX_BAND_MAX - MIB_ILLUMINANCE_CONTROL_LUX_BAND_ADJUST)
					{
						/* Increase lux target */
						psMibIlluminanceControl->sPerm.u16LuxBand += MIB_ILLUMINANCE_CONTROL_LUX_BAND_ADJUST;
					}
					/* Current target is close to maximum ? */
					else if (psMibIlluminanceControl->sPerm.u16LuxBand < MIB_ILLUMINANCE_CONTROL_LUX_BAND_MAX)
					{
						/* Increase to maximum */
						psMibIlluminanceControl->sPerm.u16LuxBand = MIB_ILLUMINANCE_CONTROL_LUX_BAND_MAX;
						/* End adjusting LuxBand */
						bLuxBandEnd = TRUE;
					}
					/* Current target is at minimum ? */
					else
					{
						/* End adjusting LuxBand */
						bLuxBandEnd = TRUE;
					}
				}
				break;
			}

			/* Need to end adjustments ? */
			if (bLuxTargetEnd || bLuxBandEnd)
			{
				/* Return to no adjust mode */
				psMibIlluminanceControl->sTemp.u8LuxAdjust = VAR_VAL_ILLUMINANCE_CONTROL_LUX_ADJUST_NONE;
				/* Notify anything with a trap set */
				psMibIlluminanceControl->u32NotifyChanged |= (1<<VAR_IX_ILLUMINANCE_CONTROL_LUX_ADJUST);
				/* Ending target change - notify anything with a trap set */
				if (bLuxTargetEnd) psMibIlluminanceControl->u32NotifyChanged |= (1<<VAR_IX_ILLUMINANCE_CONTROL_LUX_TARGET);
				/* Ending band change - notify anything with a trap set */
				if (bLuxBandEnd)   psMibIlluminanceControl->u32NotifyChanged |= (1<<VAR_IX_ILLUMINANCE_CONTROL_LUX_BAND);
			}
		}
	}
	#endif

	/* Are there any variable notifications pending ? */
	if ((psMibIlluminanceControl->u32NotifyChanged & VAR_MASK_ILLUMINANCE_CONTROL) != 0)
	{
		/* Use common function to output notifications */
		Node_vJipNotifyChanged(psMibIlluminanceControl->hMib, &psMibIlluminanceControl->u32NotifyChanged, VAR_MASK_ILLUMINANCE_CONTROL, VAR_COUNT_ILLUMINANCE_CONTROL);
	}
}
/****************************************************************************/
/***        END OF FILE                                                   ***/
/****************************************************************************/
