/*
 * Copyright (c) 2002, The Tendra Project <http://www.ten15.org/>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice unmodified, this list of conditions, and the following
 *    disclaimer.
 * 2. 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 AUTHOR ``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 AUTHOR 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.
 *
 *
 *    		 Crown Copyright (c) 1997
 *
 *    This TenDRA(r) Computer Program is subject to Copyright
 *    owned by the United Kingdom Secretary of State for Defence
 *    acting through the Defence Evaluation and Research Agency
 *    (DERA).  It is made available to Recipients with a
 *    royalty-free licence for its use, reproduction, transfer
 *    to other parties and amendment for any purpose not excluding
 *    product development provided that any such use et cetera
 *    shall be deemed to be acceptance of the following conditions:-
 *
 *        (1) Its Recipients shall ensure that this Notice is
 *        reproduced upon any copies or amended versions of it;
 *
 *        (2) Any amended version of it shall be clearly marked to
 *        show both the nature of and the organisation responsible
 *        for the relevant amendment or amendments;
 *
 *        (3) Its onward transfer from a recipient to another
 *        party shall be deemed to be that party's acceptance of
 *        these conditions;
 *
 *        (4) DERA gives no warranty or assurance as to its
 *        quality or suitability for any purpose and DERA accepts
 *        no liability whatsoever in relation to any use to which
 *        it may be put.
 *
 * $TenDRA: tendra/src/tools/disp/tree.c,v 1.9 2005/11/05 12:03:15 stefanf Exp $
 */


#include "config.h"
#include "fmm.h"
#include "tdf_types.h"

#include "types.h"
#include "ascii.h"
#include "basic.h"
#include "file.h"
#include "pretty.h"
#include "tdf.h"
#include "tree.h"


/*
 *    INTERNAL MEMORY STORAGE AREA
 *
 *    Some strings are output directly.  Others are built up gradually
 *    in this buffer.  length gives the length of the current string.
 *    page_length gives the size of the buffer.
 */

int length;
static int page_length;
static char *page = null;


/*
 *    TOP LEVEL WORD AND CURRENT WORD POINTER
 *
 *    The top of the TDF tree is given by word1.  The current position in
 *    the tree is given by word_ptr.
 */

word word1 = { SIMPLE, 0, null, null, null };
word *word_ptr;


/*
 *    SET UP INITIAL PAGE AND WORDS
 *
 *    The page buffer is initialized and the current word pointer is set
 *    to the top-level word.
 */

void
initialize_tree(void)
{
	if (page == null) {
		page = xmalloc_nof (char, 10000);
		page_length = 10000;
	}
	word_ptr = &word1;
	length = 0;
	return;
}


/*
 *    START A NEW WORD
 *
 *    A new word of layout type c is created.
 */

word *
new_word(int c)
{
#define BLOCK 100
	static word *wblock;
	static int block_count = BLOCK;

	word *new_ptr;
	if (printflag) {

		if (block_count == BLOCK) {
			/* Allocate space if required */
			wblock = xmalloc_nof (word, BLOCK);
			block_count = 0;
		}

		/* set up fields of new word */
		new_ptr = wblock + (block_count++);
		if (word_ptr->type != SIMPLE && word_ptr->son == null) {
			word_ptr->son = new_ptr;
		} else {
			word_ptr->bro = new_ptr;
		}
		word_ptr = new_ptr;
		word_ptr->type = (char) c;
		if (length) {
			word_ptr->text = page;
			word_ptr->length = length;
			page += (length + 1);
			page_length -= (length + 1);
			if (page_length < 100) {
				page = xmalloc_nof (char, 10000);
				page_length = 10000;
			}
			length = 0;
		} else {
			word_ptr->text = "";
			word_ptr->length = 0;
		}
		word_ptr->son = null;
		word_ptr->bro = null;
	}
	return (word_ptr);
}


/*
 *    OUTPUT A SINGLE CHARACTER INTO INTERNAL MEMORY
 *
 *    The character c is appended to the internal memory buffer.
 */

void
out_char(int c)
{
	if (printflag) {
		page [length] = (char) c;
		length++;
		page [length] = 0;
	}
	return;
}


/*
 *    OUTPUT A STRING INTO INTERNAL MEMORY
 *
 *    The string str is appended to the internal memory buffer.
 */

void
out_string(char *str)
{
	if (printflag) {
		IGNORE strcpy (page + length, str);
		length += (int) strlen (str);
	}
	return;
}


/*
 *    MAKE A STRING INTO A WORD
 *
 *    This routine creates a simple word from the string str, appending it
 *    to any text in the internal memory buffer.
 */

void
out(char *str)
{
	if (printflag) {
		if (length) {
			out_string (str);
			IGNORE new_word (SIMPLE);
		} else {
			word *ptr = new_word (SIMPLE);
			ptr->text = str;
			ptr->length = (int) strlen (str);
		}
	}
	return;
}


/*
 *    MAKE AN INTEGER INTO A WORD
 *
 *    This routine creates a simple word from an integer.
 */

void
out_int(long n)
{
	if (printflag) {
		/* Note that the input is cast to an unsigned int */
		unsigned long m = (unsigned long) n, dig, power = 1;

		/* Get the highest power of 10 dividing m */
		while ((m / power) >= 10) power *= 10;

		/* Now output digits of m */
		while (power != 0) {
			dig = (m / power);
			m -= dig * power;
			power = (power / 10);
			out_char (charact (dig));
		}
		/* Make this into a simple word */
		IGNORE new_word (SIMPLE);
	}
	return;
}


/*
 *    MAKE A STRING OF OCTAL DIGITS INTO A WORD
 *
 *    This routine creates a simple word from a string of octal digits
 *    and a sign.  If the string of octal digits corresponds to a
 *    number of at most 32 bits, out_int is used, otherwise the string
 *    is output directly.
 */

void
out_signed(char *n, int sn)
{
	if (printflag) {
		/* Calculate the number of binary digits in n */
		int a = digit (*n), d;
		if (a & 4) {
			d = 3 * tdf_int_digits;
		} else {
			if (a & 2) {
				d = 3 * tdf_int_digits - 1;
			} else {
				d = 3 * tdf_int_digits - 2;
			}
		}
		if (d <= BYTESIZE * (int) sizeof (long)) {
			/* If n will fit into a long work out its value */
			char *s;
			long t = 0;
			for (s = n; *s; s++) t = 8 * t + digit (*s);
			if (sn && t) out_char ('-');
			out_int (t);
		} else {
			/* Otherwise output n as a string of octal digits */
			word *w;
			out_string ("octal");
			w = new_word (HORIZ_BRACKETS);
			out_string (sn ? "-0" : "0");
			out (n);
			end_word (w);
		}
	}
	return;
}


/*
 *    MAKE A UNIQUE INTO A WORD
 *
 *    The unique u is output.
 */

void
out_unique(unique u)
{
	word *w;
	out_string ("unique");
	w = new_word (HORIZ_BRACKETS);
	while (*u) {
		out (*u);
		u++;
	}
	end_word (w);
	return;
}


/*
 *    UTILITY FORMATTING ROUTINE
 *
 *    This routine is used to format a function and its arguments.  The
 *    function name is given by func, the layout type by c, and the
 *    arguments by the decode string args.
 */

void
format(int c, char *func, char *args)
{
	if (printflag) {
		word *ptr;
		if (length) {
			out_string (func);
			ptr = new_word (c);
		} else {
			ptr = new_word (c);
			ptr->text = func;
			ptr->length = (int) strlen (func);
		}
		decode (args);
		end_word (ptr);
	} else {
		decode (args);
	}
	return;
}
