static char RCSId[] = "$Id$";
static char Copyright[] = "Copyright  Robert J. Amstadt, 1994";

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "prototypes.h"
#include "heap.h"
#include "segmem.h"

#ifdef HAVE_IPC
static key_t MemoryKeys[SHMSEG];	/* Keep track of keys were using */
static int   LinearInitialized = 0;
#endif


#ifdef HAVE_IPC
/**********************************************************************
 *					LinearFindSpace
 */
int 
LinearFindSpace(int n_segments)
{
    int i, n;
    
    if (!LinearInitialized)
    {
	memset(MemoryKeys, -1, sizeof(MemoryKeys));
	return 0;
    }

    for (i = 0, n = 0; i < SHMSEG, n != n_segments; i++)
    {
	if (MemoryKeys[i] < 0)
	    n++;
	else
	    n = 0;
    }
    
    if (n != n_segments)
	return -1;
    else
	return i - n;
}
#endif /* HAVE_IPC */

/**********************************************************************
 *					GlobalLinearLock
 *
 * OK, this is an evil beast.  We will do one of two things:
 *
 *	1. If the data item <= 64k, then just call GlobalLock().
 *	2. If the data item > 64k, then map memory.
 */
void *
GlobalLinearLock(unsigned int block)
{
    GDESC *g, *g_first;
    int loc_idx;
    unsigned long addr;
    int i;
    
    /******************************************************************
     * Get GDESC for this block.
     */
    g_first = GlobalGetGDesc(block);
    if (g_first == NULL)
	return 0;

    /******************************************************************
     * Is block less then 64k in length?
     */
    if (g_first->sequence != 1 || g_first->length == 1)
    {
	return (void *) GlobalLock(block);
    }

    /******************************************************************
     * If there is already a linear lock on this memory, then
     * just return a pointer to it.
     */
    if (g_first->linear_count)
    {
	g_first->linear_count++;
	return g_first->linear_addr;
    }
    
    /******************************************************************
     * No luck.  We need to do the linear mapping right now.
     */
#ifdef HAVE_IPC
    loc_idx = LinearFindSpace(g_first->length);
    if (loc_idx < 0)
	return NULL;
    
    addr = (unsigned long) SHM_RANGE_START + (0x10000 * loc_idx);
    g = g_first;
    for (i = loc_idx; 
	 i < loc_idx + g_first->length; 
	 i++, addr += 0x10000, g = g->next)
    {
	if ((MemoryKeys[i] = IPCCopySelector(g->handle >> 3, addr, 0)) < 0)
	    return NULL;
	g->linear_addr = (void *) addr;
	g->linear_count = 1;
    }
#endif /* HAVE_IPC */

    return g_first->linear_addr;
}

/**********************************************************************
 *					GlobalLinearUnlock
 *
 */
unsigned int
GlobalLinearUnlock(unsigned int block)
{
    GDESC *g, *g_first;
    int loc_idx;
    int i;
    
    /******************************************************************
     * Get GDESC for this block.
     */
    g_first = GlobalGetGDesc(block);
    if (g_first == NULL)
	return block;

    /******************************************************************
     * Is block less then 64k in length?
     */
    if (g_first->sequence != 1 || g_first->length == 1)
    {
	return GlobalUnlock(block);
    }

    /******************************************************************
     * Make sure we have a lock on this block.
     */
#ifdef HAVE_IPC
    if (g_first->linear_count > 1)
    {
	g_first->linear_count--;
    }
    else if (g_first->linear_count == 1)
    {
	g = g_first;
	loc_idx = (((unsigned int) g_first - (unsigned int) SHM_RANGE_START) 
		   / 0x10000);
	for (i = 0; i < g_first->length; i++, g = g->next)
	{
	    shmdt(g->linear_addr);
	    g->linear_addr = NULL;
	    MemoryKeys[i] = -1;
	}
	
	g_first->linear_count = 0;
	return 0;
    }
#endif /* HAVE_IPC */

    return 0;
}
/**********************************************************************/

void LinearTest()
{
#if 0
    unsigned int handle;
    int *seg_ptr;
    int *lin_ptr;
    int seg, i;
    int *p;

    handle = GlobalAlloc(0, 0x40000);
    seg_ptr = GlobalLock(handle);
    lin_ptr = GlobalLinearLock(handle);

    for (seg = 0; seg < 4; seg++)
    {
	p = (int *) ((char *) seg_ptr + (0x80000 * seg));
	for (i = 0; i < (0x10000 / sizeof(int)); i++, p++)
	    *p = (seg * (0x10000 / sizeof(int))) + i;
    }
    
    p = lin_ptr;
    for (i = 0; i < (0x40000 / sizeof(int)); i++, p++)
    {
	if (*p != i)
	    printf("lin_ptr[%x] = %x\n", i, *p);
    }
#endif
}
