//
//  XTTabStopModel.m
//  XTads
//
//  Created by Rune Berg on 06/02/2017.
//  Copyright © 2017 Rune Berg. All rights reserved.
//

#import "XTTabStopModel.h"
#import "XTTabStopModelEntry.h"
#import "XTStringUtils.h"
#import "XTConverter.h"
#import "XTLogger.h"
#import "XTAllocDeallocCounter.h"


@interface XTTabStopModel ()

@property XTConverter *converter;
@property NSMutableArray *entriesWithId; // of XTTabStopModelEntry, ordered by entries' position

@end


@implementation XTTabStopModel

static XTLogger* logger;

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

OVERRIDE_ALLOC_FOR_COUNTER
OVERRIDE_DEALLOC_FOR_COUNTER

- (id)init
{
	self = [super init];
	if (self) {
		_converter = [XTConverter converter];
		[self reset];
	}
	return self;
}

- (NSUInteger)count
{
	return self.entriesWithId.count;
}

- (XTTabStopModelEntry *)entryAtIndex:(NSUInteger)index
{
	XT_DEF_SELNAME;

	XTTabStopModelEntry *entry = nil;
	if (index < self.count) {
		entry = [self.entriesWithId objectAtIndex:index];
	} else {
		XT_ERROR_1("no entry at index %lu", index);
	}
	
	return entry;
}

- (void)reset
{
	[_entriesWithId removeAllObjects];
	_entriesWithId = [NSMutableArray arrayWithCapacity:30];
}

- (void)addTabStopWithId:(NSString *)ident
				position:(CGFloat)position
			   alignment:(NSString *)alignment
			 decimalChar:(NSString *)decimalChar
{
	XT_DEF_SELNAME;
	
	if ([XTStringUtils isEmptyOrNull:ident]) {
		XT_ERROR_0(@"id is empty or null");
		return;
	}

	if (position < 0.0) {
		XT_ERROR_0(@"position is a negative number");
		return;
	}

	NSNumber *posWrapped = [NSNumber numberWithDouble:position];
	NSNumber *alignWrapped = [self wrappedAlignmentFromString:alignment];
	
	XTTabStopModelEntry *newEntry = [XTTabStopModelEntry new];
	newEntry.ident = ident;
	newEntry.position = posWrapped;
	newEntry.alignment = alignWrapped;
	newEntry.decimalChar = decimalChar;
	
	// remove any existing entry with new entry's id
	for (XTTabStopModelEntry *existingEntry in self.entriesWithId) {
		if ([self ident:newEntry.ident isSameAsIdent:existingEntry.ident]) {
			[self.entriesWithId removeObject:existingEntry];
			break;
		}
	}
	
	NSUInteger idxNewEntry = 0;
	for (XTTabStopModelEntry *entry in self.entriesWithId) {
		if (position >= entry.position.doubleValue) {
			idxNewEntry += 1;
		} else {
			break;
		}
	}
	[self.entriesWithId insertObject:newEntry atIndex:idxNewEntry];
	//XT_WARN_3(@"%@=%lf at idx %lu", ident, position, idxNewEntry);
}

- (XTTabStopModelEntry *)findTabWithId:(NSString *)toId;
{
	//XT_DEF_SELNAME;
	
	XTTabStopModelEntry *res = nil;
	
	for (XTTabStopModelEntry *entry in self.entriesWithId) {
		if ([self ident:toId isSameAsIdent:entry.ident]) {
			res = entry;
			break;
		}
	}
	
	return res;
}

//TODO mv
//TODO call XTStringUtils for impl
- (BOOL)ident:(NSString *)id1 isSameAsIdent:(NSString *)id2
{
	NSString *id1Lc = [id1 lowercaseString];
	NSString *id2Lc = [id2 lowercaseString];
	BOOL res = [id1Lc isEqualToString:id2Lc];
	return res;
}

- (CGFloat)findPositionOfNextTabWithMultiple:(NSNumber *)multipleObj
								fromPosition:(CGFloat)position
								enSpaceWidth:(CGFloat)enSpaceWidth {

	//TODO test ill-formed multiple?
	//TODO handle too-large value?

	NSUInteger multiple = 1;
	if (multipleObj != nil) {
		multiple = multipleObj.unsignedIntegerValue;
		if (multiple < 1) {
			multiple = 1;
		}
	}
	
	CGFloat multipleWidth = multiple * enSpaceWidth;
	NSUInteger multiplesUsed = position / multipleWidth;
	CGFloat ptsIntoMultiple = position - (multiplesUsed * multipleWidth);
	CGFloat ptsToNextMultiple = multipleWidth - ptsIntoMultiple;
	
	// Avoid tabs that have very little visible width:
	if (ptsToNextMultiple <= (enSpaceWidth / 2.0 /*4.0*/)) {
		ptsToNextMultiple += multipleWidth;
	}
	
	CGFloat res = position + ptsToNextMultiple;
	return res;
}

- (XTTabStopModelEntry *)createOverridenTabStop:(XTTabStopModelEntry *)tabStopModelEntry
										  align:(NSString *)alignment
									decimalChar:(NSString *)decimalChar
{
	XTTabStopModelEntry *res = [tabStopModelEntry copy];

	// override:
	res.alignment = [self wrappedAlignmentFromString:alignment];
	if (decimalChar != nil) {
		res.decimalChar = decimalChar;
	}
	
	return res;
}

//--------

- (NSNumber *)wrappedAlignmentFromString:(NSString *)alignment
{
	XTTabStopAlignment alignEnum = [self alignmentFromString:alignment];	
	NSNumber *res = [NSNumber numberWithInteger:alignEnum];	
	return res;
}

- (XTTabStopAlignment)alignmentFromString:(NSString *)alignment
{
	XT_DEF_SELNAME;
	
	XTTabStopAlignment alignEnum = XTTABSTOP_ALIGN_LEFT;
	
	if (alignment != nil) {
		alignment = [alignment lowercaseString];
		if ([alignment isEqualToString:@"left"]) {
			alignEnum = XTTABSTOP_ALIGN_LEFT;
		} else if ([alignment isEqualToString:@"center"]) {
			alignEnum = XTTABSTOP_ALIGN_CENTER;
		} else if ([alignment isEqualToString:@"right"]) {
			alignEnum = XTTABSTOP_ALIGN_RIGHT;
		} else if ([alignment isEqualToString:@"decimal"]) {
			alignEnum = XTTABSTOP_ALIGN_DEC_POINT;
		} else {
			XT_TRACE_1(@"unknown alignment \"%@\" - using left", alignment);
		}
	}
	
	return alignEnum;
}

@end
