/**
 * @file pca9673_gpio_expander.c
 *
 * @brief This file contains functions that drives the PCA9673 GPIO expander
 *
 * @copyright Copyright 2023 Antaris, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "../inc/pca9673_gpio_expander.h"


/* Static function declarations */
/*!
 * @brief This utility API is used to set or reset a bit in the data
 * @param[in] data  : Data whose bit needs to be set or reset
 * @param[in] pin   : bit position.
 * @param[in] flag  : set or reset
 * @return data after the required bit is set or reset
 * @retval data after the required bit is set or reset
 */
static uint8_t bit_write (uint8_t data, pca9673_gpio_pin pin, uint8_t flag);

static uint8_t bit_write (uint8_t data, pca9673_gpio_pin pin, uint8_t flag);
 int8_t gpio_exp_self_test(pca9673_gpio_pin pin);
/*!
 * @brief This utility API is used to check the validity of the gpio pin number
 * @param[in] pin  : pin number that needs to validated.
 * @return Result of pin number validation
 * @retval PCA9673_OK -> Success / -PCA9673_E_OUT_OF_RANGE -> Error.
 */
static int8_t is_gpio_valid (pca9673_gpio_pin pin);

/*********************** User function definitions ****************************/
/**
 * @brief This utility API is used to check the validity of the gpio pin number
 */
static int8_t is_gpio_valid (pca9673_gpio_pin pin)
{
    int8_t status;

    if((PCA9673_GPIO_17 < pin))
    {
        status = PCA9673_E_OUT_OF_RANGE;
    }
    else
    {
        status = PCA9673_OK;
    }

    return status;
}

/**
 * @brief This API initializes the gpio pin as per the gpio_config. This api
 * needs to be called for all the gpio expander pins.
 */
int8_t pca9673_gpio_init(pca9673_dev *hdev)
{
    int8_t rslt;

    if(hdev == NULL)
    {
        rslt = PCA9673_E_NULL_PTR;
    }
    else
    {
        rslt=PCA9673_OK;
    }
    return rslt;
}

/**
 * @brief This utility API is used to set or reset a bit in the data
 */
static uint8_t bit_write (uint8_t data, pca9673_gpio_pin pin, uint8_t flag)
{
	if(pin < 8)
	{
		if(flag)
		{
			data = (data | (1 << pin));
		}
		else
		{
			data = (data & ~(1 << pin));
		}
	}
	else if(pin >= 8)
	{
		if(flag)
		{
			data = (data | (1 << (pin%8)));
		}
		else
		{
			data = (data & ~(1 << (pin%8)));
		}
	}
    return data;
}


/**
 * @brief This API is used to read the pca9673 registers and writes data to it ,to validate GPIO expender
 */
int8_t pca9673_self_test(pca9673_dev *hdev)
{
	int8_t sts=PCA9673_E_COM_FAIL;
	uint8_t rx_data[3];
	uint8_t tx_data = 0x3a;
	sts = hdev->write(hdev->io_intf_hdle, 0xf8, &tx_data, 1);
	sts = hdev->read(hdev->io_intf_hdle, 0xf8, rx_data, 3);
	if(sts ==PCA9673_OK)
	{
		sts= PCA9673_OK;
	}
	else
	{
		sts=PCA9673_E_COM_FAIL;
	}
	return sts ;
}

/**
 * @brief This API is used to read the pca9673 registers through i2c interface
 */
int8_t pca9673_read(pca9673_dev *hdev, uint8_t *data, uint16_t len)
{
    int8_t rslt = PCA9673_OK;

    if ((hdev == NULL) || (hdev->read == NULL))
    {
        rslt = PCA9673_E_NULL_PTR;
    }
    else if (len == 0)
    {
        rslt = PCA9673_E_READ_WRITE_LENGTH_INVALID;
    }
    else
    {

        rslt = hdev->read(hdev->io_intf_hdle, hdev->slave_addr, data, len);
        uint32_t i=0;
		for(i=0;i<90000;i++);
    }

    return rslt;
}

/**
 * @brief This API is used to write the pca9673 registers through i2c
 * interface
 */
int8_t pca9673_write(pca9673_dev *hdev, uint8_t *data, uint16_t len)
{
    int8_t rslt = PCA9673_OK;
    uint32_t i=0;
    if ((hdev == NULL) || (hdev->read == NULL))
    {
        rslt = PCA9673_E_NULL_PTR;
    }
    else if (len == 0)
    {
        rslt = PCA9673_E_READ_WRITE_LENGTH_INVALID;
    }
    else
    {
        rslt = hdev->write(hdev->io_intf_hdle, hdev->slave_addr, data, len);
		for(i=0;i<500000;i++);
    }

    return rslt;
}

/**
 * @brief This API is used to read the state of the gpio pin
 */
int8_t pca9673_readpin(pca9673_dev *hdev,pca9673_gpio_pin pin, pca9673_gpio_pinstate *state)
{
    int8_t status;
    uint8_t data[2];
    status = is_gpio_valid(pin);
    if (PCA9673_OK != status)
    {
        status = PCA9673_E_OUT_OF_RANGE;
    }
    else
    {
		status = pca9673_read(hdev,data,2);
		if (PCA9673_OK == status)
		{
			//if(pin & 0x00FF)
			if(pin < 8)
			{
				data[0] = (data[0] & pin);
				if(data[0] >0)
				{
					*state  = PCA9673_PIN_SET;
				}
				else
				{
					*state  = PCA9673_PIN_RESET;
				}
			}
			else if(pin >= 8)
			{
				//data[1] = (data[1] & pin%10);
				//data[1] = (data[1] & pin%8);
				//data[1] = (data[1] & (1 << (pin%8)) );
				if((data[1] & (1 << (pin%8)))!=0)
				{
					*state  = PCA9673_PIN_SET;
				}
				else
				{
					*state  = PCA9673_PIN_RESET;
				}
			}
			else
			{
			}
		}
		else
		{
			status = PCA9673_E_COM_FAIL;
		}
    }
    return status;
}

/**
 * @brief This API is used to write the state of the gpio pin
 */
int8_t pca9673_writepin(pca9673_dev *hdev,pca9673_gpio_pin pin, pca9673_gpio_pinstate state)
{
    int8_t status;
    uint8_t data[2] = {0x3f,0xf8};
    status = is_gpio_valid(pin);
    if (PCA9673_OK != status)
    {
        status = PCA9673_E_OUT_OF_RANGE;
    }
    else
    {
    	//status = pca9673_read(hdev,data, 2);
		//if (PCA9673_OK == status)
		//{
			if(pin < 8)
			{
				data[0] = bit_write(data[0], pin, state);
				status = pca9673_write(hdev,data, 2);
			}
			else if(pin >= 8)
			{
				//pin = pin%10;
				//pin = pin%8;
				data[1] = bit_write(data[1], pin, state);
				status = pca9673_write(hdev,data, 2);


			}
			else
			{
			}
		//}
		//else
		//{
			//status = PCA9673_E_COM_FAIL;
		//}
    }
    return status;
}

/**
 *
 * @brief This API is used toggle gpio pin
 */
int8_t pca9673_togglepin(pca9673_dev *hdev, pca9673_gpio_pin pin)
{
    uint8_t data[2];
    int8_t status;
    status = is_gpio_valid(pin);
    if (PCA9673_OK != status)
    {
        status = PCA9673_E_OUT_OF_RANGE;
    }
    else
    {
        status = pca9673_read(hdev, data, 2);
		if(pin & 0xFF00)
		{
			data[1] = (data[1] | (1 << (pin%10)));
			status = pca9673_write(hdev,data, 2);
		}
		else if(pin & 0x00FF)
		{
			  data[0] = (data[0] | (1 << pin));
			  status = pca9673_write(hdev,data, 2);
		}
		else
		{
		}

    }
    return status;
}
