/*
 * Copyright (c) 2026 Jonathan Schleifer <js@nil.im>
 *
 * All rights reserved.
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3.0 only,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * version 3.0 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3.0 along with this program. If not, see
 * <https://www.gnu.org/licenses/>.
 */

#import <ObjFW/ObjFW.h>

#import "G2HConverter.h"

@interface AppDelegate: OFObject <OFApplicationDelegate>
@end

OF_APPLICATION_DELEGATE(AppDelegate)

static void
help(void)
{
	[OFStdErr writeFormat:
	    @"Usage: %@ [-i file] [-o file] [--header=file] [--footer=file]\n"
	    @"       [--embeddable] [--image-extensions=ext1,ext2,...]\n"
	    @"       [--local-link-emoji=emoji] [--external-link-emoji=emoji]"
	    @"\n",
	    [OFApplication programName]];
}

@implementation AppDelegate
- (void)applicationDidFinishLaunching: (OFNotification *)notification
{
	OFString *inputPath, *outputPath, *headerPath, *footerPath;
	OFString *imageExtensions, *localLinkEmoji, *externalLinkEmoji;
	bool embeddable;
	OFOptionsParserOption options[] = {
		{ 'h', @"help", 0, NULL, NULL },
		{ 'i', @"input", 1, NULL, &inputPath },
		{ 'o', @"output", 1, NULL, &outputPath },
		{ 'e', @"embeddable", 0, &embeddable, NULL },
		{ '\0', @"header", 1, NULL, &headerPath },
		{ '\0', @"footer", 1, NULL, &footerPath },
		{ '\0', @"image-extensions", 1, NULL, &imageExtensions },
		{ '\0', @"local-link-emoji", 1, NULL, &localLinkEmoji },
		{ '\0', @"external-link-emoji", 1, NULL, &externalLinkEmoji },
		{ '\0', nil, 0, NULL, NULL }
	};
	OFOptionsParser *optionsParser =
	    [OFOptionsParser parserWithOptions: options];

	OFUnichar option;
	while ((option = [optionsParser nextOption]) != '\0') {
		switch (option) {
		case 'h':
			help();
			[OFApplication terminate];
		case ':':
			if (optionsParser.lastLongOption != nil)
				[OFStdErr writeFormat:
				    @"%@: Argument for option --%@ missing\n",
				    [OFApplication programName],
				    optionsParser.lastLongOption];
			else
				[OFStdErr writeFormat:
				    @"%@: Argument for option -%C missing\n",
				    [OFApplication programName],
				    optionsParser.lastOption];

			[OFApplication terminateWithStatus: 1];
			break;
		case '=':
			[OFStdErr writeFormat:
			    @"%@: Option --%@ takes no argument\n",
			    [OFApplication programName],
			    optionsParser.lastLongOption];

			[OFApplication terminateWithStatus: 1];
			break;
		case '?':
			if (optionsParser.lastLongOption != nil)
				[OFStdErr writeFormat:
				    @"%@: Unknown option: --%@\n",
				    [OFApplication programName],
				    optionsParser.lastLongOption];
			else
				[OFStdErr writeFormat:
				    @"%@: Unknown option: -%C\n",
				    [OFApplication programName],
				    optionsParser.lastOption];

			[OFApplication terminateWithStatus: 1];
			break;
		}
	}

	if (optionsParser.remainingArguments.count > 0) {
		help();
		[OFApplication terminateWithStatus: 1];
	}

	G2HConverter *converter = [G2HConverter converter];
	if (imageExtensions != nil)
		converter.imageExtensions = [OFSet setWithArray:
		    [imageExtensions componentsSeparatedByString: @","]];
	if (localLinkEmoji != nil)
		converter.localLinkEmoji = localLinkEmoji;
	if (externalLinkEmoji != nil)
		converter.externalLinkEmoji = externalLinkEmoji;

	OFStream *input = OFStdIn, *output = OFStdOut;
	if (inputPath != nil)
		input = [OFFile fileWithPath: inputPath mode: @"r"];
	if (outputPath != nil)
		output = [OFFile fileWithPath: outputPath mode: @"w"];

	if (headerPath != nil) {
		OFData *header = [OFData dataWithContentsOfFile: headerPath];
		[output writeData: header];
	}

	OFString *firstLine = [input readLine];
	OFString *title;
	if ([firstLine hasPrefix: @"#"] && ![firstLine hasPrefix: @"##"])
		title = [firstLine substringFromIndex: 1];
	else
		title = inputPath.lastPathComponent;

	if (title == nil)
		title = @"Untitled";

	if (headerPath == nil && !embeddable)
		[output writeFormat: @"<!DOCTYPE html>\n"
				     @"<html>\n"
				     @"<head>\n"
				     @"<title>%@</title>\n"
				     @"</head>\n"
				     @"<body>\n",
				     title.stringByXMLEscaping];

	if (firstLine != nil)
		[output writeLine: [converter processLine: firstLine]];

	OFString *line;
	while ((line = [input readLine]) != nil)
		[output writeLine: [converter processLine: line]];

	if ((line = [converter finishProcessing]).length > 0)
		[output writeLine: line];

	if (footerPath != nil) {
		OFData *footer = [OFData dataWithContentsOfFile: footerPath];
		[output writeData: footer];
	}

	if (footerPath == nil && !embeddable)
		[output writeString: @"</body>\n"
				     @"</html>\n"];

	[OFApplication terminate];
}
@end
