/*
 *    Copyright (C) 2013 SAMSUNG S.LSI
 *
 *   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.
 *
 *
 */

/************************************************************************
** OS interface for task handling
*************************************************************************/
#include <pthread.h>
#include <sched.h>
#include <string.h>
#include "osi.h"

/************************************************************************
** Internal function prototype
*************************************************************************/

/************************************************************************
** Public functions
*************************************************************************/
tOSI_MEM_HANDLER OSI_mem_get(size_t size) {
  tOSI_MEM_HANDLER free_mem = NULL;
  int index, err_cnt = 3;

  if (size > OSI_MEM_POOL_SIZE) {
    OSI_loge("%s : memory getting failed. Max size=%d, Requested size=%d",
             __func__, OSI_MEM_POOL_SIZE, (int)size);
    return NULL;
  }

/* Try 3 times to get memory */
retry_getting:

  osi_lock();
  for (index = 0; index < osi_info.mem_max_cnt; index++) {
#ifdef OSI_USE_DYNAMIC_BUF
    if (osi_info.mem[index]->state == OSI_FREE)
      free_mem = (tOSI_MEM_HANDLER)osi_info.mem[index];
#else
    if (osi_info.mem[index].state == OSI_FREE)
      free_mem = (tOSI_MEM_HANDLER)&osi_info.mem[index];
#endif
  }

  if (free_mem == NULL) {
    /* Not found free memory handler */
    OSI_loge("%s : Failed to find free memory pool(max: %d)", __func__,
             osi_info.mem_max_cnt);
#ifdef OSI_USE_DYNAMIC_BUF
    /* get a new buffer */
    free_mem = osi_info.mem[index] =
        (tOSI_MEM_HANDLER)malloc(OSI_MEM_POOL_SIZE);
    if (osi_info.mem[index] != NULL) {
      osi_info.mem[index]->state = OSI_FREE;
      osi_info.mem_max_cnt++;
      OSI_loge("%s : get a new buffer (max: %d)", __func__,
               osi_info.mem_max_cnt);
    } else
#endif
        if (--err_cnt > 0) {
      OSI_loge("%s : try %d time(s) more!", __func__, err_cnt + 1);
      osi_unlock();
      sched_yield();
      OSI_delay(20);
      goto retry_getting;
    }
  } else {
    free_mem->state = OSI_ALLOCATED;
    memset(free_mem->buffer, 0, OSI_MEM_POOL_SIZE);
  }
  osi_unlock();

  return free_mem;
}

void OSI_mem_free(tOSI_MEM_HANDLER target) {
  if (!target) return;

  osi_lock();
  target->state = OSI_FREE;
  osi_unlock();
}

tOSI_QUEUE_HANDLER OSI_queue_allocate(const char* que_name) {
  tOSI_QUEUE_HANDLER free_que = NULL;
  int index;

  osi_lock();
  for (index = 0; index < osi_info.queue_max_cnt; index++) {
    if (osi_info.queue[index].state == OSI_FREE) {
      if (free_que == NULL)
        free_que = (tOSI_QUEUE_HANDLER)&osi_info.queue[index];
    } else {
      if (osi_info.queue[index].name == NULL) continue;

      if (strcmp((char const*)osi_info.queue[index].name,
                 (char const*)que_name) == 0) {
        OSI_loge("%s : %s queue is already allocated [%d]", __func__, que_name,
                 index);
        free_que = NULL;
        break;
      }
    }
  }

  if (free_que == NULL) {
    OSI_loge("%s : Failed to find free queue(max: %d)", __func__,
             OSI_MAX_QUEUE);
  } else {
    memset(free_que->queue, 0, OSI_QUEUE_SIZE);
    free_que->name = que_name;
    free_que->state = OSI_ALLOCATED;
    free_que->head = 0;
    free_que->tail = OSI_QUEUE_SIZE;
  }
  osi_unlock();

  return free_que;
}

int OSI_queue_put(tOSI_QUEUE_HANDLER queue, void* p_data) {
  int ret;

  osi_lock();

  if (!queue || queue->state != OSI_ALLOCATED) {
    OSI_loge("%s : queue is not allocated", __func__);
    return -1;
  }

  if (queue->head == queue->tail) {
    OSI_loge("%s : queue is overflower (max: %d)", __func__, OSI_QUEUE_SIZE);
  } else {
    queue->queue[queue->head++] = p_data;
    if (queue->head >= OSI_QUEUE_SIZE) queue->head = 0;

    // pthread_cond_broadcast(&queue->cond);
    pthread_cond_signal(&queue->cond);
  }

  ret = (queue->head) - (queue->tail);

  osi_unlock();

  if (ret < 0) ret += OSI_QUEUE_SIZE;

  return ret;
}

void* queue_get(tOSI_QUEUE_HANDLER queue) {
  void* data = NULL;

  if (!queue || queue->state != OSI_ALLOCATED) {
    OSI_loge("%s : queue is not allocated", __func__);
    return NULL;
  }

  if (queue->tail + 1 >= OSI_QUEUE_SIZE) {
    if (queue->head == 0) {
      // empty
      // OSI_loge("%s : queue is empty", __func__);
      return NULL;
    }
  } else {
    if (queue->tail + 1 == queue->head) {
      // empty
      // OSI_loge("%s : queue is empty", __func__);
      return NULL;
    }
  }

  queue->tail++;
  if (queue->tail >= OSI_QUEUE_SIZE) queue->tail = 0;
  data = queue->queue[queue->tail];

  return data;
}

void* OSI_queue_get(tOSI_QUEUE_HANDLER queue) {
  void* data = NULL;

  osi_lock();
  data = queue_get(queue);
  osi_unlock();

  return data;
}

void* OSI_queue_get_wait(tOSI_QUEUE_HANDLER queue) {
  void* ret;

  osi_lock();

  if (!queue || queue->state != OSI_ALLOCATED) {
    OSI_loge("%s : queue is not allocated", __func__);
    return NULL;
  }

  ret = queue_get(queue);
  if (ret == NULL) {
    pthread_cond_wait(&queue->cond, &osi_info.mutex);
    ret = queue_get(queue);
  }

  osi_unlock();

  return ret;
}

void OSI_queue_free(tOSI_QUEUE_HANDLER target) {
  if (target) {
    target->name = NULL;
    target->state = OSI_FREE;
  }
}

tOSI_QUEUE_HANDLER OSI_queue_get_handler(const char* name) {
  tOSI_QUEUE_HANDLER queue = NULL;
  int index;

  if (name == NULL) return NULL;

  osi_lock();
  for (index = 0; index < OSI_MAX_QUEUE; index++) {
    if (osi_info.queue[index].name == NULL) continue;

    if (strcmp((char const*)osi_info.queue[index].name, (char const*)name) ==
        0) {
      queue = (tOSI_QUEUE_HANDLER)&osi_info.queue[index];
      break;
    }
  }
  osi_unlock();

  return queue;
}
