#
# Copyright 2013-2014 Yuichiro Moriguchi
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
putlicense_cstyle.sh

[ -z "$READER" ] && readr='(*(s->read))'
[ -n "$READER" ] && readr=$READER

echo '/**'
awk '{ printf(" * %s\n", $0) }' description
echo ' */'
[ -z "$READSLOW" ] || echo '#include <unistd.h>'
cat << EOF
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <errno.h>
#include "${FILENAME}.h"
EOF
cat definition

cat << EOF
#define N  0x1000
#define E  0x0100
#define W  0x0010
#define S  0x0001
#define L  W
#define R  E
#define NE (N | E)
#define NW (N | W)
#define SE (S | E)
#define SW (S | W)
#define INITIAL 0

#define STATE (__this__->state)
#define THROW_CODE_ACCEPT 91
#define THROW_CODE_REJECT 85
#define THROW_CODE_ERROR  72
#define NEW_NINA_STREAM_FILE(f)   ((nina_stream_t)(&(f)))
#define NEW_NINA_STREAM_STRING(s) ((nina_stream_t)(&(s)))

#define THROWERROR_C(o)  longjmp((o)->ex, THROW_CODE_ERROR);
#define THROWERROR       longjmp((__this__)->ex, THROW_CODE_ERROR);
#define _movetape(s)  ${FILENAME}_movetape(__this__, s)
#define _writetape(s) ${FILENAME}_writetape(__this__, s)
#define NINA_EOF -1

static void ${FILENAME}_run(struct ${FILENAME}_tag *s);

static int __readf(struct ${FILENAME}_tag *b, nina_stream_t f) {
	return fgetc(*((FILE **)f));
}

static int __reads(struct ${FILENAME}_tag *b, nina_stream_t f) {
	char **s = (char **)f;

	return **s ? *((*s)++) : NINA_EOF;
}

static int __readsys(struct ${FILENAME}_tag *b, nina_stream_t f) {
	char c[1];
	int r;

	if((r = read(**(int **)f, c, 1)) > 0) {
		return c[0];
	} else if(r == 0) {
		return NINA_EOF;
	} else {
		longjmp(b->ex, THROW_CODE_ERROR);
	}
}

EOF

case "$TAPETYPE" in
DFA)
  cat << EOF
int ${FILENAME}_init(struct ${FILENAME}_tag *s, FILE *f) {
	memset(s, 0, sizeof(struct ${FILENAME}_tag));
	s->run = ${FILENAME}_run;
	s->befstream = f;
	s->reader = &(s->befstream);
	s->read = __readf;
	s->now = ${readr}(s, s->reader);
	return 1;
}

int ${FILENAME}_init_string(struct ${FILENAME}_tag *s, const char *f) {
	memset(s, 0, sizeof(struct ${FILENAME}_tag));
	s->run = ${FILENAME}_run;
	s->befstream = (void *)f;
	s->reader = &(s->befstream);
	s->read = __reads;
	s->now = ${readr}(s, s->reader);
	return 1;
}

int ${FILENAME}_init_descriptor(struct ${FILENAME}_tag *s, int d) {
	memset(s, 0, sizeof(struct ${FILENAME}_tag));
	s->run = ${FILENAME}_run;
	s->sys_descriptor = d;
	s->befstream = &(s->sys_descriptor);
	s->reader = &(s->befstream);
	s->read = __readsys;
	s->now = ${readr}(s, s->reader);
	return 1;
}

static $CTYPE ${FILENAME}_readtape(struct ${FILENAME}_tag *s) {
	return s->now;
}

static void ${FILENAME}_writetape(struct ${FILENAME}_tag *s, $CTYPE o) {
	s->now = o;
}

static void ${FILENAME}_movetape(struct ${FILENAME}_tag *s, int direction) {
	switch(direction) {
	case E:
		if(s->now != -1)  s->now = ${readr}(s, s->reader);
		return;
	default:  THROWERROR_C(s);
	}
}
EOF
  ;;
*)
  cat << EOF
int ${FILENAME}_init(struct ${FILENAME}_tag *s, FILE *f) {
	int p = 0;
	$CTYPE c;

	memset(s, 0, sizeof(struct ${FILENAME}_tag));
	s->run = ${FILENAME}_run;
	while((c = fgetc(f)) > 0) {
		if(p >= sizeof(s->_tape))  return 0;
		s->_tape[p++] = c;
	}

	if(errno) {
		return 0;
	} else {
		s->_tapeptr = 0;
		s->_tapelen = p;
		return 1;
	}
}

int ${FILENAME}_init_string(struct ${FILENAME}_tag *s, const char *f) {
	int p = 0;

	memset(s, 0, sizeof(struct ${FILENAME}_tag));
	s->run = ${FILENAME}_run;
	while(f[p] != 0) {
		if(p >= sizeof(s->_tape))  return 0;
		s->_tape[p] = f[p];
		p++;
	}
	s->_tapeptr = 0;
	s->_tapelen = p;
	return 1;
}

int ${FILENAME}_init_descriptor(struct ${FILENAME}_tag *s, int d) {
	char a[NINA_BUF];

	memset(a, 0, NINA_BUF);
	if(read(d, a, NINA_BUF) > 0) {
		return ${FILENAME}_init_string(s, a);
	} else {
		return 0;
	}
}

static $CTYPE ${FILENAME}_readtape(struct ${FILENAME}_tag *s) {
	if(s->_tapeptr < 0 || s->_tapeptr >= s->_tapelen) {
		return -1;
	}
	return s->_tape[s->_tapeptr];
}

static void ${FILENAME}_writetape(struct ${FILENAME}_tag *s, $CTYPE o) {
	if(s->_tapeptr >= 0 && s->_tapeptr < s->_tapelen) {
		s->_tape[s->_tapeptr] = o;
	}
}

static void ${FILENAME}_movetape(struct ${FILENAME}_tag *s, int direction) {
	switch(direction) {
	case E:
		s->_tapeptr += s->_tapeptr < s->_tapelen ? 1 : 0;
		return;
	case W:
		s->_tapeptr -= s->_tapeptr >= 0 ? 1 : 0;
		return;
	default:  THROWERROR_C(s);
	}
}
EOF
  ;;
esac

cat field
c='$c'
echo
cat << EOF
static int ${FILENAME}_step(struct ${FILENAME}_tag *__this__, $CTYPE __c__) {
	switch(STATE) {
EOF
print_states
cat << EOF
	}
	return 0;
}

static int ${FILENAME}_accepted(struct ${FILENAME}_tag *__this__) {
EOF
print_accepts
cat << EOF
}

static int ${FILENAME}_execaction(struct ${FILENAME}_tag *__this__, $CTYPE __c__) {
	switch(STATE) {
EOF
print_actions
cat << EOF
	}
	return 1;
}

static int ${FILENAME}_execstep(struct ${FILENAME}_tag *s) {
	if(${FILENAME}_step(s, ${FILENAME}_readtape(s)) != 0) {
		return 0;
	} else if(${FILENAME}_accepted(s)) {
		return 1;
	} else {
		THROWERROR_C(s);
		return 0;
	}
}

static void ${FILENAME}_run(struct ${FILENAME}_tag *s) {
	int z;

	if((z = setjmp(s->ex)) == THROW_CODE_ERROR) {
		fprintf(stderr, "${FILENAME}:error\n");
	} else if(z) {
		abort();
	} else {
		do {
			${FILENAME}_execaction(s, ${FILENAME}_readtape(s));
		} while(!${FILENAME}_execstep(s));
	}
}

int ${FILENAME}_parse(FILE *rd) $tre {
	struct ${FILENAME}_tag b;

	if(${FILENAME}_init(&b, rd)) {
		${FILENAME}_run(&b);
		return 1;
	} else {
		return 0;
	}
}

int ${FILENAME}_parse_string(const char *rd) $tre {
	struct ${FILENAME}_tag b;

	if(${FILENAME}_init_string(&b, rd)) {
		${FILENAME}_run(&b);
		return 1;
	} else {
		return 0;
	}
}

int ${FILENAME}_parse_descriptor(int rd) $tre {
	struct ${FILENAME}_tag b;

	if(${FILENAME}_init_descriptor(&b, rd)) {
		${FILENAME}_run(&b);
		return 1;
	} else {
		return 0;
	}
}
EOF
cat fragment

#
# print header file
#
hfile="/output/${OUTPUT_FILENAME}.h"
sed "s/@@YEAR@@/$YEAR/g
s/@@OWNER@@/$OWNER/g
s/@@ORGANIZATION@@/$ORGANIZATION/g" /license/$LICENSE > $hfile

cat header >> $hfile
cat >> $hfile << EOF
#ifndef DEFINED_NINA_COMMON
#define DEFINED_NINA_COMMON
#include <stdio.h>
#include <setjmp.h>

#define NINA_TRUE 1
#define NINA_FALSE 0
#define NINA_FAIL -9
#define NINA_YIELD -85
#define NINA_BUF 4096

#ifndef YYSTYPE
#define YYSTYPE int
#endif
union LREngine_stack {
	int state;
	YYSTYPE val;
};

typedef void **nina_stream_t;
#endif

#ifndef DEFINED_${FILENAME}
#define DEFINED_${FILENAME}

struct ${FILENAME}_tag {
	void (*run)(struct ${FILENAME}_tag *s);

	int state;
	jmp_buf ex;
EOF

case "$TAPETYPE" in
DFA)
  cat >> $hfile << EOF
	int (*read)(struct ${FILENAME}_tag *, nina_stream_t);
	void *befstream;
	int sys_descriptor;
	nina_stream_t reader;
	$CTYPE now;
EOF
  ;;
*)
  cat >> $hfile << EOF
	$CTYPE _tape[NINA_BUF];
	int _tapeptr;
	int _tapelen;
EOF
  ;;
esac
cat >> $hfile << EOF
};

#ifdef __cplusplus
extern "C" {
	int ${FILENAME}_init(struct ${FILENAME}_tag *s, FILE *f);
	int ${FILENAME}_init_string(struct ${FILENAME}_tag *s, const char *f);
	int ${FILENAME}_init_descriptor(struct ${FILENAME}_tag *s, int f);
	int ${FILENAME}_parse(FILE *f);
	int ${FILENAME}_parse_string(const char *f);
	int ${FILENAME}_parse_descriptor(int f);
}

class ${FILENAME} {
private:
	struct ${FILENAME}_tag ccls;
public:
	${FILENAME}() {
		${FILENAME}_init(&ccls);
	}

	int run() {
		return ccls.run(&ccls, f);
	}
};
#else
typedef struct ${FILENAME}_tag ${FILENAME};
extern int ${FILENAME}_init(struct ${FILENAME}_tag *s, FILE *f);
extern int ${FILENAME}_init_string(struct ${FILENAME}_tag *s, const char *f);
extern int ${FILENAME}_init_descriptor(struct ${FILENAME}_tag *s, int f);
extern int ${FILENAME}_parse(FILE *f);
extern int ${FILENAME}_parse_string(const char *f);
extern int ${FILENAME}_parse_descriptor(int f);
#endif
#endif
EOF
