/*
(c) Copyright 1988-1994 Microsoft Corporation.

Microsoft Bars&Pipes Professional Source Code License

This License governs use of the accompanying Software.  
Microsoft hopes you find this Software useful.

You are licensed to do anything you want with the Software. 

In return, we simply require that you agree:

1.      Not to remove any copyright notices from the Software.

2.      That the Software comes "as is", with no warranties.  
        None whatsoever. This means no implied warranty of merchantability or 
        fitness for a particular purpose or any warranty of non-infringement.  
        Also, you must pass this disclaimer on whenever you distribute the Software.

3.      That we will not be liable for any of those types of damages known as indirect, 
        special, consequential, or incidental related to the Software or this License, 
        to the maximum extent the law permits. Also, you must pass this limitation of 
        liability on whenever you distribute the Software.

4.      That if you sue anyone over patents that you think may apply to the Software 
        for that person’s use of the Software, your license to the Software ends automatically.

5.      That the patent rights Microsoft is licensing only apply to the Software, 
        not to any derivatives you make.

6.      That your rights under the License end automatically if you breach this in any way.
*/

#include "/CompilerSpecific.h"
#include <exec/types.h>
#include <exec/memory.h>
#include <proto/exec.h>
#include <string.h>
#include <dos/dos.h>
#include "/v.h"
#include "all.h"
#include "notate.h"

extern struct Functions functions;

#define EVENTSPERBLOCK  250
#define MINEVENTS       1000

struct EventBlock {
    struct EventBlock *next;            /* Linked list of allocated blocks. */
    struct NotateEvent array[EVENTSPERBLOCK];   /* Array of events. */
    char empty;
};

static struct EventBlock *blocklist = 0;
static struct NotateEvent *eventlist = 0;
unsigned long eventcount = 0;
unsigned long totalalloced = 0;

static long warnmemflag = 0;
static long high = 1;
static long warned = 0;

void lowmemtest()

{
    if (warnmemflag) {
        if (!warned) {
            high = 0;
            warnlowmem();
            warnmemflag = 0;
            warned = 1;
        }
    }
    else {
        if (!high) high = (long) (AvailMem(MEMF_PUBLIC) > 90000);
        if (high) warned = 0;
    }
    if (high) warnmemflag = (long) (AvailMem(MEMF_PUBLIC) < 50000);
}

static allocateblock()

{
    register struct EventBlock *block;
    register unsigned short i;
    if (AvailMem(MEMF_PUBLIC) < 30000) {
        warnmemflag = 1;
        return(0);
    }
    if (AvailMem(MEMF_LARGEST) < 20000) {
        warnmemflag = 1;
        return(0);
    }
    block = (struct EventBlock *)
        AllocMem(sizeof(struct EventBlock),MEMF_PUBLIC | MEMF_CLEAR);
    if (block) {
        for (i=0;i<EVENTSPERBLOCK;i++) {
            Forbid();
            eventcount++;
            totalalloced++;
            block->array[i].next = eventlist;
            eventlist = &block->array[i];
            Permit();
        }
        Forbid();
        block->next = blocklist;
        blocklist = block;
        Permit();
    }
    return((long)block);
}

#define EVENT_FREED     0x35
#define EVENT_REMOVE    0x5A

void cleanupmemory()

{
    struct Event *event, *next;
    struct EventBlock *block;
    unsigned short i;
    unsigned short freed;
    struct Event *outevents = 0;
    struct Event *inevents = 0;
    struct EventBlock *nextblock;
    struct Event *top;
    Forbid();
    event = (struct Event *)eventlist;
    eventlist = 0;
    top = event;
    for (;event;event = event->next) event->type = EVENT_FREED;
    block = blocklist;
    for (;block;block = block->next) {
        freed = 1;
        for (i=0;i<EVENTSPERBLOCK;i++) {
            if (block->array[i].type != EVENT_FREED) {
                freed = 0;
                break;
            }
        }
        if (freed) {
            for (i=0;i<EVENTSPERBLOCK;i++) {
                block->array[i].type = EVENT_REMOVE;
            }
        }
        block->empty = freed;
    }
    event = top;
    for (;event;) {
        next = event->next;
        if (event->type == EVENT_REMOVE) {
            event->next = outevents;
            outevents = event;
        }
        else {
            event->next = inevents;
            inevents = event;
        }
        event = next;
    }
    eventlist = (struct NotateEvent *)inevents;
    block = blocklist;
    blocklist = 0;
    for (;block;) {
        nextblock = block->next;
        if (block->empty) {
            totalalloced -= EVENTSPERBLOCK;
            eventcount -= EVENTSPERBLOCK;
            FreeMem((char *)block,sizeof(struct EventBlock));
        }
        else {
            block->next = blocklist;
            blocklist = block;
        }
        block = nextblock;
    }
    Permit();
}


static long memsig = 0;
static struct Task *memtask = 0;
static char killmenow = 0;

static SAVEDS void memcode()

{
    /* geta4(); */
    memsig = 1 << AllocSignal(-1);
    for (;;) {
        Wait(memsig);
        if (killmenow) break;
        while (eventcount < MINEVENTS) {
            if (!allocateblock()) break;
        }
    }
    killmenow = 0;
}

SAVEDS  struct Event *allocevent()

{
    register struct Event *event;
    /* geta4(); */
    Forbid();
    if (eventlist) {
        event = (struct Event *)eventlist;
        eventlist = (struct NotateEvent *)event->next;
        eventcount--;
        Permit();
    }
    else {
        Permit();
        if (allocateblock()) {
            Forbid();
            event = (struct Event *)eventlist;
            eventlist = (struct NotateEvent *)event->next;
            eventcount--;
            Permit();
        }
        else return(0);
    }
    if (eventcount < MINEVENTS) Signal(memtask,memsig);
    if (event) {
        event->next = 0;
        event->type = EVENT_VOICE;
    }
    return(event);
}

SAVEDS  struct Event *fastallocevent()

{
    register struct Event *event;
    /* geta4(); */
    Forbid();
    if (eventlist) {
        event = (struct Event *)eventlist;
        eventlist = (struct NotateEvent *)event->next;
        eventcount--;
    }
    else event = 0;
    Permit();
    if (event) {
        event->next = 0;
        event->type = EVENT_VOICE;
    }
    if (eventcount < MINEVENTS) Signal(memtask,memsig);
    return(event);
}

SAVEDS  void freeevent(event)

register struct StringEvent *event;

{
    /* geta4(); */
    if ((event->type == EVENT_SYSX) || (event->type == EVENT_LYRIC)) {
        freestring(event->string);
    }
    memset((char *)&event->time,0,20);
    event->type = EVENT_FREED;
    Forbid();
    event->next = (struct StringEvent *) eventlist;
    eventlist = (struct NotateEvent *) event;
    eventcount++;
    Permit();
}

void freeallevents()

{
    register struct EventBlock *block = blocklist;
    register struct EventBlock *next;
    for (;block;) {
        next = block->next;
        FreeMem((char *)block,sizeof(struct EventBlock));
        block = next;
    }
    blocklist = 0;
    eventlist = 0;
    eventcount = 0;
}

void startmem()

{
    memtask = (struct Task *)CreateTask("Bars&Pipes Memory Allocator",3,(void_cast)memcode,4000);
}

void endmem()

{
    if (memtask) {
        killmenow = 1;
        Signal(memtask,memsig);
        while (killmenow) WaitTOF();
    }   
    freeallevents();
}

static struct Event *sortevents(
    struct Event *events,
    unsigned short len
)

{
    register struct Event *left, *right;
    register unsigned short cut;
    struct Event *top;
    if (len < 3) {
        if (!events) return(0);
        if (len == 1) return(events);
        left = events;
        right = events->next;
        if (!right) return(left);
        if (left->time > right->time) {
            left->next = 0;
            right->next = left;
            return(right);
        }
        return(left);
    }
    cut = len >> 1;
    left = events;
    events = (struct Event *) List_Index((struct ListItem *) events,cut-1);
    right = sortevents(events->next,len-cut);
    events->next = 0;
    left = sortevents(left,cut);
    top = 0;
    for (;left && right;) {
        if (left->time < right->time) {
            if (!top) top = left;
            else events->next = left;
            events = left;
            left = events->next;
        }
        else {
            if (!top) top = right;
            else events->next = right;
            events = right;
            right = events->next;
        }
    }
    if (left) events->next = left;
    else events->next = right;
    return(top);
}

static struct Track *sorttracks(
    struct Track *tracks,
    unsigned short len
)

{
    register struct Track *left, *right;
    register unsigned short cut;
    struct Track *top;
    if (len < 3) {
        if (!tracks) return(0);
        if (len == 1) return(tracks);
        left = tracks;
        right = tracks->next;
        if (!right) return(left);
        if (left->height > right->height) {
            left->next = 0;
            right->next = left;
            return(right);
        }
        return(left);
    }
    cut = len >> 1;
    left = tracks;
    tracks = (struct Track *) List_Index((struct ListItem *) tracks,cut-1);
    right = sorttracks(tracks->next,len-cut);
    tracks->next = 0;
    left = sorttracks(left,cut);
    top = 0;
    for (;left && right;) {
        if (left->height < right->height) {
            if (!top) top = left;
            else tracks->next = left;
            tracks = left;
            left = tracks->next;
        }
        else {
            if (!top) top = right;
            else tracks->next = right;
            tracks = right;
            right = tracks->next;
        }
    }
    if (left) tracks->next = left;
    else tracks->next = right;
    return(top);
}

void sorttracklist()

{
    functions.tracklist =
        sorttracks(functions.tracklist,List_Len((struct ListItem *)functions.tracklist));
}

SAVEDS  void freelist(event)

register struct Event *event;

{
    register struct Event *next;
    /* geta4(); */
    for (;event;) {
        next = event->next;
        freeevent(event);
        event = next;
    }
}

struct Event *dupeevent(event)

struct StringEvent *event;

{
    struct StringEvent *new = (struct StringEvent *) allocevent();
    char type = event->type & 0xF;
    if (new) {
        memcpy((char *)new,(char *)event,sizeof(struct NotateEvent));
        new->next = 0;
        if (type == EVENT_LYRIC) {
            dupestring(&new->string);
        }
        else if (type == EVENT_SYSX) {
            if (event->string) {
                new->string = (struct String *)
                    AllocMem(event->string->length + 1,MEMF_CLEAR);
                if (new->string) {
                    memcpy(new->string,event->string,event->string->length);
                }
            }
        }
    }
    return((struct Event *)new);
}

SAVEDS  struct Event *dupelist(list)

struct Event *list;

{
    struct Event *new;
    struct Event *last;
    struct Event *top;
    /* geta4(); */
    last = 0;
    top = 0;
    for (;list;list = list->next) {
        new = dupeevent(list);
        if (new) {
            if (top) last->next = new;
            else top = new;
            last = new;
            new->next = 0;
        }
        else {
            freelist(top);
            return(0);
        }
    }
    return(top);
}

SAVEDS  struct Event *sorteventlist(events)

register struct Event *events;

{
    /* geta4(); */
    return(sortevents(events,List_Len((struct ListItem *)events)));
}

struct ListItem {
    struct ListItem *next;
};

void freesizedlist(top,size)

struct ListItem *top;

{
    struct ListItem *next;
    for (;top;) {
        next = top->next;
        FreeMem((char *)top,size);
        top = next;
    }
}
