/*
 * 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/tnc/encode.c,v 1.7 2004/09/05 03:47:06 bp Exp $
 */


#include "config.h"
#include "msgcat.h"
#include "tdf_types.h"
#include "tdf_stream.h"

#include "types.h"
#include "encode.h"
#include "high.h"
#include "names.h"
#include "table.h"
#include "tdf.h"
#include "utility.h"


/*
 *    ENCODE AN ALIGNED STRING
 *
 *    The string s is encoded as an aligned string into the bitstream p.
 *    n is either the length of s, or -1, indicating that strlen should
 *    be used to find the length.
 */

void
enc_aligned_string(struct tdf_stream *p, char *s, long n)
{
    long i;
    if (n == -1) n = (long) strlen (s);
    tdf_en_tdfintl (p, 8);
    tdf_en_tdfintl (p, (unsigned long)n);
    tdf_en_align (p);
    for (i = 0 ; i < n ; i++) {
		tdf_en_bits (p, 8, (unsigned char)s[i]);
    }
    return;
}


/*
 *    ENCODE AN EXTERNAL NAME
 *
 *    The external name of the construct p is encoded into the bitstream b.
 */

void
enc_external(struct tdf_stream *b, construct *p)
{
    node *e = p->ename;
    tdf_en_tdfintl (b, (unsigned long)p->encoding);
    if (e->cons->encoding) {
		node *q = e->son;
		if (q->cons->sortnum == SORT_tdfstring) {
			node *r = q->bro;
			if (r == null) {
				enc_external_bits (b, ENC_string_extern);
				tdf_en_align (b);
				enc_aligned_string (b, q->cons->name, q->cons->encoding);
			} else {
				enc_external_bits (b, ENC_chain_extern);
				tdf_en_align (b);
				enc_aligned_string (b, q->cons->name, q->cons->encoding);
				enc_node (b, r);
			}
		} else {
			enc_external_bits (b, ENC_unique_extern);
			tdf_en_align (b);
			tdf_en_tdfintl (b, (unsigned long)q->cons->encoding);
			for (q = e->son->son ; q ; q = q->bro) {
				enc_aligned_string (b, q->cons->name, q->cons->encoding);
			}
		}
    } else {
		enc_external_bits (b, ENC_string_extern);
		tdf_en_align (b);
		enc_aligned_string (b, p->name, (long) -1);
    }
    return;
}


/*
 *    FIND THE VALUE OF A STRING OF OCTAL DIGITS
 *
 *    The value of the node p, which represents a number, is returned.
 */

static long
octval(node *p)
{
    long n = (long) octal_to_ulong (p->bro->cons->name);
    if (p->cons->encoding) n = -n;
    return (n);
}


/*
 *    ENCODE A NODE
 *
 *    The node p is encoded into the bitstream b.
 */

void
enc_node(struct tdf_stream *b, node *p)
{
    while (p) {
		construct *q = p->cons;
		switch (q->sortnum) {

	    case SORT_tdfbool : {
			/* Encode a bit */
			tdf_en_bits (b, 1, (unsigned long)q->encoding);
			break;
	    }

	    case SORT_bytestream : {
			/* Encode a bytestream */
			struct tdf_stream *c = tdf_bs_create (NULL, TDFS_MODE_WRITE, NULL);
			enc_node (c, p->son);
			(void)tdf_en_bitstream (b, c);
			break;
	    }

	    case SORT_completion : {
			/* Encode a completion */
			if (p->son) enc_node (b, p->son);
			break;
	    }

	    case SORT_small_tdfint : {
			/* Encode a small integer */
			tdf_en_tdfintl (b, (unsigned long)q->encoding);
			break;
	    }

	    case SORT_tdfint : {
			/* Encode a number */
			char *num = q->name;
			while (*num) {
				unsigned long d = (unsigned char) (*num - '0');
				num++;
				if (*num == 0) d |= 8;
				tdf_en_bits (b, 4, d);
			}
			break;
	    }

	    case SORT_option : {
			/* Encode an optional argument */
			if (p->son) {
				tdf_en_bits (b, 1, (unsigned long) 1);
				enc_node (b, p->son);
			} else {
				tdf_en_bits (b, 1, (unsigned long) 0);
			}
			break;
	    }

	    case SORT_repeat : {
			/* Encode a repeated argument */
			enc_list_start (b);
			tdf_en_tdfintl (b, (unsigned long)q->encoding);
			if (p->son) enc_node (b, p->son);
			break;
	    }

	    case SORT_tdfstring : {
			/* Encode a string */
			long i, n = q->encoding;
			if (n == -1) {
				node *r = p->son;
				long m = octval (r);
				if (m < 0) m = -m;
				r = r->bro->bro;
				n = r->cons->encoding;
				r = r->son;
				tdf_en_tdfintl (b, (unsigned long)m);
				tdf_en_tdfintl (b, (unsigned long)n);
				for (i = 0 ; i < n ; i++) {
					tdf_en_bits (b, (unsigned) m, (unsigned long)octval (r));
					r = r->bro->bro;
				}
			} else {
				tdf_en_cstringn (b, (size_t)n, q->name);
			}
			break;
	    }

	    case SORT_unknown : {
			/* Encode an unknown struct tdf_stream */
			MSG_FATAL_cant_encode_unknown_bitstream ();
			break;
	    }

	    case SORT_al_tag : {
			/* Encode an alignment tag */
			long e = q->encoding;
			enc_al_tag_bits (b, (int) e);
			if (e == ENC_make_al_tag) {
				tdf_en_tdfintl (b, (unsigned long)p->son->cons->encoding);
			} else {
				if (p->son) enc_node (b, p->son);
			}
			break;
	    }

	    case SORT_label : {
			/* Encode a label */
			long e = q->encoding;
			enc_label_bits (b, (int) e);
			if (e == ENC_make_label) {
				tdf_en_tdfintl (b, (unsigned long)p->son->cons->encoding);
			} else {
				if (p->son) enc_node (b, p->son);
			}
			break;
	    }

	    case SORT_tag : {
			/* Encode a tag */
			long e = q->encoding;
			enc_tag_bits (b, (int) e);
			if (e == ENC_make_tag) {
				tdf_en_tdfintl (b, (unsigned long)p->son->cons->encoding);
			} else {
				if (p->son) enc_node (b, p->son);
			}
			break;
	    }

	    case SORT_token : {
			/* Encode a token */
			tok_info *info = get_tok_info (q);
			if (is_high (info->res)) {
				enc_token_bits (b, ENC_token_apply_token);
				enc_token_bits (b, ENC_make_tok);
				tdf_en_tdfintl (b, (unsigned long)q->encoding);
				tdf_en_tdfintl (b, (long) 0);
			} else {
				enc_token_bits (b, ENC_make_tok);
				tdf_en_tdfintl (b, (unsigned long)q->encoding);
			}
			if (p->son) {
				if (p->son->cons != &token_cons) {
					struct tdf_stream *c = tdf_bs_create (NULL, TDFS_MODE_WRITE, NULL);
					enc_node (c, p->son);
					(void)tdf_en_bitstream (b, c);
				}
			} else {
				tdf_en_tdfintl (b, (long) 0);
			}
			break;
	    }

	    default : {
			/* Encode a simple sort */
			unsigned int bits = sort_encoding [ q->sortnum ];
			int extn = sort_extension [ q->sortnum ];
			if (extn) {
				tdf_en_tdfextint (b, bits, (unsigned long)q->encoding);
			} else {
				tdf_en_bits (b, bits, (unsigned long)q->encoding);
			}
			if (p->son) enc_node (b, p->son);
			break;
	    }
		}
		p = p->bro;
    }
    return;
}


/*
 *    ENCODE A SORT
 */

static void
enc_sort(struct tdf_stream *b, sortname s)
{
    if (is_high (s)) {
		int i;
		high_sort *h = high_sorts + high_no (s);
		enc_sort (b, SORT_token);
		enc_sort (b, h->res);
		enc_list_start (b);
		tdf_en_tdfintl (b, (unsigned long)h->no_args);
		for (i = 0 ; i < h->no_args ; i++) {
			enc_sort (b, h->args [i]);
		}
    } else {
		enc_sortname_bits (b, s);
    }
    return;
}


/*
 *    ALIGNMENT TAG DEFINITION AUXILIARY ENCODING ROUTINE
 *
 *    The definition of the alignment tag p is encoded into the bitstream p.
 */

void
enc_aldef(struct tdf_stream *b, construct *p)
{
    al_tag_info *info = get_al_tag_info (p);
    enc_al_tagdef_bits (b, ENC_make_al_tagdef);
    tdf_en_tdfintl (b, (unsigned long)p->encoding);
    enc_node (b, info->def);
    return;
}


/*
 *    TAG DECLARATION AUXILIARY ENCODING ROUTINE
 *
 *    The declaration of the tag p is encoded into the bitstream p.
 */

void
enc_tagdec(struct tdf_stream *b, construct *p)
{
    int m = 0;
    tag_info *info = get_tag_info (p);
    switch (info->var) {
	case 0 : m = ENC_make_id_tagdec ; break;
	case 1 : m = ENC_make_var_tagdec ; break;
	case 2 : m = ENC_common_tagdec ; break;
    }
    enc_tagdec_bits (b, m);
    tdf_en_tdfintl (b, (unsigned long)p->encoding);
    enc_node (b, info->dec);
    return;
}


/*
 *    TAG DEFINITION AUXILIARY ENCODING ROUTINE
 *
 *    The definition of the tag p is encoded into the bitstream p.  Because
 *    of common_tagdef, there may actually be more than one definition.
 *    The number of definitions is returned.
 */

int
enc_tagdef(struct tdf_stream *b, construct *p)
{
    int n = 0;
    int m = 0;
    tag_info *info = get_tag_info (p);
    node *d = info->def;
    switch (info->var) {
	case 0 : m = ENC_make_id_tagdef ; break;
	case 1 : m = ENC_make_var_tagdef ; break;
	case 2 : m = ENC_common_tagdef ; break;
    }
    while (d) {
		/* Can have multiple definitions */
		enc_tagdef_bits (b, m);
		tdf_en_tdfintl (b, (unsigned long)p->encoding);
		enc_node (b, d->son);
		d = d->bro;
		n++;
    }
    return (n);
}


/*
 *    WORK OUT THE NUMBER OF FORMAL ARGUMENTS GIVEN A STRING
 */

static unsigned long
no_formals(char *args)
{
    long n = 0;
    while (*args) {
		args = find_sortname (args, (sortname *) null);
		args++;
		n = n + 1;
    }
    return (n);
}


/*
 *    TOKEN DECLARATION AUXILIARY ENCODING ROUTINE
 *
 *    The declaration of the token p is encoded into the bitstream p.
 */

void
enc_tokdec(struct tdf_stream *b, construct *p)
{
    tok_info *info = get_tok_info (p);
    enc_tokdec_bits (b, ENC_make_tokdec);
    tdf_en_tdfintl (b, (unsigned long)p->encoding);

    /* Deal with signature */
    if (info->sig == null) {
		tdf_en_bits (b, 1, (long) 0);
    } else {
		enc_node (b, info->sig);
    }

    /* Encode token sort */
    enc_sort (b, SORT_token);

    /* Encode the token result sort */
    enc_sort (b, info->res);

    /* Encode the token argument sorts */
    enc_list_start (b);
    if (info->args) {
		char *q = info->args;
		tdf_en_tdfintl (b, no_formals (q));
		while (*q) {
			sortname s;
			q = find_sortname (q, &s);
			q++;
			enc_sort (b, s);
		}
    } else {
		tdf_en_tdfintl (b, (long) 0);
    }
    return;
}


/*
 *    TOKEN DEFINITION AUXILIARY ENCODING ROUTINE
 *
 *    The definition of the token p is encoded into the bitstream p.
 */

void
enc_tokdef(struct tdf_stream *b, construct *p)
{
    struct tdf_stream *c = tdf_bs_create (NULL, TDFS_MODE_WRITE, NULL);
    tok_info *info = get_tok_info (p);
    enc_tokdef_bits (b, ENC_make_tokdef);
    tdf_en_tdfintl (b, (unsigned long)p->encoding);

    /* Deal with signature */
    if (info->sig == null) {
		tdf_en_bits (b, 1, 0);
    } else {
		enc_node (b, info->sig);
    }

    /* Encode token definition type */
    enc_token_defn_bits (c, ENC_token_definition);

    /* Encode the token result sort */
    enc_sort (c, info->res);

    /* Encode the token arguments */
    enc_list_start (c);
    if (info->args) {
		construct **q = info->pars;
		tdf_en_tdfintl (c, no_formals (info->args));
		while (*q) {
			tok_info *qinfo = get_tok_info (*q);
			enc_sort (c, qinfo->res);
			tdf_en_tdfintl (c, (unsigned long)(*q)->encoding);
			q++;
		}
    } else {
		tdf_en_tdfintl (c, (long) 0);
    }

    /* Encode the token definition */
    enc_node (c, info->def);
    (void)tdf_en_bitstream (b, c);
    return;
}
