/***************************************************************************//**
  @addtogroup lip_rand
  @{
  @file lip_rand.c
  @brief Файл содержит реализацию модуля @ref lip_rand

  @author Borisov Alexey <borisov@lcard.ru>
  @date   05.06.2011
 ******************************************************************************/

#include "lip_rand_cfg_defs.h"
#if LIP_RAND_ENABLE
#include "lip_rand.h"
#include "ltimer.h"
#include "lip/lip_private.h"


static uint32_t f_rand;

#define LIP_RAND_A      69069
#define LIP_RAND_C          5
#define LIP_RAND_SHIFT     15
#define LIP_RAND_MASK      0xFFFF

#define LIP_RAND_DRANGE_MAX   0xFFFFFFFF

#define LIP_RAND_NEXT(val) ((val)*LIP_RAND_A + LIP_RAND_C)
#define LIP_RAND_RESULT(val) (((val) >> LIP_RAND_SHIFT) & LIP_RAND_MASK)

/*************************************************************************
 * Инициализация стандартного генератора случайных чисел для стека lip
 * Начальное значение генерируется на основе mac-адреса и текущего времени
 */
void lip_rand_init(void) {
    const uint8_t *mac = lip_eth_cur_mac();

    f_rand = lclock_get_ticks();
    f_rand += ((uint32_t)mac[0] << 24) +
            ((uint32_t)mac[1] << 16) +
            ((uint32_t)mac[2] <<  8) +
            ((uint32_t)mac[3]);
    f_rand +=  ((uint32_t)mac[4] <<  8) +
            ((uint32_t)mac[5]);
}

/*******************************************************************************
 *  Получение следующего случайного числа для стандартной последовательности
 *  (в стандартном диапазоне [0, LIP_RAND_MAX])
 *******************************************************************************/
t_lip_rand lip_rand(void) {
   f_rand = LIP_RAND_NEXT(f_rand);
   return LIP_RAND_RESULT(f_rand);
}

t_lip_rand lip_rand_next(t_lip_rand val) {
    val = LIP_RAND_NEXT(val);
    return LIP_RAND_RESULT(val);
}


/*******************************************************************************
 *  Получение следующего случайного числа для стандартной последовательности
 *  в заданном диапазоне [0,max]
 *******************************************************************************/
t_lip_rand lip_rand_range(t_lip_rand max) {
    t_lip_rand ret = 0;
    f_rand = LIP_RAND_NEXT(f_rand);
    ret = LIP_RAND_RESULT(f_rand);
    if (max <= LIP_RAND_MAX) {
        ret = ret * max / LIP_RAND_MAX;
#if LIP_RAND_UINT32_RANGE_SUPPORT
    } else {
        float dmax = (float)max/(float)LIP_RAND_DRANGE_MAX;
        f_rand = LIP_RAND_NEXT(f_rand);
        ret |= (uint32_t)(LIP_RAND_RESULT(f_rand)) << 16;
        ret = (t_lip_rand)((float)ret*dmax);
#endif
    }

    return ret;
}

#endif

/** @}*/
