/* ----------------------------------------------------------------------- *
 *   
 *   Copyright 1996-2014 The NASM Authors - All Rights Reserved
 *   See the file AUTHORS included with the NASM distribution for
 *   the specific copyright holders.
 *
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following
 *   conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *     
 *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * ----------------------------------------------------------------------- */

#include "compiler.h"

#include <stdio.h>
#include <stdlib.h>
#include "segtab.h"

struct segtabnode {
    int localseg;
    int destseg;
    int32_t offset;

    struct segtabnode *left;
    struct segtabnode *right;
    /*
     * counts of how many are left or right, for use in reorganising
     * the tree
     */
    int leftcount;
    int rightcount;
};

/*
 * init_seglocations()
 * add_seglocation()
 * get_seglocation()
 * done_seglocation()
 *
 * functions used by write_output() to manipulate associations
 * between segment numbers and locations (which are built up on a per
 * module basis, but we only need one module at a time...)
 *
 * implementation: we build a binary tree.
 */

void init_seglocations(segtab * root)
{
    *root = NULL;
}

static void descend_tree_add(struct segtabnode **node,
                      int localseg, int destseg, int32_t offset)
{
    struct segtabnode *n;

    if (*node == NULL) {
        *node = malloc(sizeof(**node));
        if (!*node) {
            fprintf(stderr, "segment table: out of memory\n");
            exit(1);
        }
        (*node)->localseg = localseg;
        (*node)->offset = offset;
        (*node)->left = NULL;
        (*node)->leftcount = 0;
        (*node)->right = NULL;
        (*node)->rightcount = 0;
        (*node)->destseg = destseg;
        return;
    }

    if (localseg < (*node)->localseg) {
        (*node)->leftcount++;
        descend_tree_add(&(*node)->left, localseg, destseg, offset);

        if ((*node)->leftcount > (*node)->rightcount + 2) {
            n = *node;
            *node = n->left;
            n->left = (*node)->right;
            n->leftcount = (*node)->rightcount;
            (*node)->right = n;
            (*node)->rightcount = n->leftcount + n->rightcount + 1;
        }
    } else {
        (*node)->rightcount++;
        descend_tree_add(&(*node)->right, localseg, destseg, offset);

        if ((*node)->rightcount > (*node)->leftcount + 2) {
            n = *node;
            *node = n->right;
            n->right = (*node)->left;
            n->rightcount = (*node)->leftcount;
            (*node)->left = n;
            (*node)->leftcount = n->leftcount + n->rightcount + 1;
        }
    }
}

void add_seglocation(segtab * root, int localseg, int destseg, int32_t offset)
{
    descend_tree_add((struct segtabnode **)root, localseg, destseg,
                     offset);
}

int get_seglocation(segtab * root, int localseg, int *destseg,
                    int32_t *offset)
{
    struct segtabnode *n = (struct segtabnode *)*root;

    while (n && n->localseg != localseg) {
        if (localseg < n->localseg)
            n = n->left;
        else
            n = n->right;
    }
    if (n) {
        *destseg = n->destseg;
        *offset = n->offset;
        return 1;
    } else
        return 0;
}

static void freenode(struct segtabnode *n)
{
    if (!n)
        return;
    freenode(n->left);
    freenode(n->right);
    free(n);
}

void done_seglocations(segtab * root)
{
    freenode(*root);
    *root = NULL;
}

#if 0
void printnode(int i, struct segtabnode *n)
{
    if (!n)
        return;
    printnode(i + 1, n->left);
    printf("%*s%d %d %ld\n", i, "", n->localseg, n->destseg, n->offset);
    printnode(i + 1, n->right);
}

void printtable()
{
    printnode(0, root);
}
#endif
