#include <stdint.h>
#include <stdbool.h>


#include "dongle_p24c02.h"


void IIC_Init(void)
{
    IIC_SCL_INIT;
	IIC_SDA_INIT;
	
	IIC_SCL_OUT;
	IIC_SDA_OUT;
}

void IIC_Start(void)
{
    IIC_SDA_HIGH;
    IIC_SCL_HIGH;
    IIC_DELAY();
    
    IIC_SDA_LOW;
    IIC_DELAY();
    
    IIC_SCL_LOW;
    IIC_DELAY();
}

void IIC_Stop(void)
{
    IIC_SDA_LOW;
    IIC_DELAY();
    
    IIC_SCL_HIGH;
    IIC_DELAY();
    
    IIC_SDA_HIGH;
    IIC_DELAY();
}

void IIC_Ack(void)
{
    IIC_SDA_LOW;
    IIC_DELAY();
    
    IIC_SCL_HIGH;
    IIC_DELAY();
    
    IIC_SCL_LOW;
    IIC_DELAY();
}

void IIC_NAck(void)
{
    IIC_SDA_HIGH;
    IIC_DELAY();
    
    IIC_SCL_HIGH;
    IIC_DELAY();
    
    IIC_SCL_LOW;
    IIC_DELAY();
}

bool IIC_WaitAck(void)
{
    bool ack;
    
    IIC_SDA_IN;
    IIC_SDA_HIGH;
    IIC_DELAY();
    
    IIC_SCL_HIGH;
    IIC_DELAY();
    
    if (IIC_SDA_GET) {
        ack = false;
    } else {
        ack = true;
    }
    
    IIC_SCL_LOW;
    IIC_DELAY();
    
    IIC_SDA_OUT;
    
    return ack;
}

bool IIC_SendByte(uint8_t data)
{
    for (uint8_t i = 0; i < 8; i++) {
        if (data & 0x80) {
            IIC_SDA_HIGH;
        } else {
            IIC_SDA_LOW;
        }
        IIC_DELAY();
        
        IIC_SCL_HIGH;
        IIC_DELAY();
        IIC_SCL_LOW;
        IIC_DELAY();
        
        data <<= 1;
    }
    
    return IIC_WaitAck();
}

uint8_t IIC_RecvByte(void)
{
    uint8_t data = 0;
    
    IIC_SDA_IN;
    IIC_SDA_HIGH;
    
    for (uint8_t i = 0; i < 8; i++) {
        data <<= 1;
        
        IIC_SCL_HIGH;
        IIC_DELAY();
        
        if (IIC_SDA_GET) {
            data |= 0x01;
        }
        
        IIC_SCL_LOW;  
        IIC_DELAY();
    }
    
    IIC_SDA_OUT;
    
    return data;
}

bool P24C02_WriteByte(uint8_t addr, uint8_t data)
{
    IIC_Start();
    
    if (!IIC_SendByte(P24C02_ADDRESS)) {
        IIC_Stop();
        return false;
    }
    
    if (!IIC_SendByte(addr)) {
        IIC_Stop();
        return false;
    }
    
    if (!IIC_SendByte(data)) {
        IIC_Stop();
        return false;
    }
    
    IIC_Stop();
    
    SYS_delay_10nop(15000);
    
    return true;
}

bool P24C02_ReadByte(uint8_t addr, uint8_t *data)
{
    if (data == NULL) return false;
    
    IIC_Start();
    
    if (!IIC_SendByte(P24C02_ADDRESS)) {
        IIC_Stop();
        return false;
    }
    
    if (!IIC_SendByte(addr)) {
        IIC_Stop();
        return false;
    }
    
    IIC_Start();
    
    if (!IIC_SendByte(P24C02_ADDRESS | 0x01)) {
        IIC_Stop();
        return false;
    }
    
    *data = IIC_RecvByte();
    
    IIC_NAck();
    IIC_Stop();
    
    return true;
}

bool P24C02_WritePage(uint8_t start_addr, const uint8_t *data, uint8_t len)
{
    if (data == NULL || len == 0 || len > 16) return false;
    
    IIC_Start();
    
    if (!IIC_SendByte(P24C02_ADDRESS)) {
        IIC_Stop();
        return false;
    }
    
    if (!IIC_SendByte(start_addr)) {
        IIC_Stop();
        return false;
    }
    
    for (uint8_t i = 0; i < len; i++) {
        if (!IIC_SendByte(data[i])) {
            IIC_Stop();
            return false;
        }
    }
    
    IIC_Stop();
    
    SYS_delay_10nop(15000);
    
    return true;
}

bool P24C02_ReadSequential(uint8_t start_addr, uint8_t *data, uint8_t len)
{
    if (data == NULL || len == 0) return false;
    
    IIC_Start();
    
    if (!IIC_SendByte(P24C02_ADDRESS)) {
        IIC_Stop();
        return false;
    }
    
    if (!IIC_SendByte(start_addr)) {
        IIC_Stop();
        return false;
    }
    
    IIC_Start();
    
    if (!IIC_SendByte(P24C02_ADDRESS | 0x01)) {
        IIC_Stop();
        return false;
    }
    
    for (uint8_t i = 0; i < len; i++) {
        data[i] = IIC_RecvByte();
        
        if (i == len - 1) {
            IIC_NAck();
        } else {
            IIC_Ack();
        }
    }
    
    IIC_Stop();
    
    return true;
}

