//The MIT License (MIT)
//
//Copyright (c) 2014 RafaƂ Augustyniak
//
//Permission is hereby granted, free of charge, to any person obtaining a copy of
//this software and associated documentation files (the "Software"), to deal in
//the Software without restriction, including without limitation the rights to
//use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
//the Software, and to permit persons to whom the Software is furnished to do so,
//subject to the following conditions:
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
//FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
//COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
//IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
//CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//


#import "RATreeNodeCollectionController.h"
#import "RATreeNodeController.h"

#import "RATreeNode.h"
#import "RATreeNodeItem+Private.h"

#import "RABatchChanges.h"


@interface RATreeNodeCollectionController () <RATreeNodeItemDataSource>

@property (nonatomic, strong) RATreeNodeController *rootController;

@end


@implementation RATreeNodeCollectionController

- (NSInteger)numberOfVisibleRowsForItems
{
  return self.rootController.numberOfVisibleDescendants;
}

- (RATreeNode *)treeNodeForIndex:(NSInteger)index
{
  return [self.rootController controllerForIndex:index].treeNode;
}

- (NSInteger)indexForItem:(id)item
{
  return [self.rootController indexForItem:item];
}

- (NSInteger)lastVisibleDescendantIndexForItem:(id)item
{
  return [self.rootController lastVisibleDescendatIndexForItem:item];
}

- (id)parentForItem:(id)item
{
  RATreeNodeController *controller = [self.rootController controllerForItem:item];
  return controller.parentController.treeNode.item;
}

- (NSInteger)levelForItem:(id)item
{
  return [self.rootController controllerForItem:item].level;
}

- (id)childInParent:(id)parent atIndex:(NSInteger)index
{
  RATreeNodeController *controller = [self.rootController controllerForItem:parent].childControllers[index];
  return controller.treeNode.item;
}

- (void)expandRowForItem:(id)item updates:(void (^)(NSIndexSet *))updates
{
  [self expandRowForItem:item expandChildren:YES updates:updates];
}

- (void)expandRowForItem:(id)item expandChildren:(BOOL)expandChildren updates:(void (^)(NSIndexSet *))updates
{
  NSParameterAssert(updates);
  
  RATreeNodeController *parentController = [self.rootController controllerForItem:item];
  NSMutableArray *items = [@[item] mutableCopy];
  
  while ([items count] > 0) {
    id currentItem = [items firstObject];
    [items removeObject:currentItem];
    
    RATreeNodeController *controller = [self.rootController controllerForItem:currentItem];
    NSMutableArray *oldChildItems = [NSMutableArray array];
    for (RATreeNodeController *nodeController in controller.childControllers) {
      [oldChildItems addObject:nodeController.treeNode.item];
    }
    
    NSInteger numberOfChildren = [self.dataSource treeNodeCollectionController:self numberOfChildrenForItem:controller.treeNode.item];
    NSIndexSet *allIndexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, numberOfChildren)];
    
    NSArray *currentChildControllersAndIndexes = [self controllersAndIndexesForNodesWithIndexes:allIndexes inParentController:controller];
    NSArray *currentChildControllers = [currentChildControllersAndIndexes valueForKey:@"controller"];
    
    NSMutableArray *childControllersToInsert = [NSMutableArray array];
    NSMutableIndexSet *indexesForInsertions = [NSMutableIndexSet indexSet];
    NSMutableArray *childControllersToRemove = [NSMutableArray array];
    NSMutableIndexSet *indexesForDeletions = [NSMutableIndexSet indexSet];
    
    for (RATreeNodeController *loopNodeController in currentChildControllers) {
      if (![controller.childControllers containsObject:loopNodeController]
          && ![oldChildItems containsObject:controller.treeNode.item]) {
        [childControllersToInsert addObject:loopNodeController];
        NSInteger index = [currentChildControllers indexOfObject:loopNodeController];
        NSAssert(index != NSNotFound, nil);
        [indexesForInsertions addIndex:index];
      }
    }
    
    for (RATreeNodeController *loopNodeController in controller.childControllers) {
      if (![currentChildControllers containsObject:loopNodeController]
          && ![childControllersToInsert containsObject:loopNodeController]) {
        [childControllersToRemove addObject:loopNodeController];
        NSInteger index = [controller.childControllers indexOfObject:loopNodeController];
        NSAssert(index != NSNotFound, nil);
        [indexesForDeletions addIndex:index];
      }
    }
    
    [controller removeChildControllersAtIndexes:indexesForDeletions];
    [controller insertChildControllers:childControllersToInsert atIndexes:indexesForInsertions];
    
    if (expandChildren) {
      for (RATreeNodeController *nodeController in controller.childControllers) {
        [items addObject:nodeController.treeNode.item];
      }
    }

    [controller expandAndExpandChildren:expandChildren];
  }
  
  updates(parentController.descendantsIndexes);
}

- (void)collapseRowForItem:(id)item collapseChildren:(BOOL)collapseChildren updates:(void (^)(NSIndexSet *))updates
{
  NSParameterAssert(updates);
  
  RATreeNodeController *controller = [self.rootController controllerForItem:item];
  NSIndexSet *deletions = controller.descendantsIndexes;
  [controller collapseAndCollapseChildren:collapseChildren];
  
  updates(deletions);
}

- (void)insertItemsAtIndexes:(NSIndexSet *)indexes inParent:(id)item
{
  RATreeNodeController *parentController = [self.rootController controllerForItem:item];
  NSArray *newControllers = [self controllersForNodesWithIndexes:indexes inParentController:parentController];
  [parentController insertChildControllers:newControllers atIndexes:indexes];
}

- (void)moveItemAtIndex:(NSInteger)index inParent:(id)parent toIndex:(NSInteger)newIndex inParent:(id)newParent updates:(void (^)(NSIndexSet *, NSIndexSet *))updates
{
  NSParameterAssert(updates);
  
  NSMutableIndexSet *removedIndexes = [NSMutableIndexSet indexSet];
  NSMutableIndexSet *addedIndexes = [NSMutableIndexSet indexSet];
  
  RATreeNodeController *parentController = [self.rootController controllerForItem:parent];
  
  if (parent == newParent) {
    [parentController moveChildControllerAtIndex:index toIndex:newIndex];
    
  } else {
    RATreeNodeController *childController = parentController.childControllers[index];
    
    [removedIndexes addIndex:childController.index];
    [removedIndexes addIndexes:childController.descendantsIndexes];
    
    RATreeNodeController *newParentController = [self.rootController controllerForItem:parent];
    [parentController removeChildControllersAtIndexes:[NSIndexSet indexSetWithIndex:index]];
    [newParentController insertChildControllers:@[childController] atIndexes:[NSIndexSet indexSetWithIndex:newIndex]];
    
    [addedIndexes addIndex:childController.index];
    [addedIndexes addIndexes:childController.descendantsIndexes];
  }
  
  updates(removedIndexes, addedIndexes);
}

- (void)removeItemsAtIndexes:(NSIndexSet *)indexes inParent:(id)item updates:(void (^)(NSIndexSet *))updates
{
  RATreeNodeController *parentController = [self.rootController controllerForItem:item];
  
  NSMutableIndexSet *indexesToRemoval = [NSMutableIndexSet indexSet];
  [indexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
    RATreeNodeController *controller = parentController.childControllers[idx];
    [indexesToRemoval addIndex:controller.index];
    [indexesToRemoval addIndexes:controller.descendantsIndexes];
  }];
  
  [parentController removeChildControllersAtIndexes:indexes];
  
  updates(indexesToRemoval);
}

- (NSArray *)controllersAndIndexesForNodesWithIndexes:(NSIndexSet *)indexes inParentController:(RATreeNodeController *)parentController
{
  NSMutableArray *childControllers = [parentController.childControllers mutableCopy];
  NSMutableArray *currentControllers = [NSMutableArray array];
  
  NSMutableArray *invalidItems = [NSMutableArray array];
  for (RATreeNodeController *nodeController in parentController.childControllers) {
    [invalidItems addObject:nodeController.treeNode.item];
  }
  
  [indexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
    
    RATreeNodeController *controller;
    RATreeNodeController *oldControllerForCurrentIndex = nil;
    
    
    RATreeNodeItem *lazyItem = [[RATreeNodeItem alloc] initWithParent:parentController.treeNode.item index:idx];
    lazyItem.dataSource = self;
    
    
    for (RATreeNodeController *controller in parentController.childControllers) {
      if ([controller.treeNode.item isEqual:lazyItem.item]) {
        oldControllerForCurrentIndex = controller;
      }
    }
    if (oldControllerForCurrentIndex != nil) {
      controller = oldControllerForCurrentIndex;
      
    } else {
      controller = [[RATreeNodeController alloc] initWithParent:parentController item:lazyItem expandedBlock:^BOOL(id item) {
        return [childControllers indexOfObjectPassingTest:^BOOL(RATreeNodeController *controller, NSUInteger idx, BOOL *stop) {
          return [controller.treeNode.item isEqual:item];
        }] != NSNotFound;
      }];
    }
    
    [currentControllers addObject:@{ @"index" : @(idx),
                                 @"controller" : controller }];
  }];
  
  return [currentControllers copy];
}

- (NSArray *)controllersForNodesWithIndexes:(NSIndexSet *)indexes inParentController:(RATreeNodeController *)parentController
{
  return [[self controllersAndIndexesForNodesWithIndexes:indexes inParentController:parentController] valueForKey:@"controller"];
}

- (NSArray *)controllersForNodes:(NSInteger)nodesNumber inParentController:(RATreeNodeController *)parentController
{
  NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, nodesNumber)];
  return [self controllersForNodesWithIndexes:indexSet inParentController:parentController];
}

#pragma mark - RATreeNodeController delegate

- (id)treeNodeController:(RATreeNodeController *)controller child:(NSInteger)childIndex
{
  return [self.dataSource treeNodeCollectionController:self child:childIndex ofItem:controller.treeNode.item];
}

- (NSInteger)numberOfChildrenForTreeNodeController:(RATreeNodeController *)controller
{
  return [self.dataSource treeNodeCollectionController:self numberOfChildrenForItem:controller.treeNode.item];
}


#pragma mark - RATreeNodeItem data source

- (id)itemForTreeNodeItem:(RATreeNodeItem *)treeNodeItem
{
  return [self.dataSource treeNodeCollectionController:self child:treeNodeItem.index ofItem:treeNodeItem.parent];
}


#pragma mark - Properties

- (RATreeNodeController *)rootController
{
  if (!_rootController) {
    _rootController = [[RATreeNodeController alloc] initWithParent:nil item:nil expandedBlock:^BOOL(id _) {
      return YES;
    }];
    
    NSInteger numberOfChildren = [self.dataSource treeNodeCollectionController:self numberOfChildrenForItem:nil];
    NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, numberOfChildren)];
    NSArray *childControllers = [self controllersForNodesWithIndexes:indexSet inParentController:_rootController];
    [_rootController insertChildControllers:childControllers atIndexes:indexSet];
  }
  
  return _rootController;
}

@end