//
//  Engine.m
//  ArmorP3V1
//
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "Engine.h"
#import "MatrixBuilder.h"
#import "SkillSet.h"
#import "PSMutex.h"
#import "DecorationCache.h"

@implementation Engine

@synthesize session = _session;

@synthesize listForScanAll = _listForScanAll;
@synthesize listForScanTop = _listForScanTop;
@synthesize listForCheck = _listForCheck;

@synthesize targetSkill = _targetSkill;

@synthesize listResultSearch = _listResultSearch;
@synthesize listResultChecked = _listResultChecked;
@synthesize useAnotherCheck = _useAnotherCheck;
@synthesize superEngine = _superEngine;
@synthesize repository = _repository;
@synthesize tempFlagAlready = _tempFlagAlready;
@synthesize tempChildQueue = _tempChildQueue;
@synthesize tempSlotResult = _tempSlotResult;
@synthesize countMax = _countMax;
@synthesize isOptimizedArmor = _isOptimizedArmor;

-(id)initWithSession:(PSSession*)session {
    self = [super init];
    if (self) {
        _listForScanAll = [[PSItemList alloc]init];
        _listForScanTop = [[PSItemList alloc]init];
        _listForCheck = [[PSItemList alloc]init];

        _listResultChecked = [[NSMutableArray alloc]init];
        _listResultSearch = [[NSMutableArray alloc]init];
        _useAnotherCheck = FALSE;
        _superEngine = nil;
        _session = session;
        [_session retain];
        
        _targetSkill = session.searchSkills;
        [_targetSkill retain];
        
        if (_listForScanAll == nil || _listForScanTop == nil || _listForCheck == nil || _listResultChecked == nil || _listResultSearch == nil) {
            [[PSMutex mainMutex] raiseMemoryError];
            [self dealloc];
            return nil;
        }
        
        _repository = [Repository mainRepository];

        _tempFlagAlready = [[NSMutableSet alloc]init];
        _tempChildQueue = [[NSMutableArray alloc]init];
        _tempSlotResult = [[NSMutableArray alloc]init];
        
        if (_tempFlagAlready == nil || _tempChildQueue == nil || _tempSlotResult == nil) {
            [[PSMutex mainMutex] raiseMemoryError];
            [self dealloc];
            return nil;
        }
    }else {
        [[PSMutex mainMutex] raiseMemoryError];
    }
    [[PSMutex mainMutex] showMemoryAlertIfError];
    return self;
}

-(void)dealloc {
    [_session release];
    [_targetSkill release];

    [_listForScanAll release];
    [_listForScanTop release];
    [_listForCheck release];

    [_listResultChecked release];
    [_listResultSearch release];
    [_superEngine release];
    [_tempFlagAlready release];
    [_tempChildQueue release];
    [_tempSlotResult release];
    
    [super dealloc];
}

-(void)prepareScan: (int)itemType:(NSMutableArray*)source:(NSMutableArray*)dest {
    SkillSet* skills = _targetSkill;
    //NSLog(@"targetSkill = %@", _targetSkill);
    
    [dest removeAllObjects];
    
    NSMutableArray* listSlot0 = [[NSMutableArray alloc]init];
    NSMutableArray* listSlot1 = [[NSMutableArray alloc]init];
    NSMutableArray* listSlot2 = [[NSMutableArray alloc]init];
    NSMutableArray* listSlot3 = [[NSMutableArray alloc]init];
    NSMutableArray* listCopie = [[NSMutableArray alloc]init];
    
    for (int i = 0; i < source.count; ++ i) {
        PSItem *item = [source objectAtIndex:i];
        PSWrap *wrap = [[PSWrap alloc]initWithItem:item:skills];
        [wrap.maskedSkills set_only: item.skills];
                
        if ([PSSession isExistForThisHunber: _session: item]) {
            if (_session.searchWithUsableStatus) {
                if (item.isCheckedForSearch == FALSE) {
                    continue;
                }
            }
            if (itemType == ITEM_TYPE_DECORATION) {
                if ([PSSession isExistForSkill:_session.searchSkills :item]) {
                    [dest addObject:wrap];
                }
            }else {
                if (item.isCopieSkill) {
                    [listCopie addObject:wrap];
                }
                else if ([PSSession isExistForSkill:skills :item]) {
                    [dest addObject:wrap];
                }
                else if ([PSSession isExistForNegativeSkill:skills :item]) {
                    [dest addObject:wrap];
                }else {
                    if (item.slotCount == 0) {
                        [listSlot0 addObject: wrap];
                    }
                    else if (item.slotCount == 1) {
                        [listSlot1 addObject:wrap];
                    }
                    else if (item.slotCount == 2) {
                        [listSlot2 addObject:wrap];
                    }
                    else if (item.slotCount == 3) {
                        [listSlot3 addObject:wrap];
                    }
                }
            }
        }else {
            /*
            if (item.slotCount == 0) {
                [listSlot0 addObject: wrap];
            }
            else if (item.slotCount == 1) {
                [listSlot1 addObject:wrap];
            }
            else if (item.slotCount == 2) {
                [listSlot2 addObject:wrap];
            }
            else if (item.slotCount == 3) {
                [listSlot3 addObject:wrap];
            }
             */
        }
        [wrap release];
    }
    
    if (itemType != ITEM_TYPE_DECORATION) {
        PSWrap* item;
        
        item = [PSWrap getVirtualWrap:itemType :VIRTUAL_SLOT0];
        item.sameArmors = listSlot0;
        if (item.sameArmors.count != 0 || itemType == ITEM_TYPE_CHARM) {
            [dest addObject:item];
        }
        
        item = [PSWrap getVirtualWrap: itemType :VIRTUAL_SLOT1];
        item.sameArmors = listSlot1;
        if (item.sameArmors.count != 0 /*|| itemType == ITEM_TYPE_CHARM*/) {
            [dest addObject: item];
        }
        
        item = [PSWrap getVirtualWrap: itemType :VIRTUAL_SLOT2];
        item.sameArmors = listSlot2;
        if (item.sameArmors.count != 0 /*|| itemType == ITEM_TYPE_CHARM*/) {
            [dest addObject: item];
        }
        
        item = [PSWrap getVirtualWrap: itemType :VIRTUAL_SLOT3];
        item.sameArmors = listSlot3;
        if (item.sameArmors.count != 0 /*|| itemType == ITEM_TYPE_CHARM*/) {
            [dest addObject: item];
        }
 
        item = [PSWrap getVirtualWrap:itemType :VIRTUAL_COPIE];
        item.sameArmors = listCopie;
        if (item.sameArmors.count != 0 && itemType != ITEM_TYPE_CHARM) {
            [dest addObject: item];
        }
    }

    [listSlot0 release];
    [listSlot1 release];
    [listSlot2 release];
    [listSlot3 release];
    [listCopie release];
}

-(int)listToChild:(int)depth:(int)itemType:(NSMutableArray*) listTops {
    int proceed = 0;
    depth ++;
    
    for (int i = 0; i < listTops.count; ++i) {
        PSWrap* objI = [listTops objectAtIndex:i];
        for (int j = 0; j < listTops.count; ++j) {
            if (i == j) {
                continue;
            }
            PSWrap* objJ = [listTops objectAtIndex: j];
            /*
             if (objI.maskedSkills.isZero && objJ.maskedSkills.isZero
             && objI.item.slotCount == objJ.item.slotCount) {
             proceed++;
             [objJ.childArmors addObject: objI];
             [listTops removeObjectAtIndex:i];
             i--;
             break;
             }
             if (objI.maskedSkills.isZero || objJ.maskedSkills.isZero) {
             continue;
             }*/
            
            if (objI.item.isVirtual && objJ.item.isVirtual) {
                if (objI.item.isCopieSkill || objJ.item.isCopieSkill) {
                    
                }else if (objI.item.slotCount < objJ.item.slotCount) {
                    proceed++;
                    [objJ.childArmors addObject: objI];
                    [listTops removeObjectAtIndex:i];
                    i--;
                    break;
                }else if (objI.item.slotCount > objJ.item.slotCount) {
                    proceed++;
                    [objI.childArmors addObject: objJ];
                    [listTops removeObjectAtIndex:j];
                    j--;
                    continue;
                }
            }
            
            if ([Engine isTargetWeakChildren: _targetSkill: objJ: objI]) {
                proceed++;
                [objJ.childArmors addObject: objI];
                [listTops removeObjectAtIndex:i];
                i--;
                break;
            } else if ([Engine isTargetWeakChildren: _targetSkill: objI: objJ]) {
                proceed++;
                [objI.childArmors addObject: objJ];
                [listTops removeObjectAtIndex:j];
                j--;
                continue;
            }
        }
    }
    //NSLog(@"child = %d", proceed);
    return proceed;
}

+(BOOL)isTargetSameSkillsAndSlot:(PSWrap*) base: (PSWrap*) target {
    if (base.item.isCopieSkill) {
        if (target.item.isCopieSkill) {
            return TRUE;
        }
        return FALSE;
    } else if (target.item.isCopieSkill) {
        return FALSE;
    }
    if (base.item.slotCount != target.item.slotCount) {
        return FALSE;
    }
    
    if ([base.maskedSkills isFixedBy:target.maskedSkills]) {
        //OK
    }else {
        NSLog(@"error");
        abort();
    }
    if ([base.maskedSkills isZero]) {
        if ([target.maskedSkills isZero]) {
            return TRUE;
        }
    }
    /*
    SkillSet* masked1 = [[SkillSet alloc]initWithColumn:mask];
    [masked1 set_only:base.skills];
    SkillSet* masked2 = [[SkillSet alloc]initWithColumn:mask];
    [masked2 set_only:target.skills];
*/
    BOOL ret = TRUE;
    if ([SkillSet compareSkillSet:base.maskedSkills to:target.maskedSkills] != 0) {
        ret = FALSE;
        /*
        if ([base.maskedSkills isFixedBy:target.maskedSkills]) {
            //OK
        }else {
            abort();
        }*/
    }
    
    /*    [masked1 release];
    [masked2 release];*/
    return ret;
}

+(BOOL)isTargetWeakChildren:(SkillSet* )guilde: (PSWrap*) base: (PSWrap*) grp {
    if (base.item.isCopieSkill || grp.item.isCopieSkill) {
        return FALSE;
    }
    if (base.item.slotCount < grp.item.slotCount) {
        return FALSE;
    }
    if ([self isTargetSameSkillsAndSlot: base: grp]) {
        return FALSE;
    }
    
    SkillSet* skill1 = base.maskedSkills;
    SkillSet* skill2 = grp.maskedSkills;
    
    for (int i = 0; i < guilde.count; ++i) {
        SkillKind* kind = [guilde.listKind objectAtIndex: i];
        
        int x1 = [skill1 findByKind: kind];
        int x2 = [skill2 findByKind: kind];
        if (x1 < 0 && x2 < 0) {
            continue;
        }
        if (x1 < 0) {
            return FALSE;
        }
        if (x2 < 0) {
            continue;
        }
        if (guilde.positive[i]) {
            if (skill1.point[x1] < skill2.point[x2]) {
                return FALSE;
            }
        } else {
            if (skill1.point[x1] > skill2.point[x2]) {
                return FALSE;
            }
        }
    }
    return TRUE;
}

-(void)buildTreeForScan: (int)itemType:
    (NSMutableArray*)source: (NSMutableArray*)listTops: (NSMutableArray*)listAll{
    SkillSet* skills = _targetSkill;
    
    if (listAll.count > 0) {
        [listAll removeAllObjects];
    }
    if (listTops.count > 0) {
        [listTops removeAllObjects];
    }
       
    PSWrap* virtualNone = [PSWrap getVirtualWrap: itemType: VIRTUAL_SLOT0];
    virtualNone.maskedSkills = [[SkillSet alloc]initWithColumn:skills];
    [listAll addObject:virtualNone];
    
    for (int i = 0; i < source.count; ++ i) {
        PSWrap* wrap = [source objectAtIndex:i];

        wrap.maskedSkills = [[SkillSet alloc]initWithColumn:skills];
        [wrap.maskedSkills set_only:wrap.item.skills];
        
        [wrap.childArmors removeAllObjects];
        [listAll addObject: wrap];
        [listTops addObject: wrap];
    }
    
    int proceed = 0;
    for (int i = 0; i < listTops.count; ++i) {
        PSWrap* objI = [listTops objectAtIndex:i];
        for (int j = 0; j < listTops.count; ++j) {
            if (i == j) {
                continue;
            }
            PSWrap* objJ = [listTops objectAtIndex:j];
            if ([Engine isTargetSameSkillsAndSlot: objI: objJ]) {
                if (objI.item.isVirtual || objJ.item.isVirtual == FALSE) {
                    [objI.sameArmors addObject:objJ];
                    [objI.sameArmors addObjectsFromArray: objJ.sameArmors];
                    [objJ.sameArmors removeAllObjects];
                    [listTops removeObjectAtIndex:j];
                    proceed ++;
                    j--;
                } else {
                    [objJ.sameArmors addObject:objI];
                    [objJ.sameArmors addObjectsFromArray: objI.sameArmors];
                    [objI.sameArmors removeAllObjects];
                    [listTops removeObjectAtIndex:i];
                    proceed ++;
                    i--;
                    break;
                }
            }
        }
    }

    NSMutableArray* queue = [[NSMutableArray alloc]init];

    [queue addObject:listTops];

    while (queue.count > 0) {
        NSMutableArray* tops = [queue objectAtIndex: queue.count - 1];
        [queue removeLastObject];
        if ([self listToChild:0: itemType: tops] > 0) {
            for (int i = 0; i < tops.count; ++ i) {
                PSWrap* child = [tops objectAtIndex:i];
                if (child.childArmors.count >= 2) {
                    [queue addObject: child.childArmors];
                }
            }
        }
    }
    [queue release];

    //NSLog(@"type %d = %d -> %d -> %d", itemType, source.count, listAll.count, listTops.count);
}

-(void)loadExtraCharms:(NSMutableArray*)dictionary {
    [dictionary removeAllObjects];
    if (_session.searchCharmCSVTable > 0) {
        NSMutableArray* extraItem = [_repository.charmDB readExtraCharm: _session.searchCharmCSVTable: _session.searchSkills : nil];

        for (int i = 0; i < extraItem.count; ++ i) {
            PSItem* item = [extraItem objectAtIndex:i];
            //PSWrap* wrap = [[PSWrap alloc]initWithItem:item:_session.searchSkills];
            
            //[wrap.maskedSkills set_only:item.skills];

            [dictionary addObject:item];
            //[wrap release];
        }
        [extraItem release];
    }
}

-(void)getVirtualCharmList:(NSMutableArray*)listCharms:(PSWrap*)none:(NSMutableArray*)result {
    NSMutableDictionary* compact0 = [[NSMutableDictionary alloc]init];
    NSMutableDictionary* compact1 = [[NSMutableDictionary alloc]init];
    NSMutableDictionary* compact2 = [[NSMutableDictionary alloc]init];
    NSMutableDictionary* compact3 = [[NSMutableDictionary alloc]init];
    
    [none.charmGroup removeAllObjects];
    [none.charmGroupSeek removeAllObjects];

    for (int i = 0; i < listCharms.count; ++ i) {
        PSWrap* wrap = [listCharms objectAtIndex:i];
        SkillSet* plain = wrap.item.skills;
        SkillSet* masked = [plain fixColumnBy:_session.searchSkills];
        
        NSMutableDictionary* map = nil;
        switch (wrap.item.slotCount) {
            case 0:
                map = compact0;
                break;
            case 1:
                map = compact1;
                break;
            case 2:
                map = compact2;
                break;
            case 3:
                map = compact3;
                break;
        }
        PSWrap* virtual = [map objectForKey:masked];
        if (virtual == nil) {
            PSItem* v = [[PSItem alloc]init];
            v.isVirtual = TRUE;
            v.itemType = ITEM_TYPE_CHARM;
            v.slotCount = wrap.item.slotCount;
            if (v.slotCount == 0 && masked.isZero) {
                //v.isVirtualNone = TRUE;
                v.name = @"なし";
            }else {
                v.name = @"お守り";
            }
            v.skills = masked;
            virtual = [[PSWrap alloc]initWithItem:v:masked];
            [v release];
            [virtual.maskedSkills set_only:masked];
            [map setObject:virtual forKey:masked];
            if (virtual.charmGroup == nil) {
                [virtual resetCharmGroup];
            }
            [virtual release];
        }
        [none.charmGroup addObject:wrap];
        [none.charmGroupSeek addObject:virtual];
        [virtual.charmGroup addObject:wrap];
        [virtual.charmGroupSeek addObject:virtual];
        [masked release];
    }
    
    [result removeAllObjects];

    for (int i = 0; i <= 3; ++ i) {
        NSMutableDictionary* dict = nil;
        switch(i){
            case 0: dict = compact0; break;
            case 1: dict = compact1; break;
            case 2: dict = compact2; break;
            case 3: dict = compact3; break;
        }
        NSEnumerator *e = [dict keyEnumerator];
        while (TRUE){
            id key = [e nextObject];
            if (key == nil) {
                break;
            }
            id value = [dict objectForKey:key];
            
            [result addObject: value];
        }
    }
    
    for (int i = 0; i < result.count; ++i) {
        PSWrap* objI = [result objectAtIndex:i];
        for (int j = 0; j < result.count; ++j) {
            if (i == j) {
                continue;
            }
            PSWrap* objJ = [result objectAtIndex:j];
            
            if ([Engine isTargetWeakChildren: _targetSkill: objI : objJ]) {
                [objJ.charmGroup addObjectsFromArray: objI.charmGroup];
                [objJ.charmGroupSeek addObjectsFromArray: objI.charmGroupSeek];
                [result removeObjectAtIndex: i];
                i --;
                break;
            }
        }
    }
    [compact0 release];
    [compact1 release];
    [compact2 release];
    [compact3 release];
}

-(DecorationCache *)prepareDataItems: (id <IProgress>)progress: (PSBaseItems*) source: (PSItemList*)destTop : (PSItemList*)destAll {
    PSSession* session = _session;
    PSBaseItems* dest = [[PSBaseItems alloc]init];

    for (int i = 0; i < 7; ++ i) {
        for (int x = VIRTUAL_START; x <= VIRTUAL_END; ++ x) {
            PSWrap* virtual = [PSWrap getVirtualWrap: i: x];
            [virtual.sameArmors removeAllObjects];
            [virtual.childArmors removeAllObjects];
        }
    }

    [self prepareScan: ITEM_TYPE_HEAD: source.listEquipHead: dest.listEquipHead];
    [self prepareScan: ITEM_TYPE_BODY: source.listEquipBody: dest.listEquipBody];
    [self prepareScan: ITEM_TYPE_ARM: source.listEquipArm: dest.listEquipArm];
    [self prepareScan: ITEM_TYPE_WEIST: source.listEquipWeist: dest.listEquipWeist];
    [self prepareScan: ITEM_TYPE_LEG: source.listEquipLeg: dest.listEquipLeg];
    [self prepareScan: ITEM_TYPE_DECORATION :source.listDecoration :dest.listDecoration];
    
    [[PSMutex mainMutex] showMemoryAlertIfError];
    
    if (_repository.resource.existCharm) {
        if (_repository.charmDB == nil && session.searchCharmCSVTable > 0) {
            _repository.charmDB = [[CharmDB alloc]init];
            [_repository.charmDB readProperties: progress];
        }
        if (session.searchCharmCSVTable > 0) {
            if (_repository.charmDB.isPermenent) {
                if (_repository.charmDB.listCharm.count == 0) {
                    [_repository.charmDB readProperties:progress];
                }
            }else {
                [_repository.charmDB readProperties:progress];
            }
        }
        
        NSMutableArray* list0 = [[NSMutableArray alloc]init];

        [self loadExtraCharms:list0];
        [list0 addObjectsFromArray:source.listCharm];

        [self prepareScan: ITEM_TYPE_CHARM :list0 :dest.listCharm];

        [list0 release];
    }else {
        [dest.listCharm removeAllObjects];
    }
    
    //[_listForScanTop.listDecoration removeAllObjects];
    //[_listForScanTop.listDecoration addObjectsFromArray:listForCandidate.listDecoration];
    
    [self buildTreeForScan:ITEM_TYPE_HEAD: dest.listEquipHead: destTop.listEquipHead
                          :destAll.listEquipHead];
    [self buildTreeForScan: ITEM_TYPE_BODY:dest.listEquipBody: destTop.listEquipBody
                          :destAll.listEquipBody];
    [self buildTreeForScan:ITEM_TYPE_ARM:dest.listEquipArm: destTop.listEquipArm
                          :destAll.listEquipArm];
    [self buildTreeForScan:ITEM_TYPE_WEIST:dest.listEquipWeist: destTop.listEquipWeist
                          :destAll.listEquipWeist];
    [self buildTreeForScan:ITEM_TYPE_LEG: dest.listEquipLeg: destTop.listEquipLeg
                          :destAll.listEquipLeg];
    [self buildTreeForScan:ITEM_TYPE_CHARM:dest.listCharm: destTop.listCharm
                          :destAll.listCharm];
    
    DecorationCache* topMatrix = [[DecorationCache alloc]initWithDecorationList:_session];
    [topMatrix addColumn: destTop.listEquipHead];
    [topMatrix addColumn: destTop.listEquipBody];
    [topMatrix addColumn: destTop.listEquipArm];
    [topMatrix addColumn: destTop.listEquipWeist];
    [topMatrix addColumn: destTop.listEquipLeg];
    [topMatrix addColumn: destTop.listCharm];
    
    return topMatrix;
}

-(SkillSet*)doCompactSkillSet:(SkillSet*) skills {
    SkillSet* orgSkills = skills;
    
    int x = -1;
    for (int i = 0; i < orgSkills.count; ++ i) {
        if (x == -1) {
            x = i;
        }else {
            if (orgSkills.positive[i]) {
                if (orgSkills.point[i] < orgSkills.point[x]) {
                    x = i;
                    continue;
                }
            }else {
                if (orgSkills.point[i] > orgSkills.point[x]) {
                    x = i;
                    continue;
                }
            }
        }
    }
    if (x < 0) {
        return nil;
    }

    SkillSet* newSkills = [[SkillSet alloc]init];
    for (int i = 0; i < orgSkills.count; ++ i) {
        if (i == x) {
            continue;
        }
        NSMutableArray* listKind = orgSkills.listKind;
        SkillKind *kind = [listKind objectAtIndex: i];
        [newSkills set:kind :orgSkills.point[i] :orgSkills.positive[i]];
    }
    
    return newSkills;
}

-(BOOL)pageActionScan:(id<IProgress>)progress {
    Repository* repository = [Repository mainRepository];
    [_tempFlagAlready removeAllObjects];
    [_tempChildQueue removeAllObjects];
    [_tempSlotResult removeAllObjects];
    
    if (_superEngine != nil && _superEngine.listResultSearch != nil && _superEngine.listResultSearch.count > 0) {
        [_tempSlotResult addObjectsFromArray:_superEngine.listResultSearch];
    }
    
    self.isOptimizedArmor = FALSE;
    
    DecorationCache* matrix = [self prepareDataItems: progress : repository.items :_listForScanAll :_listForScanTop];
    [matrix start];

    DecorationMatcher* temporaryMatcher = [[DecorationMatcher alloc]initWithData:_session];
    DecorationMatcher* targetMatcher = nil;
    
    long cntor = 0;
    BOOL disp = true;
    
    _countMax = [matrix iteratorCount];
    
    NSMutableSet* listArmorSet = [[NSMutableSet alloc]init];
    
    [_tempChildQueue removeAllObjects];
    
    @try {
        while (TRUE) {
            [[PSMutex mainMutex] showMemoryAlertIfError];
            if (progress.cancelFlag) {
                break;
            }
            int x = 1000;
            if ((cntor % x) == 0) {
                double percent = (double) (cntor + matrix.skipCount) * 100.0 / (_countMax);
                progress.percent = percent;
                NSString *text = [[NSString alloc]initWithFormat:@"%d%% (%ld+%ld)-> %d", (int)percent, cntor, matrix.skipCount, listArmorSet.count];
                progress.labelText = text;
                [text release];
                disp = false;
            }
            cntor++;    

            PSArmorSet* armorSet = nil;
            BOOL fromQueue = FALSE;
            
            if (_tempChildQueue.count > 0) {
                armorSet = [_tempChildQueue lastObject];
                [armorSet retain];
                [_tempChildQueue removeLastObject];
                fromQueue = TRUE;
                
                if ([matrix getFailedColumn: armorSet.listArmor] >= 0) {
                    [armorSet release];
                    continue;
                }
            }else {
                armorSet = [[PSArmorSet alloc]init];
                if ([matrix fetchNext:armorSet.listArmor: progress] == FALSE) {
                    [armorSet release];
                    break;
                }
            }
            
            armorSet.targetSkills = _targetSkill;
            armorSet.weaponSlotCount = _session.searchWeaponSlotCount;
            
            if (fromQueue) {
                if ([_tempFlagAlready containsObject: armorSet]) {
                    [armorSet release];
                    continue;
                }
                [_tempFlagAlready addObject:armorSet];
            }

            if ([temporaryMatcher canHaveEnoughDecoration:armorSet :FALSE :nil]== FALSE) {
                [armorSet release];
                continue;
            }
        
            if (_session.searchMaxCount >= 3) {
                if ([self canonicariseResultSet:matrix: armorSet : temporaryMatcher]) {
                    self.isOptimizedArmor = TRUE;
                    [armorSet release];
                    continue;
                }
                //NSLog(@"canon draw %@", armorSet);
            }else {
                //NSLog(@"canon nojudge %@", armorSet);
            }

            NSMutableArray* setList = [[NSMutableArray alloc]init];
            
            if (_session.searchPlusAlpha) {
                [armorSet drawArmorSets:setList :TRUE :nil: progress: _session.searchMaxCount];
            }else {
                BOOL useExtract;
                if (_session.searchMatome) {
                    useExtract = FALSE;
                }else {
                    useExtract = TRUE;
                }
                [armorSet drawArmorSets:setList :useExtract :nil: progress: _session.searchMaxCount];
            }
            disp = true;
            _countMax += setList.count;
            
            if (progress.cancelFlag) {
                break;
            }

            for (int i = 0; i < setList.count; ++ i) {
                if (progress.cancelFlag) {
                    break;
                }
                if (listArmorSet.count >= _session.searchMaxCount) {
                    break;
                }
                PSArmorSet* set = [setList objectAtIndex:i];

                int x = 1000;
                
                cntor++;
                if ((cntor % x) == 0) {
                    double percent = (double) (cntor + matrix.skipCount) * 100.0 / (_countMax);
                    progress.percent = percent;
                    NSString *text = [[NSString alloc]initWithFormat:@"%d%% (%ld+%ld) -> %d", (int)percent,cntor, matrix.skipCount,listArmorSet.count];
                    progress.labelText = text;
                    [text release];
                    disp = false;
                }
                
                [listArmorSet addObject: set];
            }
            [setList release];
            setList = nil;
            if (progress.cancelFlag) {
                break;
            }
            disp = true;
            if (listArmorSet.count >= _session.searchMaxCount) {
                break;
            }
            int n = [armorSet createChildSet:_tempChildQueue checker:_tempFlagAlready];
            _countMax += n;
            [armorSet release];
        }
        [[PSMutex mainMutex] showMemoryAlertIfError];
    }@catch (NSException *exception) {
        NSLog(@"exception %@ %@", exception, [exception callStackSymbols]);
    }@finally {
        [targetMatcher release];
        [temporaryMatcher release];

        [_tempFlagAlready removeAllObjects];
        [_tempChildQueue removeAllObjects];
        [_tempSlotResult removeAllObjects];
    }

    [_listResultSearch release];
    _listResultSearch = [[NSMutableArray alloc]initWithArray: listArmorSet.allObjects];
    for (int i = 0; i < _listResultSearch.count; ++ i) {
        PSArmorSet* set = [_listResultSearch objectAtIndex:i];
        set.hunterType = _session.searchHunterType;
        set.townRank = _session.searchTownRank;
        set.hunterRank = _session.searchHunterRank;
        set.genderType = _session.searchGenderType;
        set.weaponSlotCount = _session.searchWeaponSlotCount;
        set.targetSkills = _session.searchSkills;
    }
    [listArmorSet release];
    listArmorSet = nil;

    float percent = 100;
    progress.percent = percent;
    NSString *text = [[NSString alloc]initWithFormat:@"%d%%(%ld+%ld) -> %d", (int)percent, cntor, matrix.skipCount, _listResultSearch.count];
    progress.labelText = text;
    [text release];
    [matrix release];

    if (_listResultSearch.count > 0) {
        return TRUE;
    }else {
        return FALSE;
    }
}

-(void) filterByViewCount: (NSMutableArray*)armors: (NSMutableArray*)result {
    [result removeAllObjects];
    for (int i = 0; i < armors.count; i ++){
        PSWrap* armor = [armors objectAtIndex:i];
        if (armor.item.viewCount > 0) {
            [result addObject: armor];
        }
    }
}

-(BOOL)isCharmHaveReplaceMent:(DecorationCache*) cache: (DecorationMatcher *)matcher :(PSArmorSet *)set
{
    bool found = false;
    int type = ITEM_TYPE_CHARM;
    PSWrap* noneItem = [PSWrap getVirtualWrap:type :0];
    PSWrap* original = [set.listArmor objectAtIndex:type];
    if (original == nil) {
        return false;
    }
    if (original.item.slotCount == 0 && original.item.isVirtual) {
        return false;
    }
    
    [set.listArmor replaceObjectAtIndex:type withObject:noneItem];
    
    if ([cache getFailedColumn: set.listArmor] < 0
     && [matcher canHaveEnoughDecoration:set: FALSE: nil]) {
        found = true;
    }
    else {
        for (int i = 0; i < original.childArmors.count; ++ i) {
            PSWrap* replace = [original.childArmors objectAtIndex:i];
            if (replace.item == original.item) {
                continue;
            }
            [set.listArmor replaceObjectAtIndex:type withObject:replace];
            if ([cache getFailedColumn: set.listArmor] < 0
             && [matcher canHaveEnoughDecoration:set  :FALSE :nil]) {
                found = true;
            }
        }
    }
    
    [set.listArmor replaceObjectAtIndex:type withObject:original];
    [set calculateUseList: matcher.targetSkills];
    return found;
}

-(BOOL)canonicariseResultSet:(DecorationCache*) cache:(PSArmorSet*) set: (DecorationMatcher*) matcher {
    if (set.weaponSlotCount >= 1) {
        /*TODO
        int realWeaponSlotCount = [matcher fixRealWeaponSlot: set: _tempSlotResult];
        if (realWeaponSlotCount != set.weaponSlotCount) {
            set.weaponSlotCount = realWeaponSlotCount;
        }
        */
    }

    BOOL haveSkip = false;
    int max;
    if (_repository.resource.existCharm) {
        max = ITEM_TYPE_CHARM;
    }else {
        max = ITEM_TYPE_LEG;
    }
    for (int type = ITEM_TYPE_HEAD; type <= max; type ++) {
        PSWrap* wrap = [set.listArmor objectAtIndex:type];
        PSItem* item = wrap.item;
        
        if (item.slotCount == 0 && item.isCopieSkill == false && [item.skills isZero]) {
            continue;
        }
        if (item.slotCount > 0 && item.isVirtual) {
            PSArmorSet* newSet2 = [set createCopy];
            
            PSWrap* virtual = [PSWrap getVirtualWrap: type: item.slotCount- 1];
            [newSet2.listArmor replaceObjectAtIndex:type withObject:virtual]; 
            if ([cache getFailedColumn: newSet2.listArmor] < 0
             && [matcher canHaveEnoughDecoration:newSet2:FALSE:nil]) {
                if ([_tempFlagAlready containsObject:newSet2]) {
                    haveSkip = true;
                    [newSet2 release];
                    break;
                }
                [_tempChildQueue addObject:newSet2];
                _countMax ++;
                haveSkip = true;
                [newSet2 release];
                break;
            }else {
                [newSet2 release];
                continue;
            }
        }
        
        PSArmorSet* newSet = [set createCopy];
        PSWrap* virtual = [PSWrap getVirtualWrap:type :0];                    
        [newSet.listArmor replaceObjectAtIndex:type withObject:virtual];
        
        if ([cache getFailedColumn: newSet.listArmor] < 0
         && [matcher canHaveEnoughDecoration:newSet:FALSE:nil]) {
            if ([_tempFlagAlready containsObject:newSet]) {
                haveSkip = true;
                [newSet release];
                continue;
            }else {
                [_tempChildQueue addObject:newSet];
                _countMax ++;
                haveSkip = true;
            }
        }
        [newSet release];
    }
    if (haveSkip) {
        return TRUE;
    }
    if (_repository.resource.existCharm) {
        if ([self isCharmHaveReplaceMent: cache: matcher : set]) {
            _countMax += [set createChildSet:_tempChildQueue checker:_tempFlagAlready];
            return TRUE;
        }
    }
    return FALSE;
}

-(void)loadResult:(Engine*)another
{
    [self.listResultSearch addObjectsFromArray: another.listResultSearch];
    self.isOptimizedArmor = another.isOptimizedArmor;
}

@end
