//
//  XTOutputTextParserPlain.m
//  TadsTerp
//
//  Created by Rune Berg on 28/04/14.
//  Copyright (c) 2014 Rune Berg. All rights reserved.
//

#import "XTOutputTextParserPlain.h"
#import "XTHtmlTagQuestionMarkT2.h"
#import "XTHtmlTagQuestionMarkT3.h"
#import "XTLogger.h"


@interface XTOutputTextParserPlain ()

@property NSMutableString *regularTextBuffer;
@property NSMutableString *tagTextBuffer;

@property NSCharacterSet *whitespaceCharSet;

@end


@implementation XTOutputTextParserPlain

static XTLogger* logger;

#define CLEAR_REGULAR_TEXT [self.regularTextBuffer setString:@""]

#define CLEAR_TAG_TEXT [self.tagTextBuffer setString:@""]
#define BUFFER_TAG_TEXT [self.tagTextBuffer appendString:[NSString stringWithCharacters:&ch length:1]]

// For "unexpected" char in markup we simply treat the markup as plain text
#define HANDLE_UNEXPECTED_CHAR_IN_MARKUP \
	[self addRegularTextElementsFor:self.tagTextBuffer toArray:res]; \
	CLEAR_REGULAR_TEXT; \
	CLEAR_TAG_TEXT; \
	self.state = XT_PLAIN_INITIAL;

+ (void)initialize
{
	logger = [XTLogger loggerForClass:[XTOutputTextParserPlain class]];
}

- (id)init
{
    self = [super init];
    if (self) {
		_whitespaceCharSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
		_state = XT_PLAIN_INITIAL;
		_regularTextBuffer = [NSMutableString stringWithCapacity:100];
		_tagTextBuffer = [NSMutableString stringWithCapacity:100];
    }
    return self;
}

- (void)resetForNextCommand
{
	self.state = XT_PLAIN_INITIAL;
	CLEAR_REGULAR_TEXT;
	CLEAR_TAG_TEXT;
}

- (NSArray *)parse:(NSString *)string
{
	XT_DEF_SELNAME;
	XT_TRACE_1(@"\"%@\"", string);
	
	NSMutableArray *res = [NSMutableArray arrayWithCapacity:5];
	
	for (NSUInteger index = 0; index < string.length; index++) {
		const unichar ch = [string characterAtIndex:index];
		switch (self.state) {
			case XT_PLAIN_INITIAL:
				if (ch == '<') {
					[self addRegularTextElementsFor:self.regularTextBuffer toArray:res];
					CLEAR_REGULAR_TEXT;
					CLEAR_TAG_TEXT;
					BUFFER_TAG_TEXT;
					self.state = XT_PLAIN_AFTER_TAG_START;
				} else {
					[self.regularTextBuffer appendString:[NSString stringWithCharacters:&ch length:1]];
				}
				break;
			case XT_PLAIN_AFTER_TAG_START:
				BUFFER_TAG_TEXT;
				if (ch == '?') {
					self.state = XT_PLAIN_AFTER_Q_MARK;
				} else {
					HANDLE_UNEXPECTED_CHAR_IN_MARKUP;
				}
				break;
			case XT_PLAIN_AFTER_Q_MARK:
				BUFFER_TAG_TEXT;
				if (ch == 't' || ch == 'T') {
					self.state = XT_PLAIN_AFTER_T;
				} else {
					HANDLE_UNEXPECTED_CHAR_IN_MARKUP;
				}
				break;
			case XT_PLAIN_AFTER_T:
				BUFFER_TAG_TEXT;
				if (ch == '2') {
					self.state = XT_PLAIN_AFTER_2;
				} else if (ch == '3') {
					self.state = XT_PLAIN_AFTER_3;
				} else {
					HANDLE_UNEXPECTED_CHAR_IN_MARKUP;
				}
				break;
			case XT_PLAIN_AFTER_2:
				BUFFER_TAG_TEXT;
				if (ch == '>') {
					[res addObject:[XTHtmlTagQuestionMarkT2 new]];
					CLEAR_TAG_TEXT;
					self.state = XT_PLAIN_INITIAL;
				} else if ([self.whitespaceCharSet characterIsMember:ch]) {
					//self.state = AFTER_2;
				} else {
					HANDLE_UNEXPECTED_CHAR_IN_MARKUP;
				}
				break;
			case XT_PLAIN_AFTER_3:
				BUFFER_TAG_TEXT;
				if (ch == '>') {
					[res addObject:[XTHtmlTagQuestionMarkT3 new]];
					CLEAR_TAG_TEXT;
					self.state = XT_PLAIN_INITIAL;
				} else if ([self.whitespaceCharSet characterIsMember:ch]) {
					//self.state = AFTER_3;
				} else {
					HANDLE_UNEXPECTED_CHAR_IN_MARKUP;
				}
				break;
			default:
				// This shouldn't happen - reset everything
				XT_ERROR_1(@"*** BUG! Unexpected state %d - reset all parser states and buffers", self.state);
				HANDLE_UNEXPECTED_CHAR_IN_MARKUP;
				break;
		}
	}
	
	[self addRegularTextElementsFor:self.regularTextBuffer toArray:res];
	CLEAR_REGULAR_TEXT;
	
	return res;
}

- (NSArray *)flush
{
	XT_DEF_SELNAME;
	
	NSMutableArray *res = [NSMutableArray arrayWithCapacity:5];

	[self addRegularTextElementsFor:self.regularTextBuffer toArray:res];
	CLEAR_REGULAR_TEXT;
	
	/* EXP: do nothing, as per mjr's advice 2015-07-01
	if (self.tagTextBuffer.length >= 1) {
		XT_WARN_2(@"(state %d) flushing incomplete HTML tag: \"%@\"", self.state, self.tagTextBuffer);
		[self addRegularTextElement:self.tagTextBuffer toArray:res];
		//CLEAR_REGULAR_TEXT;
		CLEAR_TAG_TEXT;
	}*/
	
	return res;
}

- (void)addRegularTextElementsFor:(NSString *)string toArray:(NSMutableArray *)array
{
	if (string == nil || string.length == 0) {
		return;
	}
	
	NSMutableString *workBuffer = [NSMutableString string];
	NSUInteger stringLen = string.length;
	BOOL inRegularText = YES; // NO means: in whitespace except newline
	
	for (NSUInteger idx = 0; idx < stringLen; idx++) {
		unichar ch = [string characterAtIndex:idx];
		
		if (ch == '\n') {
			
			if (workBuffer.length > 0) {
				[array addObject:workBuffer];
				workBuffer = [NSMutableString string];
			}
			[array addObject:@"\n"];
			inRegularText = YES;
			
		} else if ([self.whitespaceCharSet characterIsMember:ch]) {
			
			if (inRegularText) {
				if (workBuffer.length > 0) {
					[array addObject:workBuffer];
					workBuffer = [NSMutableString string];
				}
				inRegularText = NO;
			}
			[workBuffer appendString:[NSString stringWithCharacters:&ch length:1]];
			
		} else {

			if (! inRegularText) {
				if (workBuffer.length > 0) {
					[array addObject:workBuffer];
					workBuffer = [NSMutableString string];
				}
				inRegularText = YES;
			}
			[workBuffer appendString:[NSString stringWithCharacters:&ch length:1]];		}
	}
	if (workBuffer.length > 0) {
		[array addObject:workBuffer];
		workBuffer = [NSMutableString string];
	}
}

@end

