/** @license 2022 Neil Edelman, distributed under the terms of the
 [MIT License](https://opensource.org/licenses/MIT).

 Scan journal entries for kjv references. */

#include "../src/journal.h"
#include "../src/kjv.h"
#include "../src/helper.h"
#include <inttypes.h> /* C99 */
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <limits.h>

/*!conditions:re2c*/

static int scan(union date32 date, const char *const buffer,
	struct kjv *const kj) {
	const char *YYCURSOR, *YYMARKER, *yyt1, *yyt2, *yyt3, *s0, *s1, *t0, *t1;
	enum kjv_book book = Revelation;
	uint32_t chapter = 0, verse = 0, verse_end = 0;
	enum YYCONDTYPE condition = yycline;
	size_t line = 1;
	char datestr[12] = {0};
	const char *why = "unexpected";
	assert(buffer && kj);
	YYCURSOR = YYMARKER = yyt1 = buffer;
	/*!re2c /**/
	re2c:define:YYCTYPE = char;
	re2c:yyfill:enable = 0;
	re2c:define:YYGETCONDITION = "condition";
	re2c:define:YYSETCONDITION = "condition = @@;";
	re2c:define:YYGETCONDITION:naked = 1;
	re2c:define:YYSETCONDITION:naked = 1;

	unix_control = [\x01-\x08\x0b-\x1f\x7f];
	ws = [ \t];
	glyph = [^] \ ("\x00" | "\n" | unix_control | ws);
	natural = [1-9][0-9]*;
	engage = ws+ "--" ws+;
	/* (natural ":")? Don't use for memorizing and use for reading, I think? */
	/*("``"|"\"") This is not in the next book. */
	lookat = ws* natural ":" natural [ab]? ("-" natural [ab]?)? engage;
	first = ("I" | "1") " "?;
	second = ("II" | "2") " "?;
	third = ("III" | "3") " "?;
	*/
	for( ; ; ) { /*!re2c /**/
		/* Default ignore. */
		<skip> [^\n\x00] { continue; }
		<skip> "\x00" { why = "no newline at end of file"; goto catch; }
		<line> "\x00" { return 1; }
		<line, skip> "\n" => line { line++; continue; }
		<line> * :=> skip

		/* Books. */
		<line> "Genesis" / lookat => book { book = Genesis; continue; }
		<line> "Exodus" / lookat => book { book = Exodus; continue; }
		<line> "Leviticus" / lookat => book { book = Leviticus; continue; }
		<line> "Numbers" / lookat => book { book = Numbers; continue; }
		<line> "Deuteronomy" / lookat => book { book = Deuteronomy; continue; }
		<line> "Joshua" / lookat => book { book = Joshua; continue; }
		<line> "Judges" / lookat => book { book = Judges; continue; }
		<line> "Ruth" / lookat => book { book = Ruth; continue; }
		<line> first "Samuel" / lookat => book { book = ISamuel; continue; }
		<line> second "Samuel" / lookat => book { book = IISamuel; continue; }
		<line> first "Kings" / lookat => book { book = IKings; continue; }
		<line> second "Kings" / lookat => book { book = IIKings; continue; }
		<line> first "Chronicles" / lookat
			=> book { book = IChronicles; continue; }
		<line> second "Chronicles" / lookat
			=> book { book = IIChronicles; continue; }
		<line> "Ezra" / lookat => book { book = Ezra; continue; }
		<line> "Nehemiah" / lookat => book { book = Nehemiah; continue; }
		<line> "Esther" / lookat => book { book = Esther; continue; }
		<line> "Job" / lookat => book { book = Job; continue; }
		<line> "Psalms" / lookat => book { book = Psalms; continue; }
		<line> "Proverbs" / lookat => book { book = Proverbs; continue; }
		<line> "Ecclesiastes" / lookat
			=> book { book = Ecclesiastes; continue; }
		<line> "Song of Solomon" / lookat
			=> book { book = Song_of_Solomon; continue; }
		<line> "Isaiah" / lookat => book { book = Isaiah; continue; }
		<line> "Jeremiah" / lookat => book { book = Jeremiah; continue; }
		<line> "Lamentations" / lookat
			=> book { book = Lamentations; continue; }
		<line> "Ezekiel" / lookat => book { book = Ezekiel; continue; }
		<line> "Daniel" / lookat => book { book = Daniel; continue; }
		<line> "Hosea" / lookat => book { book = Hosea; continue; }
		<line> "Joel" / lookat => book { book = Joel; continue; }
		<line> "Amos" / lookat => book { book = Amos; continue; }
		<line> "Obadiah" / lookat => book { book = Obadiah; continue; }
		<line> "Jonah" / lookat => book { book = Jonah; continue; }
		<line> "Micah" / lookat => book { book = Micah; continue; }
		<line> "Nahum" / lookat => book { book = Nahum; continue; }
		<line> "Habakkuk" / lookat => book { book = Habakkuk; continue; }
		<line> "Zephaniah" / lookat => book { book = Zephaniah; continue; }
		<line> "Haggai" / lookat => book { book = Haggai; continue; }
		<line> "Zechariah" / lookat => book { book = Zechariah; continue; }
		<line> "Malachi" / lookat => book { book = Malachi; continue; }
		<line> "Matthew" / lookat => book { book = Matthew; continue; }
		<line> "Mark" / lookat => book { book = Mark; continue; }
		<line> "Luke" / lookat => book { book = Luke; continue; }
		<line> "John" / lookat => book { book = John; continue; }
		<line> "Acts" / lookat => book { book = Acts; continue; }
		<line> "Romans" / lookat => book { book = Romans; continue; }
		<line> first "Corinthians" / lookat
			=> book { book = ICorinthians; continue; }
		<line> second "Corinthians" / lookat
			=> book { book = IICorinthians; continue; }
		<line> "Galatians" / lookat => book { book = Galatians; continue; }
		<line> "Ephesians" / lookat => book { book = Ephesians; continue; }
		<line> "Philippians" / lookat => book { book = Philippians; continue; }
		<line> "Colossians" / lookat => book { book = Colossians; continue; }
		<line> first "Thessalonians" / lookat
			=> book { book = IThessalonians; continue; }
		<line> second "Thessalonians" / lookat
			=> book { book = IIThessalonians; continue; }
		<line> first "Timothy" / lookat => book { book = ITimothy; continue; }
		<line> second "Timothy" / lookat => book { book = IITimothy; continue; }
		<line> "Titus" / lookat => book { book = Titus; continue; }
		<line> "Philemon" / lookat => book { book = Philemon; continue; }
		<line> "Hebrews" / lookat => book { book = Hebrews; continue; }
		<line> "James" / lookat => book { book = James; continue; }
		<line> first "Peter" / lookat => book { book = IPeter; continue; }
		<line> second "Peter" / lookat => book { book = IIPeter; continue; }
		<line> first "John" / lookat => book { book = IJohn; continue; }
		<line> second "John" / lookat => book { book = IIJohn; continue; }
		<line> third "John" / lookat => book { book = IIIJohn; continue; }
		<line> "Jude" / lookat => book { book = Jude; continue; }
		<line> "Revelation" / lookat => book { book = Revelation; continue; }

		/* Extract further information. */
		<book> * { why = "default unrecognized"; goto catch; }
		/* 19:15a, just ignore the a. */
		<book> ws+ @s0 natural @s1 ":" @t0 natural @t1 [ab]? {
			if(chapter || verse || verse_end)
				{ why = "reference unrecognized"; goto catch; }
			if(!helper_natural(s0, s1, &chapter)
				|| !helper_natural(t0, t1, &verse))
				{ why = "reference numerical error"; goto catch; }
			continue;
		}
		<book> "-" @s0 natural @s1 [ab]? { /* Verse range. */
			if(!chapter || !verse || verse_end)
				{ why = "range unrecognized"; goto catch; }
			if(!helper_natural(s0, s1, &verse_end))
				{ why = "range numerical error"; goto catch; }
			continue;
		}
		<book> engage => skip {
			char citestr[12];
			if(!chapter || !verse) { why = "missing information"; goto catch; }
			if(verse_end && verse_end <= verse)
				{ why = "interval error"; goto catch; }
			union kjvcite cite
				= { .book = book, .chapter = chapter, .verse = verse };
			if(!datestr[0]) date32_to_string(date, &datestr); /* Only once. */
			kjvcite_to_string(cite, &citestr);
			for( ; ; verse++, cite.verse++) {
				if(!kjv_add(kj, cite)) { why = "add to set"; goto catch; }
				if(!verse_end || verse_end <= verse) break;
			}
			printf("%s\t%zu\t%zu\t%zu\t# ",
				datestr, kj->words.verse, kj->words.set, kj->words.cumulative);
			if(verse_end) {
				printf("%s-%" PRIu32 "\n", citestr, verse_end);
			} else {
				printf("%s\n", citestr);
			}
			book = Revelation, chapter = 0, verse = 0, verse_end = 0;
			continue;
		}
	*/ }
	assert(0); /* Never gets here. */
catch:
	if(!errno) errno = EILSEQ;
	date32_to_string(date, &datestr);
	fprintf(stderr, "%s\n"
		"%s line %zu: %s.\n", buffer, datestr, line, why);
	return 0;
}

int main(void) {
	int success = EXIT_SUCCESS;
	struct journal j;
	struct journal_iterator it;
	struct kjv bible = kjv();
	union date32 k;
	const char *v;
	j = journal();
	if(!journal_is_valid(&j)) goto catch;
	fprintf(stderr, "Journal: %s.\n", journal_to_string(&j));
	printf("set term postscript eps enhanced\n"
		"set output \"kjv.eps\"\n"
		"$Data <<EOD\n"
		"# date\tverse\tset\tcumulative / %zu\n", bible.words.total);
	it = journal_begin(&j);
	while(journal_next(&it, &k, &v)) if(!scan(k, v, &bible)) goto catch;
	printf("EOD\n"
		"set monochrome\n"
		"set xdata time\n"
		"set timefmt \"%%Y-%%m-%%d\"\n"
		"set xtics format \"%%Y-%%m-%%d\" rotate by -30\n"
		"set ylabel \"words in KJV\"\n"
		"set format y \"%%g%%%%\"\n"
		"set key top left\n"
		"set grid\n"
		"unset border\n"
		"#set style fill solid 0.1 #pattern 5 (better, but restarts)\n"
		"plot $Data using 1:($3)*100/%zu with fillsteps lw 2 title \"set\", \\\n"
		"$Data using 1:($4)*100/%zu with steps lw 1 title \"cumulative\"\n",
		bible.words.total, bible.words.total);
	goto finally;
catch:
	success = EXIT_FAILURE;
	perror("journal");
finally:
	journal_(&j);
	return success;
}
