//
// anyRemote
// a wi-fi or bluetooth remote for your PC.
//
// Copyright (C) 2006-2016 Mikhail Fedotov <anyremote@mail.ru>
//
// 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 3 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 St, Fifth Floor, Boston, MA  02110-1301  USA
//

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#include "common.h"
#include "queue.h"

//
// Message queue related wrappers
//

#ifdef USE_GLIB

#include <glib.h>

GAsyncQueue* _queues[Q_MAX] = {NULL};

int queueNew(int id) 
{
     if (id >=0 && id < Q_MAX && _queues[id] == NULL) {
         _queues[id] = g_async_queue_new();
	     return RC_OK;
     }
     return RC_NOK;
}

void* queuePop(int id) 
{
    if (id >=0 && id < Q_MAX && _queues[id] != NULL) {
        return g_async_queue_try_pop(_queues[id]);
    }
    return NULL;
}

int queuePush(int id, void* data) 
{ 
    if (id >=0 && id < Q_MAX && _queues[id] != NULL) {
        g_async_queue_push(_queues[id], data);
	    return RC_OK;
    }
    return RC_NOK;
}


void queueRemove(int id, DestroyCallback func)
{
   if (id >=0 && id < Q_MAX && _queues[id] != NULL) {
   
       while (1) {   // force to empty queue
           void* data = queuePop(id);
	       if (data) {
	           func(data);
	       } else {
	           break;
	       } 
       }
   
       g_async_queue_unref(_queues[id]);
       _queues[id] = NULL;
   }
}

int queueCanPop(int id)
{
    if (id >=0 && id < Q_MAX && _queues[id] != NULL) {
        return (g_async_queue_length(_queues[id]) > 0 ? RC_OK : RC_NOK);
    }
    return RC_NOK;
}

#else

#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#include <mutex.h>

typedef struct {
  DoubleList *head;
  DoubleList *tail;
  uint  length;
} Queue;

Queue* _queues[Q_MAX] = {NULL}; 

int queueNew(int id) 
{
    if (mutexExists(M_QUEUE) != RC_OK) {
         mutexNew(M_QUEUE);
    }
    
    if (id >=0 && id < Q_MAX && _queues[id] == NULL) {
    
         mutexLock(M_QUEUE);

         _queues[id] = malloc(sizeof(Queue));
         _queues[id]->head = _queues[id]->tail = NULL;
         _queues[id]->length = 0;
	 
	     mutexUnlock(M_QUEUE);
	 
	     return RC_OK;
     }
     return RC_NOK;
}

void* queuePop(int id) 
{
    void* data = NULL;
    
    if (id >=0 && id < Q_MAX && _queues[id] != NULL) {
	
	    mutexLock(M_QUEUE);
	    if (_queues[id]->tail) {

	        DoubleList *node = _queues[id]->tail;
	        data = node->data;

	        _queues[id]->tail = node->prev;
	        if (_queues[id]->tail) {
	            _queues[id]->tail->next = NULL;
	        } else {
	            _queues[id]->head = NULL;
	        }

	        _queues[id]->length--;

	        listDoubleFreeNode(node);
	    }
        mutexUnlock(M_QUEUE);
    }

    return data;
}

int queuePush(int id, void* data) 
{ 
    if (id >=0 && id < Q_MAX && _queues[id] != NULL) {
	
	    mutexLock(M_QUEUE);

	    _queues[id]->head = listDoublePrepend(_queues[id]->head, data);
	    if (!_queues[id]->tail) {
	        _queues[id]->tail = _queues[id]->head;
	    }
	    _queues[id]->length++;

	    mutexUnlock(M_QUEUE);

	    return RC_OK;
    }
    return RC_NOK;
}

void queueRemove(int id, DestroyCallback func)
{
    if (id >=0 && id < Q_MAX && _queues[id] != NULL) {
      
       mutexLock(M_QUEUE);
       
       listDoubleFullFree(_queues[id]->head, func);
       
       free(_queues[id]);
       _queues[id] = NULL;
       
       mutexUnlock(M_QUEUE);
    }
}

int queueCanPop(int id)
{
    if (id >=0 && id < Q_MAX && _queues[id] != NULL) {
        
	    mutexLock(M_QUEUE);
	    int size = _queues[id]->length;
	    mutexUnlock(M_QUEUE);
	
        return (size > 0 ? RC_OK : RC_NOK);
    }
    return RC_NOK;
}

#endif

int queueExists(int id)
{
    return (id >=0 && id < Q_MAX && _queues[id] != NULL ? RC_OK : RC_NOK);
}
