/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "Licence").
 * You may not use this file except in compliance with the Licence.
 *
 * You can obtain a copy of the licence at RISC OS path @.^.LICENCE
 * or  http://www.riscosdev.com/lanman98/LICENCE.CDDL
 * See the Licence for the specific language governing permissions
 * and limitations under the Licence.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the Licence file. If applicable, add the
 * following below this CDDL HEADER, with the fields enclosed by
 * brackets "[]" replaced with your own identifying information:
 * Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
 
/*
 *   Copyright 1996 Warm Silence Software Ltd.  All rights reserved.
 *   Use is subject to license terms.
 */

/*
 *   PHBG 3/8/98: Initial version
 *   PHBG 11/8/98: Separation of debug messages, window per thread.
 */

#include <stdarg.h>
#include <string.h>
#include <stdio.h>
#include "error.h"
#include "memory.h"

#include "debug.h"

#define MIN(x, y) ((x) < (y) ? (x) : (y))

typedef struct string_list_s *string_list_t;

struct string_list_s
{
    string_list_t next;
    char buffer[128];
};

struct monitor_s
{
    int pnum;
    int dead;
    string_list_t first, last;
    int used_in_last;
    monitor_t next;
};

static int active = 0;
static int pnum = 1;
static monitor_t current_monitor = NULL;
static monitor_t list = NULL;

monitor_t Monitor(void)
{
    monitor_t mon, *p;

    mon = Malloc(sizeof(*mon));
    memset(mon, 0, sizeof(*mon));
    mon->pnum = pnum++;
    for(p = &list; *p; p = &(*p)->next)
        ;
    *p = mon;
    return mon;
}

int vmcrprintf(monitor_t mon, char *fmt, va_list args)
{
    static char buf[1024];
    char *p;
    int res, len, lump;

    res = vsprintf(buf, fmt, args);
    len = strlen(buf);
    buf[len++] = '\n';
    buf[len] = 0;
    if(mon->first == NULL)
    {
        mon->first = Malloc(sizeof(*mon->first));
        mon->first->next = NULL;
        mon->last = mon->first;
        mon->used_in_last = 0;
    }
    p = buf;
    while(len)
    {
        if(mon->used_in_last >= 127)
        {
            mon->last->next = Malloc(sizeof(*mon->first));
            mon->last->next->next = NULL;
            mon->last = mon->last->next;
            mon->used_in_last = 0;
        }
        lump = MIN(len, 127 - mon->used_in_last);
        memcpy(mon->last->buffer + mon->used_in_last, p, lump);
        p += lump;
        len -= lump;
        mon->used_in_last += lump;
        mon->last->buffer[mon->used_in_last] = 0;
    }
    return res;
}

int vmprintf(monitor_t mon, char *fmt, va_list args)
{
    static char buf[1024];
    char *p;
    int res, len, lump;

    res = vsprintf(buf, fmt, args);
    len = strlen(buf);
    if(len == 0) return res;
    if(mon->first == NULL)
    {
        mon->first = Malloc(sizeof(*mon->first));
        mon->first->next = NULL;
        mon->last = mon->first;
        mon->used_in_last = 0;
    }
    p = buf;
    while(len)
    {
        if(mon->used_in_last >= 127)
        {
            mon->last->next = Malloc(sizeof(*mon->first));
            mon->last->next->next = NULL;
            mon->last = mon->last->next;
            mon->used_in_last = 0;
        }
        lump = MIN(len, 127 - mon->used_in_last);
        memcpy(mon->last->buffer + mon->used_in_last, p, lump);
        p += lump;
        len -= lump;
        mon->used_in_last += lump;
        mon->last->buffer[mon->used_in_last] = 0;
    }
    return res;
}

int mprintf(monitor_t mon, char *fmt, ...)
{
    va_list args;
    int res;

    va_start(args, fmt);
    res = vmprintf(mon, fmt, args);
    va_end(args);
    return res;
}

void MonitorDestruct(monitor_t mon)
{
    monitor_t *p;

    if(mon)
    {
        p = &list;
        while(*p)
        {
            if(*p == mon)
            {
                mon->dead = 1;
                if(mon->first == NULL)
                {
                    *p = (*p)->next;
                    Free(mon);
                }
                return;
            }
            else
            {
                p = &(*p)->next;
            }
        }
    }
}

void MonitorChange(monitor_t mon)
{
    current_monitor = mon;
}

void DEBUG(char *fmt, ...)
{
    va_list args;

    if(active)
    {
        va_start(args, fmt);
        if(current_monitor)
        {
            vmcrprintf(current_monitor, fmt, args);
        }
        else
        {
            vprintf(fmt, args);
            printf("\n");
        }
        va_end(args);
    }
}

void DEBUG_(char *fmt, ...)
{
    va_list args;

    if(active)
    {
        va_start(args, fmt);
        if(current_monitor)
            vmprintf(current_monitor, fmt, args);
        else
            vprintf(fmt, args);
        va_end(args);
    }
}

void vDEBUG(char *fmt, va_list args)
{
    if(active)
    {
        if(current_monitor)
            vmprintf(current_monitor, fmt, args);
        else
            vprintf(fmt, args);
    }
}

int MonitorPoll(char *buf)
{
    monitor_t *p, mon;
    static monitor_t last_mon = NULL;
    int pnum;

    active = 1;
    p = &list;
    if(last_mon)
    {
        string_list_t s;

        mon = last_mon;
        s = mon->first;
        mon->first = s->next;
        last_mon = mon->first ? mon : NULL;
        strcpy(buf, s->buffer);
        // sprintf(buf, "%d\n", strlen(s->buffer));
        Free(s);
        return mon->pnum;
    }
    while(*p)
    {
        mon = *p;
        if(mon->first == NULL)
        {
            if(mon->dead)
            {
                buf[0] = 0;
                *p = (*p)->next;
                pnum = mon->pnum;
                Free(mon);
                return pnum;
            }
            else
            {
                p = &(*p)->next;
            }
        }
        else
        {
            string_list_t s;

            s = mon->first;
            mon->first = s->next;
            last_mon = mon->first ? mon : NULL;
            strcpy(buf, s->buffer);
            // sprintf(buf, "%d\n", strlen(s->buffer));
            Free(s);
            return mon->pnum;
        }
    }
    return -1;
}

