//
//  XTHtmlTagContainer.m
//  XTads
//
//  Created by Rune Berg on 10/05/2018.
//  Copyright © 2018 Rune Berg. All rights reserved.
//

#import "XTHtmlTag_private.h"
#import "XTHtmlTagContainer.h"
#import "XTHtmlTagContainer_private.h"
#import "XTHtmlTagSecondOutermost.h"
#import "XTOutputTextParserHtml.h"
#import "XTLogger.h"
#import "XTHtmlTagCenter.h"
#import "XTHtmlUtils.h"


@implementation XTHtmlTagContainer

static XTLogger* logger;

@synthesize cachedIsEnclosedByListOrListItem = _cachedIsEnclosedByListOrListItem;

@synthesize hasFormattedExit = _hasFormattedExit;

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

- (instancetype)init
{
	self = [super init];
	if (self) {
		_contents = [NSMutableArray arrayWithCapacity:10];
		_formattingSpecForHtmlTag = [XTFormattingSpecificationForHtmlTag new];
		_closed = NO;
		_cachedIsEnclosedByListOrListItem = nil;
		_hasFormattedExit = NO;
	}
	return self;
}

- (void)onParsing:(NSObject<XTOutputTextParserProtocol> *)parser
{
	[parser appendTagToCurrentContainer:self];
		//TODO !!! reconsider vs html tads
	[parser pushContainer:self];
}

// see htmltags.cpp, CHtmlTagContainer::get_next_fmt_tag
- (XTHtmlTag *)getNextTagToFormat:(NSObject<XTOutputFormatterProtocol> *)formatter
					  textHandler:(XTBaseTextHandler *)textHandler
					 shouldFormat:(BOOL)shouldFormat
{
	[self assertHasContainer];

	/*
	 *   if I have a child, return it; otherwise, return the next sibling
	 *   or container's sibling (etc) as though we were an ordinary tag
	 */
	if (self.contents.firstObject != nil) {
		/* return my first child */
		return self.contents.firstObject;
	} else {
		//TODO !!! write test game for this case
		/*
		 *   I have no children.  Call my format_exit() method right now.
		 *   Normally, the last child would call this on the way out; since
		 *   we have no children, though, no one would otherwise call this.
		 *   Note that, as usual, we only want to call format_exit() if the
		 *   tag is finished (i.e., closed).
		 */
		if (self.closed) {
			//if (! self.hasFormatted) { //TODO exp rm for size_unspec/10b etc. bugs
			if (shouldFormat) {
				[self formatExit:formatter textHandler:textHandler];
			}
			//}
		}
		
		/*
		 *   get the next item using the non-container algorithm; this
		 *   will find our container's next sibling, or its container's
		 *   next sibling, and so on, until we find a container with a
		 *   sibling or run out of containers
		 */
		return [super getNextTagToFormat:formatter textHandler:textHandler shouldFormat:shouldFormat];
	}
}

- (void)format:(NSObject<XTOutputFormatterProtocol> *)formatter
   textHandler:(XTBaseTextHandler *)textHandler
{
	[self.formattingSpecForHtmlTag formattingEntry:formatter tagContainer:self];
}

- (void)appendToContents:(XTHtmlTag *)tag
{
	XTHtmlTag *lastInContents = self.contents.lastObject;
	if (lastInContents != nil) {
		lastInContents.nextSibling = tag;
	}
	[self.contents addObject:tag];
	tag.container = self;
}

- (void)removeFromContents:(XTHtmlTag *)tag
{
	//XT_DEF_SELNAME;
	//XT_WARN_2(@"of %@ : %@", self.name, tag.name);

	NSUInteger idxOfTag = [self.contents indexOfObject:tag];

	if (idxOfTag != NSNotFound) {
		if (idxOfTag >= 1) {
			XTHtmlTag *tagBefore = [self.contents objectAtIndex:(idxOfTag - 1)];
			tagBefore.nextSibling = tag.nextSibling;
		}
		[self.contents removeObjectAtIndex:idxOfTag];
	} else {
		XT_DEF_SELNAME;
		XT_WARN_1(@"contents don't have tag %@", tag.name);
	}
}

- (BOOL)hasContents
{
	BOOL res = (self.contents.count >= 1);
	return res;
}

//TODO !!! override as in htmltags.h
- (void) preClose:(NSObject<XTOutputTextParserProtocol> *)parser
{
	// Nothing by default
}

/*
 *   When we reach the end of a container, the parser will call on_close
 *   on the current container to let it know that it's been closed.
 *   Note that we don't need to pop the container off the parser stack,
 *   as the parser will always do this automatically.  Note also that
 *   this is called while we're still the active container.
 */
- (void)onClose:(NSObject<XTOutputTextParserProtocol> *)parser
{
	self.closed = YES;
}

/*
 *   receive notification that we've just finished closing the tag; the
 *   immediate container is now re-established as the active container
 */
- (void)postClose:(NSObject<XTOutputTextParserProtocol> *)parser
{
	// Nothing to do by default
}

- (void)checkNotHasFormatted
{
	// Nothing - allow re-traversal for "dangling" open containers
}

- (void)formatExit:(NSObject<XTOutputFormatterProtocol> *)formatter
	   textHandler:(XTBaseTextHandler *)textHandler
{
	[self.formattingSpecForHtmlTag formattingExit:formatter];
	[self postFormatForBlockLevel:formatter textHandler:textHandler];
	[self removeChildren];
	_hasFormattedExit = YES;
}

- (void)removeChildren
{
	NSMutableArray *childrenToRemove = [NSMutableArray array];
	
	NSUInteger numObjsToRemove = self.contents.count;
	
	if (numObjsToRemove >= 1) {
		// Make sure each child's container is set to nil
		for (id obj in self.contents) {
			if ([obj isKindOfClass:[XTHtmlTag class]]) {
				XTHtmlTag *tag = (XTHtmlTag *)obj;
				[childrenToRemove addObject:tag];
			} else {
				int brkpt = 1;
			}
		}
		
		for (XTHtmlTag *tag in childrenToRemove) {
			[tag removeFromContainer];
		}
		
		[self.contents removeAllObjects];
				
		//XT_DEF_SELNAME;		
		//XT_WARN_3(@"removed all %lu children from contents of <%@> (uid=%lu)", numObjsToRemove, self.name, self.uniqueId);
	}
}

- (void)removeChildrenUntilFirstOpenContainer
{
	NSMutableArray *childrenToRemove = [NSMutableArray array];
	
	for (id obj in self.contents) {
		if ([obj isKindOfClass:[XTHtmlTagContainer class]]) {
			XTHtmlTagContainer *container = (XTHtmlTagContainer *)obj;
			if (! container.closed) {
				break;
			}
		}
		if ([obj isKindOfClass:[XTHtmlTag class]]) {
			if (! [obj isKindOfClass:[XTHtmlTagSecondOutermost class]]) {
				XTHtmlTag *tag = (XTHtmlTag *)obj;
				[childrenToRemove addObject:tag];
			}
		}
	}

	for (XTHtmlTag *tag in childrenToRemove) {
		[tag removeFromContainer];
	}
}

- (BOOL)isInList
{
	BOOL res;
	if (self.cachedIsEnclosedByListOrListItem != nil) {
		res = [self.cachedIsEnclosedByListOrListItem boolValue];
	} else {
		res = [XTHtmlUtils tagIsEnclosedByListOrListItem:self];
		_cachedIsEnclosedByListOrListItem = [NSNumber numberWithBool:res];
	}
	return res;
}

- (NSUInteger)recursivelyCountChildren
{
	NSUInteger res = 1; // self
	if (self.contents != nil) {
		for (XTHtmlTag *tag in self.contents) {
			res += [tag recursivelyCountChildren];
		}
	}
	return res;
}

@end
