//
//  XTHtmlLinebreakHandler2.m
//  TadsTerp
//
//  Created by Rune Berg on 11/05/14.
//  Copyright (c) 2014 Rune Berg. All rights reserved.
//

#import "XTHtmlLinebreakHandler2.h"
#import "XTHtmlTagBr.h"
#import "XTHtmlTagP.h"
#import "XTHtmlTagTab.h"
#import "XTStringUtils.h"
#import "XTLogger.h"
#import "XTAllocDeallocCounter.h"


@implementation XTHtmlLinebreakHandler2

static XTLogger* logger;
static NSArray *newlinesStringsCache;

+ (void)initialize
{
	logger = [XTLogger loggerForClass:[XTHtmlLinebreakHandler2 class]];
	newlinesStringsCache = @[
		@"",
		@"\n",
		@"\n\n",
		@"\n\n\n",
		@"\n\n\n\n"
		];
}

OVERRIDE_ALLOC_FOR_COUNTER
OVERRIDE_DEALLOC_FOR_COUNTER

- (id)init
{
    self = [super init];
    if (self) {
		_state = XT_LINEBREAKHANDLER2_AT_START_OF_LINE;
    }
    return self;
}

- (void)reset
{
	_state = XT_LINEBREAKHANDLER2_AT_START_OF_LINE;
}

- (void)resetForNextCommand
{
	_state = XT_LINEBREAKHANDLER2_AT_START_OF_LINE;
}

- (NSString *)handleTagBr:(NSInteger)height
{
	if (height < 0) {
		// <br> == <br height=1>
		height = 1;
	}
	
	//[logger warn:@"handleTagBr enter state=%@ height=%ld", [self stateAsString], height];
	
	NSUInteger numberOfNewlines;
	XTHtmlLinebreakHandler2State oldState = self.state;
	
	switch (self.state) {
		case XT_LINEBREAKHANDLER2_AT_START_OF_LINE:
			// fall through
		case XT_LINEBREAKHANDLER2_AT_START_OF_TABLE_CELL:
			//TODO simplify:
			if (height == 0) {
				numberOfNewlines = 0;
			} else if (height == 1) {
				numberOfNewlines = 1;
			} else {
				numberOfNewlines = height;
			}
			break;
		case XT_LINEBREAKHANDLER2_AFTER_TAB_START_OF_LINE:
			if (height <= 1) {
				numberOfNewlines = 0;
			} else {
				numberOfNewlines = height + 1;
			}
			break;
		case XT_LINEBREAKHANDLER2_IN_TEXT:
			if (height == 0) {
				numberOfNewlines = 1;
			} else if (height == 1) {
				numberOfNewlines = 1;
			} else {
				numberOfNewlines = height + 1;
			}
			break;
		default: {
			XT_DEF_SELNAME;
			XT_ERROR_1(@"handleTagBr unexpected state %d", self.state);
			//TODO handle error
			break;
		}
	}

	if (oldState == XT_LINEBREAKHANDLER2_AT_START_OF_TABLE_CELL && numberOfNewlines == 0) {
		_state = oldState;
	} else {
		_state = XT_LINEBREAKHANDLER2_AT_START_OF_LINE;
	}

	//[logger warn:@"handleTagBr exit state=%@ numberOfNewlines=%ld", [self stateAsString], numberOfNewlines];
	return [self stringOfNewlines:numberOfNewlines];
}

- (void)handleTagPOpen
{
	if (_state == XT_LINEBREAKHANDLER2_AT_START_OF_TABLE_CELL) {
		_state = XT_LINEBREAKHANDLER2_AT_START_OF_LINE;
	}
}

- (NSString *)handleTagPClose
{
	return nil;
}

- (NSString *)handleTagTab
{
	switch (self.state) {
		case XT_LINEBREAKHANDLER2_AT_START_OF_LINE:
			_state = XT_LINEBREAKHANDLER2_AFTER_TAB_START_OF_LINE;
			break;
		case XT_LINEBREAKHANDLER2_IN_TEXT:
			// stay in current state
			break;
		case XT_LINEBREAKHANDLER2_AFTER_TAB_START_OF_LINE:
			// stay in current state
			break;
		case XT_LINEBREAKHANDLER2_AT_START_OF_TABLE_CELL:
			_state = XT_LINEBREAKHANDLER2_AFTER_TAB_START_OF_LINE;
				//TODO !!! exp. need new state?
			break;
		default: {
			XT_DEF_SELNAME;
			XT_ERROR_1(@"handleTagTab unexpected state %d", self.state);
			//TODO handle error
			break;
		}
	}
	//TODO rm return type? no - defer space for tabs... see gold.gam initial loc
	return @"";
}

- (void)handleText:(NSString *)text
{
	if (text != nil && text.length >= 1) {
		BOOL endsWithNewline = [text hasSuffix:@"\n"];
		if (endsWithNewline) {
			_state = XT_LINEBREAKHANDLER2_AT_START_OF_LINE;
		} else {
			BOOL endsWithNewlineZwsp = [text hasSuffix:@"\n" ZERO_WIDTH_SPACE];
			if (! endsWithNewlineZwsp) {
				_state = XT_LINEBREAKHANDLER2_IN_TEXT;
			} else {
				int brkpt = 1;
			}
		}
	}
}

//TODO missing from diagram
//TODO better name
//TODO distinguish between open and close?
- (NSString *)handleBlockLevelNewline:(XTHtmlTag *)tag
{
	NSString *res = nil;
	
	if ([tag isBlockLevel]) {
		if (_state == XT_LINEBREAKHANDLER2_IN_TEXT) {
			res = @"\n";
		}
		if (_state != XT_LINEBREAKHANDLER2_AT_START_OF_TABLE_CELL) {
			_state = XT_LINEBREAKHANDLER2_AT_START_OF_LINE;
		}
	} else {
		//TODO log error
	}
	
	return res;
}

//TODO missing from diagram
- (void)handleTableClose
{
	// A </table> starts a new "line"
	_state = XT_LINEBREAKHANDLER2_AT_START_OF_LINE;
}

//TODO missing from diagram
- (void)handleTdOpen
{
	// A table cell <td> starts its own "line"
	_state = XT_LINEBREAKHANDLER2_AT_START_OF_TABLE_CELL;
}

- (NSString *)handleTdClose
{
	//TODO !!! clean up - always return \n if that's what it comes down to
	NSString *endOfPara;
	if (_state == XT_LINEBREAKHANDLER2_AT_START_OF_TABLE_CELL ||
		_state == XT_LINEBREAKHANDLER2_IN_TEXT ||
		_state == XT_LINEBREAKHANDLER2_AFTER_TAB_START_OF_LINE) { 
		endOfPara = @"\n";
	} else if (_state == XT_LINEBREAKHANDLER2_AT_START_OF_LINE) {
		//TODO exp for table_size_unspec 13b bug: endOfPara = @"\n";
		endOfPara = nil;
	} else {
		endOfPara = nil;
	}
	
	// No state change
	
	return endOfPara;
}

- (BOOL)isAtStartOfLine
{
	BOOL res = (_state == XT_LINEBREAKHANDLER2_AT_START_OF_LINE);
	return res;
}

//-------------------------------

- (NSString *)stringOfNewlines:(NSUInteger)numberOfNewlines
{
	NSString *res;
	if (numberOfNewlines < newlinesStringsCache.count) {
		res = newlinesStringsCache[numberOfNewlines];
	} else {
		NSMutableString *tempRes = [NSMutableString string];
		while (numberOfNewlines-- > 0) {
			[tempRes appendString:@"\n"];
		}
		res = tempRes;
	}
	return res;
}

- (void)setState:(XTHtmlLinebreakHandler2State)state
{
	_state = state;
}

- (NSString *)stateAsString
{
	NSString *res;
	
	switch (self.state) {
		case XT_LINEBREAKHANDLER2_AT_START_OF_LINE:
			res = @"AT_START_OF_LINE";
			break;
		case XT_LINEBREAKHANDLER2_AT_START_OF_TABLE_CELL:
			res = @"AT_START_OF_TABLE_CELL";
			break;
		case XT_LINEBREAKHANDLER2_AFTER_TAB_START_OF_LINE:
			res = @"AFTER_TAB_START_OF_LINE";
			break;
		case XT_LINEBREAKHANDLER2_IN_TEXT:
			res = @"IN_TEXT";
			break;
		default:
			res = @"(unknown)";
			break;
	}

	return res;
}

@end
