/*---------------------------------------------------------------------------*/
int cc1100_send_csmaca(radio_address_t address, protocol_t protocol, int priority, char *payload, radio_packet_length_t payload_len)
{
    uint16_t min_window_size;
    uint16_t max_window_size;
    uint16_t difs;
    uint16_t slottime;

    switch(priority) {
        case PRIORITY_ALARM:
            min_window_size = PRIO_ALARM_MIN_WINDOW_SIZE;
            max_window_size = PRIO_ALARM_MAX_WINDOW_SIZE;
            difs = PRIO_ALARM_DIFS;
            slottime = PRIO_ALARM_SLOTTIME;
            break;

        case PRIORITY_WARNING:
            min_window_size = PRIO_WARN_MIN_WINDOW_SIZE;
            max_window_size = PRIO_WARN_MAX_WINDOW_SIZE;
            difs = PRIO_WARN_DIFS;
            slottime = PRIO_WARN_SLOTTIME;
            break;

        default:
            min_window_size = PRIO_DATA_MIN_WINDOW_SIZE;
            max_window_size = PRIO_DATA_MAX_WINDOW_SIZE;
            difs = PRIO_DATA_DIFS;
            slottime = PRIO_DATA_SLOTTIME;
    }

    /* Calculate collisions per second */
    if (collision_state == COLLISION_STATE_INITIAL) {
        vtimer_now(&collision_measurement_start);
        collision_count = 0;
        collisions_per_sec = 0;
        collision_state = COLLISION_STATE_MEASURE;
    }
    else if (collision_state == COLLISION_STATE_MEASURE) {
        timex_t now;
        vtimer_now(&now);
        timex_t timespan = timex_sub(now, collision_measurement_start);

        if (timex_cmp(timespan, timex_set(1, 0)) > 0) {
            collisions_per_sec = (collision_count * 1000000) / (double) timex_uint64(timespan);

            if (collisions_per_sec > 0.5 && collisions_per_sec <= 2.2) {
                timex_t now;
                vtimer_now(&now);
                collision_measurement_start = now;
                collision_state = COLLISION_STATE_KEEP;
            }
            else if (collisions_per_sec > 2.2) {
                timex_t now;
                vtimer_now(&now);
                collision_measurement_start = now;
                collision_state = COLLISION_STATE_KEEP;
            }
            else {
                collision_state = COLLISION_STATE_INITIAL;
            }
        }
    }
    else if (collision_state == COLLISION_STATE_KEEP) {
        timex_t now;
        vtimer_now(&now);
        timex_t timespan = timex_sub(now, collision_measurement_start);

        if (timex_cmp(timespan, timex_set(5, 0)) > 0) {
            collision_state = COLLISION_STATE_INITIAL;
        }
    }

    /* Adjust initial window size according to collision rate */
    if (collisions_per_sec > 0.5 && collisions_per_sec <= 2.2) {
        min_window_size *= 2;
    }
    else if (collisions_per_sec > 2.2) {
        min_window_size *= 4;
    }

    uint16_t windowSize = min_window_size;      /* Start with window size of PRIO_XXX_MIN_WINDOW_SIZE */
    uint16_t backoff = 0;                       /* Backoff between 1 and windowSize */
    uint32_t total;                             /* Holds the total wait time before send try */
    uint32_t cs_timeout;                        /* Current carrier sense timeout value */

    if (protocol == 0) {
        return RADIO_INVALID_PARAM;             /* Not allowed, protocol id must be greater zero */
    }

    cc1100_phy_mutex_lock();                    /* Lock radio for exclusive access */

    /* Get carrier sense timeout based on overall error rate till now */
    send_csmaca_calls++;
    int fail_percentage = (send_csmaca_calls_cs_timeout * 100) / send_csmaca_calls;

    if (fail_percentage == 0) {
        fail_percentage = 1;
    }

    cs_timeout = CARRIER_SENSE_TIMEOUT / fail_percentage;

    if (cs_timeout < CARRIER_SENSE_TIMEOUT_MIN) {
        cs_timeout = CARRIER_SENSE_TIMEOUT_MIN;
    }

    cc1100_cs_init();                           /* Initialize carrier sensing */

window:

    if (backoff != 0) {
        goto cycle;    /* If backoff was 0 */
    }

    windowSize *= 2;                        /* ...double the current window size */

    if (windowSize > max_window_size) {
        windowSize = max_window_size;       /* This is the maximum size allowed */
    }

    backoff = rand() % windowSize;          /* ...and choose new backoff */

    backoff += (uint16_t) 1;
cycle:
    cs_timeout_flag = 0;                    /* Carrier sense timeout flag */
    cs_hwtimer_id = hwtimer_set(cs_timeout, /* Set hwtimer to set CS timeout flag */
                                cs_timeout_cb, NULL);

    while (cc1100_cs_read()) {          /* Wait until air is free */
        if (cs_timeout_flag) {
            send_csmaca_calls_cs_timeout++;
#ifndef CSMACA_MAC_AGGRESSIVE_MODE
            cc1100_phy_mutex_unlock();
            cc1100_go_after_tx();           /* Go from RX to default mode */
            return RADIO_CS_TIMEOUT;        /* Return immediately */
#endif
#ifdef CSMACA_MAC_AGGRESSIVE_MODE
            goto send;                      /* Send anyway */
#endif
        }
    }

    hwtimer_remove(cs_hwtimer_id);          /* Remove hwtimer */
    cc1100_cs_write_cca(1);                 /* Air is free now */
    cc1100_cs_set_enabled(true);

    if (cc1100_cs_read()) {
        goto window;    /* GDO0 triggers on rising edge, so */
    }

    /* test once after interrupt is enabled */
    if (backoff > 0) {
        backoff--;    /* Decrement backoff counter */
    }

    total = slottime;                       /* Calculate total wait time */
    total *= (uint32_t)backoff;             /* Slot vector set */
    total += difs;                          /* ...and standard DIFS wait time */
    cs_timeout_flag = 0;                    /* Carrier sense timeout flag */
    cs_hwtimer_id = hwtimer_set(total,      /* Set hwtimer to set CS timeout flag */
                                cs_timeout_cb, NULL);

    while (!cs_timeout_flag
          || !cc1100_cs_read_cca()) {   /* Wait until timeout is finished */
        if (cc1100_cs_read_cca() == 0) {    /* Is the air still free? */
            hwtimer_remove(cs_hwtimer_id);
            goto window;                    /* No. Go back to new wait period. */
        }
    }

    cc1100_cs_set_enabled(false);
#ifdef CSMACA_MAC_AGGRESSIVE_MODE
send:
#endif
    int res = cc1100_send(address, protocol, priority, payload, payload_len);

    if (res < 0) {
        collision_count++;
    }

    return res;
}
/*---------------------------------------------------------------------------*/
int cc1100_send_csmaca(radio_address_t address, protocol_t protocol, int priority, char *payload, int payload_len)
{
	uint16_t min_window_size;
	uint16_t max_window_size;
	uint16_t difs;
	uint16_t slottime;

	switch (priority)
	{
		case PRIORITY_ALARM:
			min_window_size = PRIO_ALARM_MIN_WINDOW_SIZE;
			max_window_size = PRIO_ALARM_MAX_WINDOW_SIZE;
			difs = PRIO_ALARM_DIFS;
			slottime = PRIO_ALARM_SLOTTIME;
			break;
		case PRIORITY_WARNING:
			min_window_size = PRIO_WARN_MIN_WINDOW_SIZE;
			max_window_size = PRIO_WARN_MAX_WINDOW_SIZE;
			difs = PRIO_WARN_DIFS;
			slottime = PRIO_WARN_SLOTTIME;
			break;
		default:
			min_window_size = PRIO_DATA_MIN_WINDOW_SIZE;
			max_window_size = PRIO_DATA_MAX_WINDOW_SIZE;
			difs = PRIO_DATA_DIFS;
			slottime = PRIO_DATA_SLOTTIME;
	}

	// Calculate collisions per second
	if (collision_state == COLLISION_STATE_INITIAL) {
		timex_t now = vtimer_now();
		collision_measurement_start =  now.microseconds;
		collision_count = 0;
		collisions_per_sec = 0;
		collision_state = COLLISION_STATE_MEASURE;
	} else if (collision_state == COLLISION_STATE_MEASURE) {
			timex_t now = vtimer_now();
			uint64_t timespan = now.microseconds - collision_measurement_start;
			if (timespan > 1000000) {
				collisions_per_sec = (collision_count * 1000000) / (double) timespan;
			if (collisions_per_sec > 0.5 && collisions_per_sec <= 2.2) {
				timex_t now = vtimer_now();
				collision_measurement_start = now.microseconds;
				collision_state = COLLISION_STATE_KEEP;
			} else if (collisions_per_sec > 2.2) {
				timex_t now = vtimer_now();
				collision_measurement_start = now.microseconds;
				collision_state = COLLISION_STATE_KEEP;
			} else {
				collision_state = COLLISION_STATE_INITIAL;
			}
		}
	} else if (collision_state == COLLISION_STATE_KEEP) {
		timex_t now = vtimer_now();
        uint64_t timespan = now.microseconds - collision_measurement_start;
        if (timespan > 5000000) {
			collision_state = COLLISION_STATE_INITIAL;
		}
	}

	// Adjust initial window size according to collision rate
	if (collisions_per_sec > 0.5 && collisions_per_sec <= 2.2) {
		min_window_size *= 2;
	} else if (collisions_per_sec > 2.2) {
		min_window_size *= 4;
	}

	uint16_t windowSize = min_window_size;		// Start with window size of PRIO_XXX_MIN_WINDOW_SIZE
	uint16_t backoff = 0;						// Backoff between 1 and windowSize
	uint32_t total;								// Holds the total wait time before send try
	uint32_t cs_timeout;						// Current carrier sense timeout value

	if (protocol == 0)
	{
		return RADIO_INVALID_PARAM;				// Not allowed, protocol id must be greater zero
	}

	cc1100_phy_mutex_lock();					// Lock radio for exclusive access

	// Get carrier sense timeout based on overall error rate till now
	send_csmaca_calls++;
	int fail_percentage = (send_csmaca_calls_cs_timeout * 100) / send_csmaca_calls;
	if (fail_percentage == 0) fail_percentage = 1;
	cs_timeout = CARRIER_SENSE_TIMEOUT / fail_percentage;
	if (cs_timeout < CARRIER_SENSE_TIMEOUT_MIN) cs_timeout = CARRIER_SENSE_TIMEOUT_MIN;

	cc1100_cs_init();							// Initialize carrier sensing

	window:
		if (backoff != 0) goto cycle;			// If backoff was 0
		windowSize *= 2;						// ...double the current window size
		if (windowSize > max_window_size)
		{
			windowSize = max_window_size;		// This is the maximum size allowed
		}
		backoff = rand() % windowSize;			// ...and choose new backoff
		if (backoff < 0) backoff *= -1;
		backoff += (uint16_t) 1;
	cycle:
		cs_timeout_flag = 0;					// Carrier sense timeout flag
		cs_hwtimer_id = hwtimer_set(cs_timeout,	// Set hwtimer to set CS timeout flag
				cs_timeout_cb, NULL);
		while (cc1100_cs_read())				// Wait until air is free
		{
			if (cs_timeout_flag)
			{
				send_csmaca_calls_cs_timeout++;
#ifndef CSMACA_MAC_AGGRESSIVE_MODE
				cc1100_phy_mutex_unlock();
				cc1100_go_after_tx();			// Go from RX to default mode
				return RADIO_CS_TIMEOUT;		// Return immediately
#endif
#ifdef CSMACA_MAC_AGGRESSIVE_MODE
				goto send;						// Send anyway
#endif
			}
		}
		hwtimer_remove(cs_hwtimer_id);			// Remove hwtimer
		cc1100_cs_write_cca(1);					// Air is free now
		cc1100_cs_set_enabled(true);
		if (cc1100_cs_read()) goto window;		// GDO0 triggers on rising edge, so
												// test once after interrupt is enabled
		if (backoff > 0) backoff--;				// Decrement backoff counter
		total = slottime;						// Calculate total wait time
		total *= (uint32_t)backoff;				// Slot vector set
		total += difs;							// ...and standard DIFS wait time
		cs_timeout_flag = 0;					// Carrier sense timeout flag
		cs_hwtimer_id = hwtimer_set(total,		// Set hwtimer to set CS timeout flag
				cs_timeout_cb, NULL);
		while (!cs_timeout_flag
				|| !cc1100_cs_read_cca())		// Wait until timeout is finished
		{
			if (cc1100_cs_read_cca() == 0)		// Is the air still free?
			{
				hwtimer_remove(cs_hwtimer_id);
				goto window;					// No. Go back to new wait period.
			}
		}
		cc1100_cs_set_enabled(false);
#ifdef CSMACA_MAC_AGGRESSIVE_MODE
	send:
#endif
		int res = cc1100_send(address, protocol, priority, payload, payload_len);
		if (res < 0) {
			collision_count++;
		}
		return res;
}