/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#import "WXComponent+DataBinding.h"
#import "WXComponent_internal.h"
#import "WXComponent+Layout.h"
#import "WXSDKInstance_private.h"
#import "WXComponentManager.h"
#import "WXSDKManager.h"
#import "WXAssert.h"
#import "WXJSASTParser.h"
#import "WXBridgeManager.h"
#import "WXUtility.h"
#import "WXRecycleListComponent.h"
#import "WXRecycleListDataManager.h"
#import "WXCoreBridge.h"

#import <JavaScriptCore/JavaScriptCore.h>

#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"

typedef enum : NSUInteger {
    WXDataBindingTypeProp = 0,
    WXDataBindingTypeStyle,
    WXDataBindingTypeAttributes,
    WXDataBindingTypeEvents,
    WXDataBindingTypeCount,
} WXDataBindingType;

static JSContext *jsContext;

@implementation WXComponent (DataBinding)

- (void)updateBindingData:(NSDictionary *)data
{
    WXAssertComponentThread();
    NSString * listRef = nil;
    NSIndexPath *indexPath  = nil;
    NSDictionary * dictionary = nil;
    listRef = data[@"recycleListComponentRef"];
    indexPath = data[@"indexPath"];
    NSString *phase = data[@"@phase"];
    if (!indexPath || !listRef) {
        if (data[@"aliasKey"]) {
            id key = data[@"aliasKey"];
            dictionary =  data[key];
        } else {
            dictionary = data;
        }
        listRef = dictionary[@"recycleListComponentRef"];
        indexPath = dictionary[@"indexPath"];
    }
    if (!phase && dictionary) {
        phase = dictionary[@"@phase"];
    }
    
    if (!indexPath || !listRef) {
        return;
    }
    WXRecycleListComponent * recycleListComponent = (WXRecycleListComponent*)[self.weexInstance.componentManager componentForRef:listRef];
    if (_isSkipUpdate) {
        _isSkipUpdate = NO;
        return;
    }
    
    WXLogDebug(@"Update binding data:%@, for component:%@", data, self.ref);
    if (!data) {
        WXLogWarning(@"No data for updateBindingData");
        return;
    }
    
    WXComponent *templateComponent = _templateComponent;
    if (!templateComponent) {
        WXLogError(@"No template fount for component:%@", templateComponent);
        return;
    }
    
    __block NSMutableDictionary *newData = [NSMutableDictionary dictionary];
    if (templateComponent->_bindingProps) {
        [templateComponent->_bindingProps enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, WXDataBindingBlock  _Nonnull block, BOOL * _Nonnull stop) {
            BOOL needUpdate;
            id value = block(data, &needUpdate);
            if (value) {
                newData[key] = value;
            }
        }];
    }
    
    if (self.attributes[@"@isComponentRoot"]) {
        NSString *templateId = self.attributes[@"@templateId"];
        if (![recycleListComponent.dataManager virtualComponentDataWithIndexPath:indexPath templateId:templateId]) {
            static NSUInteger __componentId = 0;
            self->_virtualComponentId = [NSString stringWithFormat:@"%@@%lu*%@", listRef, (unsigned long)__componentId % (2048*1024),templateId];
            __componentId++;
            dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
            [[WXSDKManager bridgeMgr] callComponentHook:self.weexInstance.instanceId componentId:templateId type:@"lifecycle" hook:@"create" args:@[self->_virtualComponentId, newData] competion:^(JSValue *value) {
                [newData setObject:indexPath forKey:@"indexPath"];
                [newData setObject:listRef forKey:@"recycleListComponentRef"];
                if ([[value toArray][0] isKindOfClass:[NSDictionary class]]) {
                    NSMutableDictionary *virtualComponentData = [value toArray][0];
                    [virtualComponentData setObject:indexPath forKey:@"indexPath"];
                    [[recycleListComponent dataManager] updateVirtualComponentData:self->_virtualComponentId data:virtualComponentData];
                    [newData addEntriesFromDictionary:virtualComponentData];
                }
                dispatch_semaphore_signal(semaphore);
            }];
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            
            [self _refsConventFromData:newData];
            [[WXSDKManager bridgeMgr] callComponentHook:self.weexInstance.instanceId componentId:self->_virtualComponentId type:@"lifecycle" hook:@"attach" args:@[@{@"virtualComponentId":self->_virtualComponentId,@"position":@(indexPath.row),@"refs":self->_virtualElementInfo[@"refs"]?:@{}}] competion:nil];
            if ([newData count]) {
                data = newData;
            }
        } else {
            NSDictionary *virtualComponentData = [recycleListComponent.dataManager virtualComponentDataWithIndexPath:indexPath templateId:templateId];
            [newData addEntriesFromDictionary:virtualComponentData];
            newData[@"virtualComponentId"] = self->_virtualComponentId;
            [newData setObject:indexPath forKey:@"indexPath"];
            [newData setObject:listRef forKey:@"recycleListComponentRef"];
            data = newData;
        }
    }
    
    if (phase) {
        NSMutableDictionary * newData = [data mutableCopy];
        newData[@"@phase"] = phase;
        data = newData;
    }
    if (self->_templateComponent && self->_templateComponent->_dataBindOnce && recycleListComponent && data[@"@phase"]) {
        WXLogInfo(@"interrupt update data: %@ because of v-once ", data);
        return;
    }
    if (!_isRepeating) {
        WXDataBindingBlock repeatBlock = templateComponent->_bindingRepeat;
        if (repeatBlock) {
            BOOL needUpdate = NO;
            NSArray *repeatData = repeatBlock(data, &needUpdate);
            [self _repeat:repeatData inData:data];
            return;
        }
    }
    
    WXDataBindingBlock matchBlock = templateComponent->_bindingMatch;
    if (matchBlock) {
        BOOL needUpdate = NO;
        BOOL needDisplay = NO;
        id match = matchBlock(data, &needUpdate);
        if ([match isKindOfClass:[NSNumber class]]) {
            needDisplay = [match boolValue];
        } else {
            needDisplay = (match != nil);
        }
        
        if (!needDisplay) {
            self.displayType = WXDisplayTypeNone;
            return;
        } else if (needDisplay) {
            self.displayType = WXDisplayTypeBlock;
        }
    }
    
    for (int i = WXDataBindingTypeStyle; i < WXDataBindingTypeCount; i++) {
        NSDictionary *bindingMap = i == WXDataBindingTypeStyle ? templateComponent->_bindingStyles : (i == WXDataBindingTypeAttributes ? templateComponent->_bindingAttributes : templateComponent->_bindingEvents);
        if (!bindingMap || bindingMap.count == 0) {
            continue;
        }
        NSMutableDictionary *newAttributesOrStyles = [NSMutableDictionary dictionary];
        
        [bindingMap enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull attributeOrStyleName, WXDataBindingBlock  _Nonnull bindingBlock, BOOL * _Nonnull stop) {
            BOOL needUpdate = NO;
            id newValue = bindingBlock(data, &needUpdate);
            if (needUpdate) {
                newAttributesOrStyles[attributeOrStyleName] = newValue;
            }
        }];
        
        if (newAttributesOrStyles.count > 0) {
            [self.weexInstance.componentManager startComponentTasks];
            if (i == WXDataBindingTypeStyle) {
                [self.weexInstance.componentManager updateStyles:newAttributesOrStyles forComponent:self.ref];
            } else if (i == WXDataBindingTypeAttributes) {
                [self.weexInstance.componentManager updateAttributes:newAttributesOrStyles forComponent:self.ref];
            } else if (i == WXDataBindingTypeEvents) {
                [self _addEventParams:newAttributesOrStyles];
            } else if (i == WXDataBindingTypeProp) {//!OCLint
            }
        }
    }
    
    NSArray *subcomponents = self.subcomponents;
    for (WXComponent *subcomponent in subcomponents) {
        if (subcomponent->_virtualComponentId &&dictionary[@"componentDataId"] && ![subcomponent->_virtualComponentId isEqualToString:dictionary[@"componentDataId"]]) {
            continue;
        }
        [subcomponent updateBindingData:data];
    }
}

- (void)_repeat:(NSArray *)repeatData inData:(NSDictionary *)data
{
    NSMutableDictionary *dataCopy = [data mutableCopy];
    WXComponent *templateComponent = _templateComponent;
    NSArray *subcomponents = self.supercomponent.subcomponents;
    NSUInteger startIndex = [subcomponents indexOfObject:self];
    [repeatData enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (templateComponent->_repeatIndexIdentify) {
            dataCopy[templateComponent->_repeatIndexIdentify] = @(idx);
        }
        if (templateComponent->_repeatLabelIdentify) {
            dataCopy[templateComponent->_repeatLabelIdentify] = obj;
        }
        
        WXComponent *exsitingComponent;
        if (startIndex + idx < subcomponents.count) {
            if (subcomponents[startIndex + idx]
                && ((WXComponent *)(subcomponents[startIndex + idx]))->_templateComponent == templateComponent) {
                exsitingComponent = subcomponents[startIndex + idx];
                exsitingComponent.displayType = WXDisplayTypeBlock;
            }
        }
        
        WXComponent *component = exsitingComponent ? : [templateComponent copy];
        component->_isRepeating = YES;
        [component updateBindingData:dataCopy];
        component->_isRepeating = NO;
        
        if (idx > 0 && exsitingComponent) {
            component->_isSkipUpdate = YES;
        }
        
        if (!exsitingComponent) {
            [self.weexInstance.componentManager startComponentTasks];
            BOOL inserted = [self.supercomponent _insertSubcomponent:component atIndex:startIndex + idx];
            if (inserted) {
                // add to layout tree
                [WXCoreBridge addChildRenderObject:component->_flexCssNode toParent:self.supercomponent->_flexCssNode];
                
                [self.weexInstance.componentManager _addUITask:^{
                    [self.supercomponent insertSubview:component atIndex:startIndex + idx];
                }];
            }
            else {
                WXLogError(@"fail to insert copied component for data binding.");
            }
        }
    }];
    
    // set display:none to the redundant components;
    NSUInteger i = startIndex + repeatData.count;
    while (i < self.supercomponent.subcomponents.count) {
        WXComponent *component = self.supercomponent.subcomponents[i];
        if (component->_templateComponent == templateComponent) {
            component->_isSkipUpdate = YES;
            component.displayType = WXDisplayTypeNone;
        }
        i++;
    }
}

- (void)_storeBindingsWithProps:(NSDictionary *)props styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSDictionary *)events
{
    WXAssertComponentThread();
    
    if (props.count > 0) {
        if (!_bindingProps) {
            _bindingProps = [NSMutableDictionary dictionary];
        }
        [self _storeBindings:props type:WXDataBindingTypeProp];
    }
    
    if (styles.count > 0) {
        if (!_bindingStyles) {
            _bindingStyles = [NSMutableDictionary dictionary];
        }
        [self _storeBindings:styles type:WXDataBindingTypeStyle];
    }
    
    if (attributes.count > 0) {
        if (!_bindingAttributes) {
            _bindingAttributes = [NSMutableDictionary dictionary];
        }
        [self _storeBindings:attributes type:WXDataBindingTypeAttributes];
    }
    
    if (events.count > 0) {
        if (!_bindingEvents) {
            _bindingEvents = [NSMutableDictionary dictionary];
        }
        [self _storeBindings:events type:WXDataBindingTypeEvents];
    }
}

- (void)_storeBindings:(NSDictionary *)stylesOrAttributesOrEvents type:(WXDataBindingType)type
{
    WXAssertComponentThread();
    
    if (_bindingExpressions == nullptr) {
        _bindingExpressions = new std::vector<WXJSExpression *>();
    }
    
    NSMutableDictionary *bindingMap;
    switch (type) {
        case WXDataBindingTypeProp:
            bindingMap = _bindingProps;
            break;
        case WXDataBindingTypeStyle:
            bindingMap = _bindingStyles;
            break;
        case WXDataBindingTypeAttributes:
            bindingMap = _bindingAttributes;
            break;
        case WXDataBindingTypeEvents:
            bindingMap = _bindingEvents;
            break;
        default:
            WXAssert(NO, @"error binding type:%z", type);
            break;
    }
    [stylesOrAttributesOrEvents enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull name, id  _Nonnull binding, BOOL * _Nonnull stop) {
        if ([binding isKindOfClass:[NSDictionary class]] && binding[WXBindingIdentify]) {
            // {"attributeOrStyleName":{"@binding":"bindingExpression"}
            NSString *bindingExpression = binding[WXBindingIdentify];
            WXJSASTParser *parser = [WXJSASTParser parserWithScript:bindingExpression];
            WXJSExpression *expression = [parser parseExpression];
            self->_bindingExpressions->push_back(expression);
            WXDataBindingBlock block = [self bindingBlockWithExpression:expression];
            bindingMap[name] = block;
        } else if ([binding isKindOfClass:[NSArray class]]) {
            // {"attributeOrStyleName":[..., "string", {"@binding":"bindingExpression"}, "string", {"@binding":"bindingExpression"}, ...]
            NSMutableDictionary *bindingBlocksForIndex = [NSMutableDictionary dictionary];
            __block BOOL isBinding = NO;
            [binding enumerateObjectsUsingBlock:^(id  _Nonnull bindingInArray, NSUInteger idx, BOOL * _Nonnull stop) {
                if ([bindingInArray isKindOfClass:[NSDictionary class]] && bindingInArray[WXBindingIdentify]) {
                    isBinding = YES;
                    NSString *bindingExpression = bindingInArray[WXBindingIdentify];
                    WXJSASTParser *parser = [WXJSASTParser parserWithScript:bindingExpression];
                    WXJSExpression *expression = [parser parseExpression];
                    self->_bindingExpressions->push_back(expression);
                    WXDataBindingBlock block = [self bindingBlockWithExpression:expression];
                    bindingBlocksForIndex[@(idx)] = block;
                }
            }];
            
            bindingMap[name] = ^id(NSDictionary *data, BOOL *needUpdate) {
                NSMutableArray *newArray = [binding mutableCopy];
                [binding enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                    BOOL _needUpdate = NO;
                    WXDataBindingBlock block = bindingBlocksForIndex[@(idx)];
                    if (block) {
                        id newValue = block(data, &_needUpdate);
                        if (newValue) {
                             newArray[idx] = newValue;
                        }  
                        if (_needUpdate) {
                            *needUpdate = YES;
                        }
                    }
                }];
                return type == WXDataBindingTypeEvents ? newArray : [newArray componentsJoinedByString:@""];
            };
        }
        
        if (type == WXDataBindingTypeAttributes) {
            if ([WXBindingOnceIdentify isEqualToString:name]) {
                self->_dataBindOnce = [WXConvert BOOL:binding];
            } else if ([WXBindingMatchIdentify isEqualToString:name]) {
                WXJSASTParser *parser = [WXJSASTParser parserWithScript:binding];
                WXJSExpression *expression = [parser parseExpression];
                self->_bindingExpressions->push_back(expression);
                self->_bindingMatch = [self bindingBlockWithExpression:expression];
            } else if ([WXBindingRepeatIdentify isEqualToString:name]) {
                WXJSASTParser *parser = [WXJSASTParser parserWithScript:binding[WXBindingRepeatExprIdentify]];
                WXJSExpression *expression = [parser parseExpression];
                self->_bindingExpressions->push_back(expression);
                self->_bindingRepeat = [self bindingBlockWithExpression:expression];
                self->_repeatIndexIdentify = binding[WXBindingRepeatIndexIdentify];
                self->_repeatLabelIdentify = binding[WXBindingRepeatLabelIdentify];
            }
        }
    }];
}

- (WXDataBindingBlock)bindingBlockWithExpression:(WXJSExpression *)expression
{
    if (!expression) {
        return nil;
    }
    
    __weak typeof(self) weakSelf = self;
    WXDataBindingBlock block = ^id(NSDictionary *data, BOOL *needUpdate) {
        __strong typeof(weakSelf) strongSelf = weakSelf;
        if (strongSelf == nil) {
            *needUpdate = NO;
            return nil;
        }
        
        if (expression->is<WXJSStringLiteral>()) {
            *needUpdate = NO;
            return [NSString stringWithCString:(((WXJSStringLiteral *)expression)->value).c_str() encoding:[NSString defaultCStringEncoding]];
        } else if (expression->is<WXJSNumericLiteral>()) {
            *needUpdate = NO;
            return @(((WXJSNumericLiteral *)expression)->value);
        } else if (expression->is<WXJSBooleanLiteral>()) {
            *needUpdate = NO;
            return @(((WXJSBooleanLiteral *)expression)->value);
        } else if (expression->is<WXJSNullLiteral>()) {
            *needUpdate = NO;
            return nil;
        } else if (expression->is<WXJSIdentifier>()) {
            NSString *identiferName = [NSString stringWithCString:const_cast<char*>((((WXJSIdentifier *)expression)->name).c_str()) encoding:NSUTF8StringEncoding];
            if (data[identiferName]) {
                *needUpdate = YES;
                return data[identiferName];
            } else {
                WXLogError(@"identifer:%@ not found", identiferName);
                *needUpdate = YES;
                return @"";
            }
        } else if (expression->is<WXJSMemberExpression>()) {
            WXJSMemberExpression *member = (WXJSMemberExpression *)expression;
            BOOL objectNeedUpdate = NO, propertyNeedUpdate = NO;
            id object = [strongSelf bindingBlockWithExpression:member->object](data, &objectNeedUpdate);
            if (member->computed) {
                id propertyName = [strongSelf bindingBlockWithExpression:member->property](data, &propertyNeedUpdate);
                *needUpdate = objectNeedUpdate || propertyNeedUpdate;
                if ([object isKindOfClass:[NSDictionary class]] && [propertyName isKindOfClass:[NSString class]]) {
                    return object[propertyName];
                } else if ([object isKindOfClass:[NSArray class]] && [propertyName isKindOfClass:[NSNumber class]]) {
                    return [object objectAtIndex:[propertyName unsignedIntegerValue]];
                }
            } else {
                WXJSExpression * memberExpression = member->property;
                if (memberExpression->is<WXJSIdentifier>()) {
                    NSString *propertyName = [NSString stringWithCString:(((WXJSStringLiteral *)member->property)->value).c_str() encoding:[NSString defaultCStringEncoding]];
                    *needUpdate = objectNeedUpdate;
                    if ([object isKindOfClass:[NSDictionary class]]) {
                        return object[propertyName];
                    }
                } else {
                    id retvalue = [strongSelf bindingBlockWithExpression:member->property](object, &objectNeedUpdate);
                    *needUpdate = objectNeedUpdate || propertyNeedUpdate;
                    return retvalue;
                }
            }
            
            return nil;
        } else if (expression->is<WXJSArrayExpression>()) {
            WXJSArrayExpression *expr = (WXJSArrayExpression *)expression;
            std::vector<WXJSExpression *> expressions = expr->expressions;
            NSMutableArray *array = [NSMutableArray array];
            for(WXJSExpression *expr : expressions) {
                if (expr == NULL) {
                    continue;
                }
                WXDataBindingBlock block = [strongSelf bindingBlockWithExpression:expr];
                *needUpdate = NO;
                if (block) {
                    BOOL elementNeedUpdate;
                    id object = block(data, &elementNeedUpdate);
                    if (object) {
                        *needUpdate = *needUpdate || elementNeedUpdate;
                        [array addObject:object];
                    }
                }
            }
            
            return array;
        } else if (expression->is<WXJSUnaryExpression>()) {
            WXJSUnaryExpression *expr = (WXJSUnaryExpression *)expression;
            std::string operator_ = expr->operator_;
            id argument = [strongSelf bindingBlockWithExpression:expr->argument](data, needUpdate);
            if (operator_ == "+") {
                return @([argument doubleValue]);
            } else if (operator_ == "-") {
                return @(-[argument doubleValue]);
            } else if (operator_ == "!") {
                return @(![argument boolValue]);
            } else {
                WXLogError(@"Not supported unary operator:%s", operator_.c_str());
                return nil;
            }
        } else if (expression->is<WXJSBinaryExpression>()) {
            WXJSBinaryExpression *expr = (WXJSBinaryExpression *)expression;
            std::string operator_ = expr->operator_;
            BOOL leftNeedUpdate = NO, rightNeedUpdate = NO;
            id left = [strongSelf bindingBlockWithExpression:expr->left](data, &leftNeedUpdate);
            id right = [strongSelf bindingBlockWithExpression:expr->right](data, &rightNeedUpdate);
            *needUpdate = leftNeedUpdate || rightNeedUpdate;
            if (operator_ == "+") {
                return @([left doubleValue] + [right doubleValue]);
            } else if (operator_ == "-") {
                return @([left doubleValue] - [right doubleValue]);
            } else if (operator_ == "*") {
                return @([left doubleValue] * [right doubleValue]);
            } else if (operator_ == "/") {
                return @([left doubleValue] / [right doubleValue]);
            } else if (operator_ == "%") {
                return @([left integerValue] % [right integerValue]);
            } else if (operator_ == ">") {
                return @([left doubleValue] > [right doubleValue]);
            } else if (operator_ == ">=") {
                return @([left doubleValue] >= [right doubleValue]);
            } else if (operator_ == "<") {
                return @([left doubleValue] < [right doubleValue]);
            } else if (operator_ == "<=") {
                return @([left doubleValue] <= [right doubleValue]);
            } else if (operator_ == "===" || operator_ == "==") {
                if ([left isKindOfClass:[NSString class]]) {
                    return @([left isEqualToString:right]);
                } else if ([left isKindOfClass:[NSNumber class]]) {
                    return  @([left doubleValue] == [right doubleValue]);
                } else {
                    WXLogError(@"Wrong type %@ at left of '===' or '=='", NSStringFromClass([left class]));
                    return nil;
                }
            } else if (operator_ == "!==" || operator_ == "!=") {
                if ([left isKindOfClass:[NSString class]]) {
                    return @(![left isEqualToString:right]);
                } else if ([left isKindOfClass:[NSNumber class]]) {
                    return  @([left doubleValue] != [right doubleValue]);
                } else {
                    WXLogError(@"Wrong type %@ at left of '!==' or '!='", NSStringFromClass([left class]));
                    return nil;
                }
            } else if (operator_ == "||") {
                return @([left boolValue] || [right boolValue]);
            } else if (operator_ == "&&") {
                return @([left boolValue] && [right boolValue]);
            } else {
                WXLogError(@"Not supported binary operator:%s", operator_.c_str());
                return nil;
            }
        } else if (expression->is<WXJSConditionalExpression>()) {
            WXJSConditionalExpression *conditional = (WXJSConditionalExpression *)expression;
            BOOL testNeedUpdate = NO, conditionalNeedUpdate = NO, alternateNeedUpdate = NO;
            id testResult = [strongSelf bindingBlockWithExpression:conditional->test](data, &testNeedUpdate);
            id result;
            if ([testResult boolValue]) {
                result = [strongSelf bindingBlockWithExpression:conditional->consequent](data, &conditionalNeedUpdate);
            } else {
                result = [strongSelf bindingBlockWithExpression:conditional->alternate](data, &alternateNeedUpdate);
            }
            *needUpdate = testNeedUpdate || conditionalNeedUpdate || alternateNeedUpdate;
            return result;
        } else {
            WXAssert(NO, @"expression type error");
            return nil;
        }
    };
    
    return block;
}

- (void)attachSlotEvent:(NSDictionary *)data
{
    [self cellSlotEventHandle:data isAttach:YES];
}

- (void)detachSlotEvent:(NSDictionary *)data
{
    [self cellSlotEventHandle:data isAttach:NO];
}

- (void)cellSlotEventHandle:(NSDictionary *)data isAttach:(BOOL)isAttach
{
    [self _refsConventFromData:data];
    if (_virtualElementInfo.count != 0) {
        NSString *recycleListComponentRef = data[@"recycleListComponentRef"];
        NSIndexPath *indexPath = data[@"indexPath"];
        if (!recycleListComponentRef) {
            if (data[@"aliasKey"]) {
                id key = data[@"aliasKey"];
                recycleListComponentRef = data[key][@"recycleListComponentRef"];
                indexPath = data[key][@"indexPath"];
            }
        }
        [_virtualElementInfo addEntriesFromDictionary:@{@"position":@(indexPath.row)}];
        [[WXSDKManager bridgeMgr] fireEvent:self.weexInstance.instanceId ref:recycleListComponentRef type:isAttach ?@"_attach_slot": @"_detach_slot" params:_virtualElementInfo domChanges:nil handlerArguments:nil];
    }
}

- (void )_refsConventFromData:(NSDictionary *)data
{
    _virtualElementInfo = [NSMutableDictionary new];
    if (self.attributes[@"ref"]) {
        NSMutableDictionary *subInfo = [NSMutableDictionary new];
        [self _componentInfoOfRef:self subInfo:subInfo data:data];
        [self _recursiveSlotComponent:self subInfo:subInfo data:data];
    }
    else
    {
        [self _recursiveSlotComponent:self subInfo:nil data:data];
    }
}

- (void)_recursiveSlotComponent:(WXComponent *)component subInfo:(NSMutableDictionary *)subInfo data:(NSDictionary *)data
{
    subInfo = subInfo ? : [NSMutableDictionary new];
    for (WXComponent *subcomponent in component.subcomponents) {
        if (subcomponent.subcomponents.count != 0) {
            [self _recursiveSlotComponent:subcomponent subInfo:subInfo data:data];
        }
        [self _componentInfoOfRef:subcomponent subInfo:subInfo data:data];
    }
    if (subInfo.count !=0) {
        [_virtualElementInfo setObject:subInfo forKey:@"refs"];
    }
}

- (void)_componentInfoOfRef:(WXComponent *)component subInfo:(NSMutableDictionary *)subInfo data:(NSDictionary *)data
{
    if (component.attributes[@"ref"]) {
        NSIndexPath *indexPath = data[@"indexPath"];
        if (!indexPath) {
            if (data[@"aliasKey"]) {
                id key = data[@"aliasKey"];
                indexPath = data[key][@"indexPath"];
            }
        }
        NSString *virtualElementInfo = [NSString stringWithFormat:@"%@@%ld",component.ref,(long)indexPath.row];
        NSDictionary *refInfo = @{@"attrs":component.attributes,@"type":component->_type,@"ref":virtualElementInfo,@"[[VirtualElement]]":@"true"};
        if (subInfo[component.attributes[@"ref"]]) {
            [subInfo[component.attributes[@"ref"]] addObject:refInfo];
        }
        else
        {
            [subInfo setValue:[NSMutableArray arrayWithArray:@[refInfo]] forKey:component.attributes[@"ref"]];
        }
    }
}

@end
