/*
 * UPIWIN - Micro Pi Windowing Framework Kernel
 * Copyright (C) 2019 Amy Bowersox/Erbosoft Metaverse Design Solutions
 *    
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *    
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *-------------------------------------------------------------------------
 */
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include "wintype.h"
#include "time_func.h"
#include "msg_queue.h"

PMSG_QUEUE Mq_alloc(UINT32 nentries)
{
  int sz = sizeof(MSG_QUEUE) + (nentries * sizeof(MSG));
  PMSG_QUEUE rc;

  rc = (PMSG_QUEUE)malloc(sz);
  if (!rc)
    return NULL;
  memset(rc, 0, sz);
  rc->startbound = &(rc->messagestore[0]);
  rc->endbound = rc->startbound + nentries;
  rc->head = rc->tail = rc->startbound;
  rc->nentries = nentries;
  pthread_mutex_init(&(rc->mutex), NULL);
  return rc;
}

void Mq_destroy(PMSG_QUEUE queue)
{
  pthread_mutex_destroy(&(queue->mutex));
  free(queue);
}

static void post_internal(PMSG_QUEUE queue, PMSG msg)
{
  PMSG nexttail;
  
  pthread_mutex_lock(&(queue->mutex));
  nexttail = queue->tail + 1;
  if (nexttail == queue->endbound)
    nexttail = queue->startbound;
  if (nexttail != queue->head)
  {
    memcpy(queue->tail, msg, sizeof(MSG));
    queue->tail = nexttail;
  }
  /* else drop the message silently */
  
  pthread_mutex_unlock(&(queue->mutex));
}

void Mq_post(PMSG_QUEUE queue, HANDLE target, UINT32 message, const UINT_PTR *attrs, int nattrs)
{
  MSG tmpmsg;

  memset(&tmpmsg, 0, sizeof(MSG));
  tmpmsg.target = target;
  tmpmsg.message = message;
  if (nattrs > MSG_ATTRCOUNT)
    nattrs = MSG_ATTRCOUNT;
  if (nattrs > 0)
    memcpy(&(tmpmsg.attrs), attrs, sizeof(UINT_PTR) * nattrs);
  tmpmsg.timestamp = Time_since_start();
  post_internal(queue, &tmpmsg);
}

void Mq_post2(PMSG_QUEUE queue, HANDLE target, UINT32 message, UINT_PTR attr1, UINT_PTR attr2)
{
  MSG tmpmsg;
  
  memset(&tmpmsg, 0, sizeof(MSG));
  tmpmsg.target = target;
  tmpmsg.message = message;
  tmpmsg.attrs[0] = attr1;
  tmpmsg.attrs[1] = attr2;
  tmpmsg.timestamp = Time_since_start();
  post_internal(queue, &tmpmsg);
}

BOOL Mq_peek(PMSG_QUEUE queue, PMSG msg, UINT32 flags)
{
  BOOL rc = FALSE;
  PMSG nexthead;
  
  pthread_mutex_lock(&(queue->mutex));
  if (queue->head != queue->tail)
  {
    nexthead = queue->head + 1;
    if (nexthead == queue->endbound)
      nexthead = queue->startbound;
    memcpy(msg, queue->head, sizeof(MSG));
    if (flags & PEEK_REMOVE)
      queue->head = nexthead;
    rc = TRUE;
  }
  pthread_mutex_unlock(&(queue->mutex));
  return rc;
}

BOOL Mq_is_empty(PMSG_QUEUE queue)
{
  BOOL rc;
  pthread_mutex_lock(&(queue->mutex));
  rc = (queue->head == queue->tail);
  pthread_mutex_unlock(&(queue->mutex));
  return rc;
}
