/* Given a GString containing MMD source, and optional base directory,
	substitute transclusion references in the source 

	Pass the path to the current folder if available -- should be a full path. 

	Keep track of what we're parsing to prevent recursion using stack. */
void transclude_source(GString *source, char *basedir, char *stack, int output_format, GString *manifest) {
	char *base = NULL;
	char *path = NULL;
	char *start;
	char *stop;
	char *temp;
	int curchar;
	size_t pos;
	char real[1000];
	FILE *input;
	long offset;

	if (basedir == NULL) {
		base = strdup("");
	} else {
		base = strdup(basedir);
	}

	GString *folder = NULL;
	GString *filename = NULL;
	GString *filebuffer = NULL;
	GString *stackstring = NULL;

	path = strdup(base);

	/* Look for override folder inside document */
	if (has_metadata(source->str, 0x000000)) {
		char *meta = extract_metadata_value(source->str, 0x000000, "transcludebase");
		if (meta != NULL)
			path = path_from_dir_base(base, meta);
	}

	if (path == NULL) {
		/* We have nowhere to look, so nothing to do */
		free(path);
		free(base);
		return;
	}

	folder = g_string_new(path);

	/* Ensure that folder ends in "/" */
	/* TODO: adjust for windows */
	if (!(folder->str[strlen(folder->str)-1] == '/') ) {
		g_string_append_c(folder, '/');
	}

	/* fprintf(stderr, "Transclude using '%s'\n", folder->str); */

	/* Iterate through {{foo.txt}} and substitute contents of file without metadata */

	start = strstr(source->str,"{{");

	while (start != NULL) {
		stop = strstr(start,"}}");
		if (stop == NULL)
			break;

		/* Check that we found something reasonable -- we cap at 1000 characters */
        if (stop - start < 1000) {
			strncpy(real,start+2,stop-start-2);
			real[stop-start-2] = '\0';

			if (is_separator(real[0])) {
				filename = g_string_new(real);
			} else {
				filename = g_string_new(folder->str);
				g_string_append_printf(filename, "%s",real);
			}

			if (strcmp(filename->str,"./TOC") == 0) {
				pos = stop - source->str;
				start = strstr(source->str + pos,"{{");
				g_string_free(filename, true);
				continue;
			}

			/* Adjust for wildcard extensions */
			/* But not if output_format == 0 */
			if (output_format && strncmp(&filename->str[strlen(filename->str) - 2],".*",2) == 0) {
				g_string_erase(filename, strlen(filename->str) - 2, 2);
				if (output_format == TEXT_FORMAT) {
					g_string_append(filename,".txt");
				} else if (output_format == HTML_FORMAT) {
					g_string_append(filename,".html");
				} else if (output_format == LATEX_FORMAT) {
					g_string_append(filename,".tex");
				} else if (output_format == BEAMER_FORMAT) {
					g_string_append(filename,".tex");
				} else if (output_format == MEMOIR_FORMAT) {
					g_string_append(filename,".tex");
				} else if (output_format == ODF_FORMAT) {
					g_string_append(filename,".fodt");
				} else if (output_format == OPML_FORMAT) {
					g_string_append(filename,".opml");
				} else if (output_format == LYX_FORMAT) {
					g_string_append(filename,".lyx");
				} else if (output_format == RTF_FORMAT) {
					g_string_append(filename,".rtf");
				} else {
					/* default extension -- in this case we only have 1 */
					g_string_append(filename,".txt");
				}
			}

			pos = stop - source->str;

			/* Add to the manifest (if not already included) */
			if (manifest != NULL) {
				temp = strstr(manifest->str,filename->str);

				offset = temp - manifest->str;
				if ((temp != NULL) &&
					((temp == manifest->str) || ((manifest->str)[offset - 1] == '\n')) &&
					(temp[strlen(filename->str)] == '\n') ){
					/* Already on manifest, so don't add again */
				} else {
					g_string_append_printf(manifest,"%s\n",filename->str);
				}
			}


			/* Don't reparse ourselves */
			if (stack != NULL) {
				temp = strstr(stack,filename->str);

				if ((temp != NULL) && (temp[strlen(filename->str)] == '\n')){
					start = strstr(source->str + pos,"{{");
					g_string_free(filename, true);
					continue;
				}
			}

			/* Read file */
#if defined(__WIN32)
			int wchars_num = MultiByteToWideChar(CP_UTF8, 0, filename->str, -1, NULL, 0);
			wchar_t wstr[wchars_num];
			MultiByteToWideChar(CP_UTF8, 0, filename->str, -1, wstr, wchars_num);

			if ((input = _wfopen(wstr, L"r")) != NULL ) {
#else
			if ((input = fopen(filename->str, "r")) != NULL ) {
#endif
				filebuffer = g_string_new("");

				while ((curchar = fgetc(input)) != EOF)
					g_string_append_c(filebuffer, curchar);
				
				fclose(input);

	 			pos = start - source->str;

				g_string_erase(source, pos, 2 + stop - start);

				/* Update stack list */
				stackstring = g_string_new(stack);
				g_string_append_printf(stackstring,"%s\n",filename->str);


				/* Recursively transclude files */

				/* We want to reset the base directory if we enter a subdirectory */
				char * new_dir;
				char * file_only;
				split_path_file(&new_dir, &file_only, filename->str);

				/* transclude_source(filebuffer, folder->str, stackstring->str, output_format, manifest); */
				transclude_source(filebuffer, new_dir, stackstring->str, output_format, manifest);

				free(new_dir);
				free(file_only);
				
				temp = source_without_metadata(filebuffer->str, 0x000000);

				g_string_insert(source, pos, temp);

				pos += strlen(temp);
				g_string_free(filebuffer, true);
				g_string_free(stackstring, true);
			} else {
				/* fprintf(stderr, "error opening file: %s\n", filename->str); */
			}

            g_string_free(filename, true);
        } else {
        	/* Our "match" was > 1000 characters long */
            pos = stop - source->str;
        }
		start = strstr(source->str + pos,"{{");
	}

	g_string_free(folder, true);
	free(path);
	free(base);
}

/* Allow for a footer to specify files to be appended to the end of the text, and then transcluded.
	Useful for appending a list of footnotes, citations, abbreviations, etc. to each separate file,
	but not including multiple copies when processing the master file. */
void append_mmd_footer(GString *source) {
	/* Look for mmd_footer metadata */
	if (has_metadata(source->str, 0x000000)) {
		char *meta = extract_metadata_value(source->str, 0x000000, "mmdfooter");
		if (meta != NULL)
			g_string_append_printf(source, "\n\n{{%s}}\n", meta);
	}
}

void prepend_mmd_header(GString *source) {
	/* Same thing, but to be inserted after metadata and before content */
	if (has_metadata(source->str, 0x000000)) {
		char *meta = extract_metadata_value(source->str, 0x000000, "mmdheader");
		if (meta != NULL) {
			char *content = strstr(source->str, "\n\n");
			if (content != NULL) {
				size_t pos = content - source->str;
				g_string_insert_printf(source, pos, "\n\n{{%s}}", meta);
			} else {
				g_string_append_printf(source, "\n\n{{%s}}\n", meta);
			}
		}
	}
}
int main(int argc, char * argv[]) {
	
    int numargs;            /* number of filename arguments */
    int i;

    GString *inputbuf;
    char *out;              /* string containing processed output */

    GString *file;
    char *fake;
    FILE *input;
    FILE *output;
    char curchar;
    char *progname = argv[0];

    int output_format = HTML_FORMAT;

    /* Code for command-line option parsing. */

    static gboolean opt_version = FALSE;
    static gchar *opt_output = 0;
    static gchar *opt_to = 0;
    static gboolean opt_smart = TRUE;
    static gboolean opt_no_smart = FALSE;
    static gboolean opt_notes = TRUE;
    static gboolean opt_no_notes = FALSE;
    static gboolean opt_process_html = FALSE;
    static gboolean opt_filter_html = FALSE;
    static gboolean opt_filter_styles = FALSE;
    static gboolean opt_allext = FALSE;
    static gboolean opt_compatibility = FALSE;
    static gboolean opt_batchmode = FALSE;
    static gchar *opt_extract_meta = FALSE;
    static gboolean opt_no_labels = FALSE;

	static struct option entries[] =
	{
	  MD_ARGUMENT_FLAG( "help", 'h', 1, NULL, "Show help options", NULL ),
	  MD_ARGUMENT_FLAG( "version", 'v', 1, &opt_version, "print version and exit", NULL ),
      MD_ARGUMENT_STRING( "output", 'o', &opt_output, "send output to FILE (default is stdout)", "FILE" ),
      MD_ARGUMENT_STRING( "to", 't', &opt_to, "convert to FORMAT (default is html)", "FORMAT" ),
      MD_ARGUMENT_FLAG( "extensions", 'x', 1, &opt_allext, "use all syntax extensions", NULL ),
      MD_ARGUMENT_FLAG( "filter-html", 0, 1, &opt_filter_html, "filter out raw HTML (except styles)", NULL ),
      MD_ARGUMENT_FLAG( "filter-styles", 0, 1, &opt_filter_styles, "filter out HTML styles", NULL ),
      MD_ARGUMENT_FLAG( "compatibility", 'c', 1, &opt_compatibility, "markdown compatibility mode", NULL ),
      MD_ARGUMENT_FLAG( "batch", 'b', 1, &opt_batchmode, "process multiple files automatically", NULL ),
      MD_ARGUMENT_STRING( "extract", 'e', &opt_extract_meta, "extract and display specified metadata", NULL ),
      MD_ARGUMENT_FLAG( "smart", 0, 1, &opt_smart, "use smart typography extension (on by default)", NULL ),
      MD_ARGUMENT_FLAG( "nosmart", 0, 1, &opt_no_smart, "do not use smart typography extension", NULL ),
      MD_ARGUMENT_FLAG( "notes", 0, 1, &opt_notes, "use notes extension (on by default)", NULL ),
      MD_ARGUMENT_FLAG( "nonotes", 0, 1, &opt_no_notes, "do not use notes extension", NULL ),
      MD_ARGUMENT_FLAG( "process-html", 0, 1, &opt_process_html, "process MultiMarkdown inside of raw HTML", NULL ),
      MD_ARGUMENT_FLAG( "nolabels", 0, 1, &opt_no_labels, "do not generate id attributes for headers", NULL ),
      { NULL }
    };

	char ch;
	while ((ch = getopt_long(argc, argv, "hvo:t:xcbe:", entries, NULL)) != -1) {
		 switch (ch) {
			case 'h':
				printUsage();
				return EXIT_SUCCESS;
				break;
			case 'v':
				opt_version = true;
				break;
			case 'o':
				opt_output = malloc(strlen(optarg) + 1);
				strcpy(opt_output, optarg);
				break;
			case 't':
				opt_to = malloc(strlen(optarg) + 1);
				strcpy(opt_to, optarg);
				break;
			case 'x':
				opt_allext = true;
				break;
			case 'c':
				opt_compatibility = true;
				break;
			case 'b':
				opt_batchmode = true;
				break;
			case 'e':
				opt_extract_meta = malloc(strlen(optarg) + 1);
				strcpy(opt_extract_meta, optarg);
				break;
		 }
	}

	 argc -= optind;
	 argv += optind;		 
	
	/* We expect argc and argv to still point just one below the start of remaining args */
	argc++;
	argv--;
	
    /* Process command-line options and arguments. */

    if (opt_version) {
        version(progname);
        return EXIT_SUCCESS;
    }

    extensions = 0;
    if (opt_allext)
        extensions = 0xFFFFFF;  /* turn on all extensions */
    if (opt_no_smart)
        opt_smart = FALSE;
    if (opt_smart)
        extensions = extensions | EXT_SMART;
    if (opt_no_notes)
        opt_notes = FALSE;
    if (opt_notes)
        extensions = extensions | EXT_NOTES;
    if (opt_process_html)
        extensions = extensions | EXT_PROCESS_HTML;
    if (opt_filter_html)
        extensions = extensions | EXT_FILTER_HTML;
    if (opt_filter_styles)
        extensions = extensions | EXT_FILTER_STYLES;
    if (opt_no_labels)
        extensions = extensions | EXT_NO_LABELS;

    /* Compatibility mode turns off extensions and most 
        MultiMarkdown-specific features */
    if (opt_compatibility) {
        extensions = 0x000000;
        extensions = extensions | EXT_COMPATIBILITY;
        extensions = extensions | EXT_NO_LABELS;
    }

    if (opt_to == NULL)
        output_format = HTML_FORMAT;
    else if (strcmp(opt_to, "html") == 0)
        output_format = HTML_FORMAT;
    else if (strcmp(opt_to, "latex") == 0)
        output_format = LATEX_FORMAT;
    else if (strcmp(opt_to, "memoir") == 0)
        output_format = MEMOIR_FORMAT;
    else if (strcmp(opt_to, "beamer") == 0)
        output_format = BEAMER_FORMAT;
    else if (strcmp(opt_to, "opml") == 0)
        output_format = OPML_FORMAT;
    else if (strcmp(opt_to, "odf") == 0)
        output_format = ODF_FORMAT;
    else {
        fprintf(stderr, "%s: Unknown output format '%s'\n", progname, opt_to);
        exit(EXIT_FAILURE);
    }

    numargs = argc - 1;

    if (opt_batchmode && numargs != 0) {
        /* handle each file individually, and set output to filename with
            appropriate extension */
        
           for (i = 0; i < numargs; i++) {
                inputbuf = g_string_new("");   /* string for concatenated input */
                /* Read file */
                if ((input = fopen(argv[i+1], "r")) == NULL) {
                    perror(argv[i+1]);
                    exit(EXIT_FAILURE);
                }
                while ((curchar = fgetc(input)) != EOF)
                    g_string_append_c(inputbuf, curchar);
                fclose(input);

                /* Display metadata on request */
                if (opt_extract_meta) {
                    out = extract_metadata_value(inputbuf->str, extensions, opt_extract_meta);
                    if (out != NULL) fprintf(stdout, "%s\n", out);
                    return(EXIT_SUCCESS);
                }
                
                /* remove file extension, if present */
                fake = argv[i+1];
                if (strrchr(fake, '.') != NULL) {
                    int count = strrchr(fake,'.') - fake;
                    if (count != 0) {
                        fake[count] = '\0';
                    }
                }

                file = g_string_new(fake);
                if (output_format == HTML_FORMAT) {
                    g_string_append(file,".html");
                } else if (output_format == OPML_FORMAT) {
                    g_string_append(file,".opml");
                } else if (output_format == ODF_FORMAT) {
                    g_string_append(file,".fodt");
                } else {
                    g_string_append(file,".tex");
                }

                /* open output file */
                if (!(output = fopen(file->str, "w"))) {
                    perror(opt_output);
                    return 1;
                }
               
                out = markdown_to_string(inputbuf->str, extensions, output_format);

                fprintf(output, "%s\n", out);
                fclose(output);
                g_string_free(file,true);
                free(out);
                g_string_free(inputbuf, true);
           }
        
    } else {
        /* Read input from stdin or input files into inputbuf */

        inputbuf = g_string_new("");   /* string for concatenated input */

        if (numargs == 0) {        /* use stdin if no files specified */
            while ((curchar = fgetc(stdin)) != EOF)
                g_string_append_c(inputbuf, curchar);
            fclose(stdin);
        }
        else {                  /* open all the files on command line */
           for (i = 0; i < numargs; i++) {
                if ((input = fopen(argv[i+1], "r")) == NULL) {
                    perror(argv[i+1]);
                    exit(EXIT_FAILURE);
                }
                while ((curchar = fgetc(input)) != EOF)
                    g_string_append_c(inputbuf, curchar);
                fclose(input);
           }
        }

        /* Display metadata on request */
        if (opt_extract_meta) {
            out = extract_metadata_value(inputbuf->str, extensions, opt_extract_meta);
            if (out != NULL) fprintf(stdout, "%s\n", out);
            return(EXIT_SUCCESS);
        }
        
       /* we allow "-" as a synonym for stdout here */
        if (opt_output == NULL || strcmp(opt_output, "-") == 0)
            output = stdout;
        else if (!(output = fopen(opt_output, "w"))) {
            perror(opt_output);
            return 1;
        }

        out = markdown_to_string(inputbuf->str, extensions, output_format);
        fprintf(output, "%s\n", out);
        free(out);
        fclose(output);
        g_string_free(inputbuf, true);
        
    }

    return(EXIT_SUCCESS);
}
/* Given a GString containing MMD source, and optional base directory,
	substitute transclusion references in the source 

	Pass the path to the current folder if available -- should be a full path. 

	Keep track of what we're parsing to prevent recursion using stack. */
void transclude_source(GString *source, char *basedir, char *stack, int output_format) {
	char *base = NULL;
	char *path = NULL;
	char *start;
	char *stop;
	char *temp;
	int curchar;
	size_t pos;
	char real[1000];
	FILE *input;

	if (basedir == NULL) {
		base = strdup("");
	} else {
		base = strdup(basedir);
	}

	GString *folder = NULL;
	GString *filename = NULL;
	GString *filebuffer = NULL;
	GString *stackstring = NULL;

	path = strdup(base);

	/* Look for override folder inside document */
	if (has_metadata(source->str, 0x000000)) {
		char *meta = extract_metadata_value(source->str, 0x000000, "transcludebase");
		if (meta != NULL)
			path = path_from_dir_base(base, meta);
	}

	if (path == NULL) {
		/* We have nowhere to look, so nothing to do */
		free(path);
		free(base);
		return;
	}

	folder = g_string_new(path);

	/* Ensure that folder ends in "/" */
	/* TODO: adjust for windows */
	if (!(folder->str[strlen(folder->str)-1] == '/') ) {
		g_string_append_c(folder, '/');
	}

	//fprintf(stderr, "Transclude using '%s'\n", folder->str);

	/* Iterate through {{foo.txt}} and substitute contents of file without metadata */

	start = strstr(source->str,"{{");

	while (start != NULL) {
		stop = strstr(start,"}}");
		if (stop == NULL)
			break;

		// TODO: Need to check that we found something reasonable 

		strncpy(real,start+2,stop-start-2);
		real[stop-start-2] = '\0';

		filename = g_string_new(folder->str);
		g_string_append_printf(filename, "%s",real);

		/* Adjust for wildcard extensions */
		/* But not if output_format == 0 */
		if (output_format && strncmp(&filename->str[strlen(filename->str) - 2],".*",2) == 0) {
			g_string_erase(filename, strlen(filename->str) - 2, 2);
			if (output_format == TEXT_FORMAT) {
				g_string_append(filename,".txt");
			} else if (output_format == HTML_FORMAT) {
				g_string_append(filename,".html");
			} else if (output_format == LATEX_FORMAT) {
				g_string_append(filename,".tex");
			} else if (output_format == BEAMER_FORMAT) {
				g_string_append(filename,".tex");
			} else if (output_format == MEMOIR_FORMAT) {
				g_string_append(filename,".tex");
			} else if (output_format == ODF_FORMAT) {
				g_string_append(filename,".fodt");
			} else if (output_format == OPML_FORMAT) {
				g_string_append(filename,".opml");
			} else if (output_format == LYX_FORMAT) {
				g_string_append(filename,".lyx");
			} else if (output_format == RTF_FORMAT) {
				g_string_append(filename,".rtf");
			} else {
				/* default extension -- in this case we only have 1 */
				g_string_append(filename,".txt");
			}
		}

		pos = stop - source->str;

		/* Don't reparse ourselves */
		if (stack != NULL) {
			temp = strstr(stack,filename->str);

			if ((temp != NULL) && (temp[strlen(filename->str)] == '\n')){
				start = strstr(source->str + pos,"{{");
				g_string_free(filename, true);
				continue;
			}
		}

		/* Read file */
		if ((input = fopen(filename->str, "r")) != NULL ) {
			filebuffer = g_string_new("");

			while ((curchar = fgetc(input)) != EOF)
				g_string_append_c(filebuffer, curchar);
			
			fclose(input);

 			pos = start - source->str;

			g_string_erase(source, pos, 2 + stop - start);

			/* Update stack list */
			stackstring = g_string_new(stack);
			g_string_append_printf(stackstring,"%s\n",filename->str);


			/* Recursively transclude files */
			transclude_source(filebuffer, folder->str, stackstring->str, output_format);

			temp = source_without_metadata(filebuffer->str, 0x000000);

			g_string_insert(source, pos, temp);

			pos += strlen(temp);
			g_string_free(filebuffer, true);
			g_string_free(stackstring, true);
		} else {
			/* fprintf(stderr, "error opening file: %s\n", filename->str); */
		}

		start = strstr(source->str + pos,"{{");
		g_string_free(filename, true);
	}

	g_string_free(folder, true);
	free(path);
	free(base);
}