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

#import "XTOutputTextParserBase_private.h"
#import "XTOutputTextParserPlain.h"
#import "XTHtmlTagQuestionMarkT2.h"
#import "XTHtmlTagQuestionMarkT3.h"
#import "XTHtmlTagTab.h"
#import "XTHtmlTagText.h"
#import "XTHtmlTagOutermost.h"
#import "XTHtmlTagSecondOutermost.h"
#import "XTLogger.h"
#import "XTAllocDeallocCounter.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]; \
	CLEAR_REGULAR_TEXT; \
	CLEAR_TAG_TEXT; \
	self.state = XT_PLAIN_INITIAL;

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

OVERRIDE_ALLOC_FOR_COUNTER
OVERRIDE_DEALLOC_FOR_COUNTER

- (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)teardown
{
	self.whitespaceCharSet = nil;
	self.regularTextBuffer = nil;
	self.tagTextBuffer = nil;
}


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

- (XTHtmlTagContainer *)getOuterContainer
{
	return self.containerStack.bottom;
}

- (void)parse:(NSString *)string
{
	// Note: Breaking up string into whitespace and text parts is needed because of
	// the way XTads does pagination: need to do fine-grained backtracking when pagination limit is reached.
	
	XT_DEF_SELNAME;
	XT_TRACE_1(@"\"%@\"", string);
	
	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];
					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 == '>') {
					XTHtmlTagQuestionMarkT2 *tag = [XTHtmlTagQuestionMarkT2 new];
					[tag onParsing:self];
					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 == '>') {
					XTHtmlTagQuestionMarkT3 *tag = [XTHtmlTagQuestionMarkT3 new];
					[tag onParsing:self];
					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];
	CLEAR_REGULAR_TEXT;
}

- (void)flush
{
	[self addRegularTextElementsFor:self.regularTextBuffer];
	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;
	}*/
}

- (void)hardFlush
{
	[self flush];
}

- (void)endNormalTag:(XTHtmlTag *)tag
{
	XT_WARN_ENTRY;  // this should never be called for plain-text games

}

- (void)addRegularTextElementsFor:(NSString *)string
{
	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) {
				[self addTagText:workBuffer];
				workBuffer = [NSMutableString string];
			}
			[self addTagText:@"\n"];
			inRegularText = YES;
			
		} else if (ch == '\t') {
			if (workBuffer.length > 0) {
				[self addTagText:workBuffer];
				workBuffer = [NSMutableString string];
			}
			XTHtmlTagTab *tabTag = [XTHtmlTagTab forPlainTextMode];
			//TODO no, make separate formatting object? or handle XTHtmlTagTab for plain text mode?
			[tabTag onParsing:self];
			inRegularText = NO;
		
		} else if ([self.whitespaceCharSet characterIsMember:ch]) {
			
			if (inRegularText) {
				if (workBuffer.length > 0) {
					[self addTagText:workBuffer];
					workBuffer = [NSMutableString string];
				}
				inRegularText = NO;
			}
			[workBuffer appendString:[NSString stringWithCharacters:&ch length:1]];
			
		} else {

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

- (void)addTagText:(NSString *)text
{
	if (text != nil && text.length >= 1) {
		XTHtmlTagText *tag = [XTHtmlTagText tagWithText:text];
		[tag onParsing:self];
	}
}

@end

