#include <stdlib.h>
 
#include "mib-init.h"
#include "ber.h"
#include "utils.h"
#include "logging.h"
#include "radio.h"
#include "rf230bb.h"
 
#if CONTIKI_TARGET_AVR_RAVEN && ENABLE_PROGMEM
#include <avr/pgmspace.h>
#else
#define PROGMEM
#endif
 
/* common oid prefixes*/
static u8t ber_oid_system_desc[] PROGMEM  = {0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x01, 0x00};
static ptr_t oid_system_desc PROGMEM      = {ber_oid_system_desc, 8};
static u8t ber_oid_system_time[] PROGMEM  = {0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x03, 0x00};
static ptr_t oid_system_time PROGMEM      = {ber_oid_system_time, 8};
static u8t ber_oid_system_str[] PROGMEM   = {0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x0B, 0x00};
static ptr_t oid_system_str PROGMEM       = {ber_oid_system_str, 8};
static u8t ber_oid_system_tick[] PROGMEM  = {0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x0D, 0x00};
static ptr_t oid_system_tick PROGMEM      = {ber_oid_system_tick, 8};
 
static u8t ber_oid_if_number[] PROGMEM    = {0x2b, 0x06, 0x01, 0x02, 0x01, 0x02, 0x01, 0x00};
static ptr_t oid_if_number PROGMEM        = {ber_oid_if_number, 8};
 
static u8t ber_oid_if_table[] PROGMEM     = {0x2b, 0x06, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01};
static ptr_t oid_if_table PROGMEM         = {ber_oid_if_table, 8};
 
 
static u8t ber_oid_test_uint[] PROGMEM    = {0x2b, 0x06, 0x01, 0x02, 0x01, 0x89, 0x52, 0x02, 0x00};
static ptr_t oid_test_uint PROGMEM        = {ber_oid_test_uint, 9};
 
/* Beuth Steckdose*/
static u8t ber_oid_steckdose_int[] PROGMEM     = {0x2b, 0x06, 0x01, 0x04, 0x01, 0x81, 0xac, 0x5d, 0x64, 0x01};
static ptr_t oid_steckdose_int PROGMEM         = {ber_oid_steckdose_int, 10};
 
/*RSSI Value*/
static u8t ber_oid_rssi_int[] PROGMEM     = {0x2b, 0x06, 0x01, 0x04, 0x01, 0x81, 0xac, 0x5d, 0x65, 0x01};
static ptr_t oid_rssi_int PROGMEM         = {ber_oid_rssi_int, 10};
 
/*Temperaturerfassung*/
static u8t ber_oid_temp_int[] PROGMEM     = {0x2b, 0x06, 0x01, 0x04, 0x01, 0x81, 0xac, 0x5d, 0x66, 0x01};
static ptr_t oid_temp_int PROGMEM         = {ber_oid_temp_int, 10};
 
/* Beuth Steckdose*/
/* 110620 fschw: Steckdose for snmpd */
//static u8t ber_oid_system_Steckdose [] PROGMEM          = {0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x0a, 0x00};
//static ptr_t oid_system_Steckdose PROGMEM               = {ber_oid_system_Steckdose, 8};
//static u8t ber_oid_system_SteckdoseOID []               = {0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x0a, 0x00};
//static ptr_t oid_system_SteckdoseOID                    = {ber_oid_system_SteckdoseOID, 8};
 
 
s8t getSysDescr(mib_object_t* object, u8t* oid, u8t len)
{
    if (!object->varbind.value.p_value.len) {
        object->varbind.value.p_value.ptr = (u8t*)"Beuth MIB";
        object->varbind.value.p_value.len = 9;
    }
    return 0;
}
 
s8t setSysDescr(mib_object_t* object, u8t* oid, u8t len, varbind_value_t value)
{
    object->varbind.value.p_value.ptr = (u8t*)"Beuth MIB changed";
    object->varbind.value.p_value.len = 17;
    return 0;
}
 
s8t getTimeTicks(mib_object_t* object, u8t* oid, u8t len)
{
    object->varbind.value.u_value = 1234;
    return 0;
}
 
/* 110620 fschw: add Steckdose for snmpd - start */
s8t getBeuthState(mib_object_t* object, u8t* oid, u8t len)
{
    object->varbind.value.i_value = ((PORTD >> PIN7) & 1);
	printf("Get Pin State ausgeführt, Ergebnis %d\n",object->varbind.value.i_value);
    return 0;
}
 
s8t setBeuthState(mib_object_t* object, u8t* oid, u8t len, varbind_value_t value)
{
	DDRD |= (1 << PIN7);
    if (value.i_value == 1) {
        PORTD |= (1 << PIN7);
		printf("Set Pin ausgeführt (if==1-Zweig), Ergebnis %d\n",value.i_value);
    } else {
        PORTD &= ~(1 << PIN7);
		printf("Set Pin ausgeführt (else Zweig), Ergebnis %d\n",value.i_value);
    }
    object->varbind.value.i_value = (PORTD & (1 << PIN7));
	printf("Set Pin: Pin7 hat nun den Zustand:%d\n",object->varbind.value.i_value);
	return 0;
}
/* 110620 fschw end */
 
/* RSSI value*/
s8t getRssiValue(mib_object_t* object, u8t* oid, u8t len)
{
	s16t rssi_temp;
	rssi_temp=rf230_get_raw_rssi();
    object->varbind.value.i_value = (-91)+(rssi_temp); //Already multiplicated with three
	printf("Get RSSI Value ausgeführt, Raw_RSSI Ergebnis %d\n",rssi_temp);
	printf("Get RSSI Value ausgeführt, iValue Ergebnis %d\n",object->varbind.value.i_value);
    return 0;
}
 
/* RSSI value end */
 
/* temperature value*/
/************************/
 
void adc_init()
{
    // AREF = AVcc
    ADMUX = (1<<REFS0);
 
    // ADC Enable and prescaler of 128
    // 16000000/128 = 125000
    ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
}
 
u16t adc_read(u8t ch)
{
    // select the corresponding channel 0~7
    // ANDing with '7' will always keep the value
    // of 'ch' between 0 and 7
    ch &= 0b00000111;  // AND operation with 7
    ADMUX = (ADMUX & 0xF8)|ch;     // clears the bottom 3 bits before ORing
 
    // start single convertion
    // write '1' to ADSC
    ADCSRA |= (1<<ADSC);
 
    // wait for conversion to complete
    // ADSC becomes '0' again
    // till then, run loop continuously
    while(ADCSRA & (1<<ADSC));
 
    return (ADC);
}
//int temp_array[]={325, 253, 198, 157, 125, 100, 80, 65, 53, 43, 36, 29, 25, 21, 17, 15, 12, 11, 9, 7, 6};
//Werte laut Datenblatt in 5grad Schritten
u16t temp_array[] PROGMEM ={325, 311, 296, 282, 267, 
                           253, 242, 231, 220, 209, 
				           198, 189, 182, 173, 165, 
				           157, 151, 144, 138, 131, 
				           125, 120, 115, 110, 105, 
				           100, 96, 92, 88, 84,
				           80, 77, 74, 71, 68, 
				           65, 63, 60, 58, 55,
				           53, 51, 49, 47, 45,
				           43, 42, 40, 39, 37,
				           36, 35, 33, 32, 30,
				           29, 28, 27, 27, 26,
				           25, 24, 23, 23, 22,
				           21, 20, 19, 19, 18,
				           17, 17, 16, 16, 15,
				           15, 14, 14, 13, 13,
				           12, 12, 12, 11, 11,
				           11, 11, 10, 10, 9,
				           9, 9, 8, 8, 7,
				           7, 7, 7, 6, 6,
				           6};
//Werte laut Datenblatt in 5grad Schritten Zwischenwerte gemittelt Beginn bei 0°
 
u16t getTempArray(u8t index) {
    return pgm_read_dword(&temp_array[index]);
}
 
float adcToOhm(u16t adc_value)
{
	float one_adc=0.00318359375;
	float r_div=10000;
	float u_ges=3.26;
	float r_ntc;
	r_ntc=(((float)adc_value)*one_adc*r_div)/(u_ges-(((float)adc_value)*one_adc));
	printf("RNTC=%f\n",r_ntc);
	return r_ntc;
}
 
u8t find_temp_celsius(float r_ntc)
{	
	float rt_r25;
	float r_div=10000;
	rt_r25=(r_ntc/r_div)*100;
	u8t i=0;
	while((u16t)rt_r25 <= getTempArray(i))
	{
		i++;
	}
	return (i-1);
}
 
s8t getTempValue(mib_object_t* object, u8t* oid, u8t len)
{
	adc_init();
	u16t adc_value;
	u8t adc_channel;
	float r_ntc;
	adc_channel=3;
	adc_value=adc_read(adc_channel);
	r_ntc=adcToOhm(adc_value);
    object->varbind.value.i_value = find_temp_celsius(r_ntc);
	printf("Get temperature Value ausgeführt, ADC Value int %d RNTC=%f Ohm\n",adc_value,r_ntc);
	printf("Get temperature Value ausgeführt, iValue Ergebnis in Grad Celsius: %d\n",object->varbind.value.i_value);
    return 0;
}
 
/* temperature value end */
 
/**** IF-MIB ****************/
 
#define ifNumber 3
 
s8t getIfNumber(mib_object_t* object, u8t* oid, u8t len)
{
    object->varbind.value.i_value = ifNumber;
    return 0;
}
 
#define ifIndex 1
 
s8t getIf(mib_object_t* object, u8t* oid, u8t len)
{
    u32t oid_el1, oid_el2;
    u8t i;
    i = ber_decode_oid_item(oid, len, &oid_el1);
    i = ber_decode_oid_item(oid + i, len - i, &oid_el2);
 
    if (len != 2) {
        return -1;
    }
    switch (oid_el1) {
        case ifIndex:
            object->varbind.value_type = BER_TYPE_INTEGER;
            if (0 < oid_el2 && oid_el2 <= ifNumber) {
                object->varbind.value.i_value = oid_el2;
            } else {
                return -1;
            }
            break;
        default:
            break;
    }
    return 0;
}
 
ptr_t* getNextIfOid(mib_object_t* object, u8t* oid, u8t len)
{
    u32t oid_el1, oid_el2;
    u8t i;
    i = ber_decode_oid_item(oid, len, &oid_el1);
    i = ber_decode_oid_item(oid + i, len - i, &oid_el2);
 
    if (oid_el1 < ifIndex || (oid_el1 == ifIndex && oid_el2 < ifNumber)) {
        ptr_t* ret = oid_create();
        CHECK_PTR_U(ret);
        ret->len = 2;
        ret->ptr = malloc(2);
        CHECK_PTR_U(ret->ptr);
        ret->ptr[0] = ifIndex;
        if (oid_el1 < ifIndex) {
            ret->ptr[1] = 1;
        } else {
            ret->ptr[1] = oid_el2 + 1;
        }
        return ret;
    }
    return 0;
}
 
/*-----------------------------------------------------------------------------------*/
/*
 * Initialize the MIB.
 */
s8t mib_init()
{
    const u32t tconst = 12345678;
    if (add_scalar(&oid_system_desc, 0, BER_TYPE_OCTET_STRING, 0, &getSysDescr, &setSysDescr) == -1 ||
        add_scalar(&oid_system_time, 0, BER_TYPE_TIME_TICKS, 0, &getTimeTicks, 0) == -1  ||
        add_scalar(&oid_system_str, 0, BER_TYPE_OCTET_STRING, "Pointer to a string", 0, 0) == -1 ||
        add_scalar(&oid_system_tick, 0, BER_TYPE_TIME_TICKS, &tconst, 0, 0) == -1) 
		{
        return -1;
    }
    if (add_scalar(&oid_if_number, 0, BER_TYPE_INTEGER, 0, &getIfNumber, 0) == -1) {
        return -1;
    }
 
    if (add_table(&oid_if_table, &getIf, &getNextIfOid, 0) == -1) {
        return -1;
    }
 
    if (add_scalar(&oid_test_uint, 0, BER_TYPE_GAUGE, 0, 0, 0) == -1) {
        return -1;
    }
	if (add_scalar(&oid_steckdose_int, 0, BER_TYPE_INTEGER, 0, &getBeuthState, &setBeuthState) == -1) 
	{
        return -1;
    }
		if (add_scalar(&oid_rssi_int, 0, BER_TYPE_INTEGER, 0, &getRssiValue, 0) == -1) 
	{
        return -1;
    }
 
	if (add_scalar(&oid_temp_int, 0, BER_TYPE_INTEGER, 0, &getTempValue, 0) == -1) 
	{
        return -1;
    }
    return 0;
}