//
//  DecorationCache.m
//  ArmorP3V1
//

#import "DecorationCache.h"
#import "Repository.h"
#import "PSSession.h"

@implementation DecorationCache

@synthesize decoMaster = _decoMaster;
@synthesize decoForSlot = _decoForSlot;
@synthesize skillForSlot = _skillForSlot;
@synthesize borderForSlot = _borderForSlot;
@synthesize targetSkill = _targetSkill;
@synthesize matrixForSearch = _matrixForSearch;
@synthesize skillForSearch = _skillForSearch;
@synthesize skillForSearchCalcA = _skillForSearchCalcA;
@synthesize fetch = _fetch;
@synthesize arrayColumnList = _arrayColumnList;
@synthesize rowIndex = _rowIndex;
@synthesize endOfMatrix = _endOfMatrix;
@synthesize totalNeeds = _totalNeeds;
@synthesize needAboutParts = _needAboutParts;
@synthesize needAboutCopie = _needAboutCopie;
@synthesize skipCount = _skipCount;
@synthesize matcherSet = _matcherSet;
@synthesize weaponSlotCount = _weaponSlotCount;
@synthesize bodyCopyAvail = _bodyCopyAvail;

-(void)dealloc {
    [_decoMaster release];
    [_targetSkill release];
    [_skillForSearch release];
    [_skillForSearchCalcA release];
    [_fetch release];
    [_matrixForSearch release];
    [_arrayColumnList release];
    [_matcherSet release];
    
    for (int i = 0; i <= 3; ++ i) {
        if (_decoForSlot != nil) {
            [_decoForSlot[i] release];
        }
        if (_skillForSlot != nil) {
            [_skillForSlot[i] release];
        }
    }
    free(_decoForSlot);
    free(_skillForSlot);
    free(_borderForSlot);
    free(_rowIndex);
    free(_needAboutParts);
    free(_needAboutCopie);
    free(_bodyCopyAvail);
    
    [super dealloc];
}

-(id)initWithDecorationList:(PSSession*)session {
    //ここにバグがあるように思われます。スキル毎に、とりえる最大値を取得していますが、正しくは装飾品ごとの＋－両方のとりえる最大値の合計が必要です
    self = [super init];
    if (self) {
        _arrayColumnList = [[NSMutableArray alloc]init];
        
        _targetSkill = session.searchSkills;
        [_targetSkill retain];
        _skillForSearch = [[SkillSet alloc]initWithColumn:_targetSkill];
        _skillForSearchCalcA = [[SkillSet alloc]initWithColumn:_targetSkill];
        _weaponSlotCount = session.searchWeaponSlotCount;
        _bodyCopyAvail = calloc(sizeof(BOOL), 6);
        
        _decoForSlot = calloc(4, sizeof(void*) * 4);
        for(int i = 0; i <= 3; i ++) {
            _decoForSlot[i] = [[NSMutableArray alloc]init];
        }
        _skillForSlot = calloc(4, sizeof(void*) * 4);
        for(int i = 0; i <= 3; i ++) {
            _skillForSlot[i] = [[NSMutableArray alloc]init];
        }
        
        NSMutableArray* decoList = nil;
        Repository* repository = [Repository mainRepository];
        decoList = repository.items.listDecoration;
        
        _decoMaster = decoList;
        [_decoMaster retain];
        
        for (int i = 0; i < decoList.count; ++ i) {
            PSItem* d = [decoList objectAtIndex:i];
            if ([self decoIsForSkillKind:d :_targetSkill]) {
                switch (d.slotCount) {
                    case 0:
                        break;
                    case 1:
                        [_decoForSlot[1] addObject: d];
                        [_decoForSlot[2] addObject: d];
                        [_decoForSlot[3] addObject: d];
                        break;
                    case 2:
                        [_decoForSlot[2] addObject: d];
                        [_decoForSlot[3] addObject: d];
                        break;
                    case 3:
                        [_decoForSlot[3] addObject: d];
                        break;
                }
            }
        }
        for (int i = 0; i <= 3; ++ i) {
            [self listupSkillFor:_skillForSlot[i] :_decoForSlot[i] :i];
        }
        
        _borderForSlot = calloc(4, sizeof(int));
        for (int i = 0; i <= 3; ++ i) {
            int border = 0;
            
            NSMutableArray* listSkill = _skillForSlot[i];
            for (int j = 0; j < listSkill.count; ++ j) {
                SkillSet* skill = [listSkill objectAtIndex: j];
                int sumPoint = 0;
                for (int k = 0; k < skill.listKind.count; ++ k) {
                    if (skill.positive[k]) {
                        if (skill.point[k] > 0) {
                            sumPoint += skill.point[k];
                        }
                    }else {
                        if (skill.point[k] < 0) {
                            sumPoint -= skill.point[k];
                        }
                    }
                }
                if (sumPoint > border) {
                    border = sumPoint;
                }
            }
            _borderForSlot[i] = border;
            //NSLog(@"border[%d] = %d", i, border);
        }
        
        _matrixForSearch = [[MatrixBuilder alloc]init];
        _fetch = [[NSMutableArray alloc]init];
        
        session.searchSkills = _targetSkill;

    }
    return self;
}

-(void)listupSkillForSet:(NSMutableSet*)result:(SkillSet*)current:(int)slotCount:(NSMutableArray*)listDeco {
    if (slotCount == 0) {
        [result addObject:current];
        return;
    }
    
    BOOL did = FALSE;
    
    for (int i = 0; i < listDeco.count; ++ i) {
        PSItem* d = [listDeco objectAtIndex:i];
        if (slotCount >= d.slotCount) {
            SkillSet* next = [[SkillSet alloc]init];
            [next set_all:current];
            [next sum_only: d.skills];
            int nextSlot = slotCount - d.slotCount;
            [self listupSkillForSet:result :next :nextSlot :listDeco];
            [next release];
            did = TRUE;
        }
    }
    
    if (did == FALSE) {
        [result addObject:current];
        return;
    }
}

-(void)listupSkillFor:(NSMutableArray*)skills:(NSMutableArray*)deco:(int)slotCount {
    NSMutableSet* result = [[NSMutableSet alloc]init];
    
    SkillSet* empty = [[SkillSet alloc]initWithColumn:_targetSkill];
    
    [self listupSkillForSet:result : empty :slotCount :deco];
    
    for (SkillSet* s in result) {
        [skills addObject:s];
    }
    [result release];
    if (skills.count == 0) {
        SkillSet* nilObj = [[SkillSet alloc]initWithColumn:_targetSkill];
        [skills addObject:nilObj];
        [nilObj release];
    }
}

-(BOOL)decoIsForSkillKind:(PSItem*) item: (SkillSet*) skills {
    for (int i = 0; i < skills.count; ++i) {
        SkillKind* kind = [skills.listKind objectAtIndex:i];
        BOOL range = skills.positive[i];
        //int point1 = skills.point[i];
        
        int x = [item.skills findByKind:kind];
        if (x >= 0) {
            int point2 = item.skills.point[x];
            if (range) {
                if (point2 > 0) {
                    return TRUE;
                }
            } else {
                if (point2 < 0) {
                    return TRUE;
                }
            }
        }
    }
    return FALSE;
}

-(void)cleanup {
    [_arrayColumnList removeAllObjects];
    free(_rowIndex);
    _rowIndex = nil;
}

-(NSString*)find:(NSArray*)array:(NSString*)prefix {
    for (int i = 0; i < array.count; ++ i) {
        PSWrap *w = [array objectAtIndex:i];
        if ([w.item.name hasPrefix: prefix]) {
            return w.item.name;
        }
    }
    return nil;
}

-(void)addColumn:(NSArray*)column
{
    [_arrayColumnList addObject: column];
}

-(long)iteratorCount
{
    long x = 1;
    for (int i = 0; i < _arrayColumnList.count; ++i) {
        NSArray* e = [_arrayColumnList objectAtIndex: i];
        x *= e.count == 0 ? 1 : e.count;
    }
    return x;
}

NSComparisonResult listCompare (id obj1, id obj2, void* context)
{
    //自分が比較したいオブジェクトに変換
    PSWrap* my = obj1;
    PSWrap* target = obj2;
    
    if (my.item.isCopieSkill)
        return NSOrderedAscending;
    if (target.item.isCopieSkill) 
        return NSOrderedDescending;
    
    //値を比較して（ここではvalueという物）それに当たる物を返す。
    if (my.maskedSkillsSummary > target.maskedSkillsSummary)
        return NSOrderedAscending;
    else if (my.maskedSkillsSummary == target.maskedSkillsSummary)
        return NSOrderedSame;
    else
        return NSOrderedDescending;
}

-(void)start
{
    if (_rowIndex != nil) {
        free(_rowIndex);
    }
    if (_needAboutParts != nil) {
        free(_needAboutParts);
    }
    if (_needAboutCopie != nil) {
        free(_needAboutCopie);
    }
    NSMutableArray* array0 = [_arrayColumnList objectAtIndex:0];
    NSMutableArray* array1 = [_arrayColumnList objectAtIndex:1];
    
    PSWrap* w00 = [array0 objectAtIndex:0];
    //PSWrap* w10 = [array1 objectAtIndex:0];
    
    if (w00.item.itemType != ITEM_TYPE_BODY) {
        [array0 retain];
        [array1 retain];
        
        [_arrayColumnList replaceObjectAtIndex:1 withObject:array0];
        [_arrayColumnList replaceObjectAtIndex:0 withObject:array1];
        
        [array0 release];
        [array1 release];
    }
    _rowIndex = calloc(sizeof(int), _arrayColumnList.count);
    _needAboutParts = calloc(sizeof(int), _arrayColumnList.count);
    _needAboutCopie = calloc(sizeof(int), _arrayColumnList.count);
    int* topOfTheParts = calloc(sizeof(int), _arrayColumnList.count); 
    
    for (int i = 0; i < _arrayColumnList.count; ++ i) {
        NSMutableArray* column = [_arrayColumnList objectAtIndex:i];
        
        long top = 0;
        for (int j = 0; j < column.count; ++ j) {
            PSWrap* wrap = [column objectAtIndex:j];
            int tmp = _borderForSlot[wrap.item.slotCount];
            SkillSet* set = wrap.maskedSkills;
            if (wrap.maskedSkills == nil || set.listKind != _targetSkill.listKind) {
                set = [[SkillSet alloc]initWithColumn:_targetSkill];
                [set sum_only:wrap.item.skills];
                wrap.maskedSkills = set;
                [set release];
            }
            for (int x =0 ; x < set.listKind.count; ++ x) {
                if (set.positive[x]) {
                    if (set.point[x] > 0) {
                        if (set.point[x] > _targetSkill.point[x]) {
                            tmp += _targetSkill.point[x]; //広域２ピアスなどの対策
                        }else {
                            tmp += set.point[x];
                        }
                    }
                }else {
                    if (set.point[x] < 0)
                        tmp -= set.point[x];
                }
            }
            wrap.maskedSkillsSummary = tmp;
            //NSLog(@"%@ + %d = %d", wrap.maskedSkills, wrap.item.slotCount, tmp);
            if (tmp > top) {
                top = tmp;
            }
        }
        [column sortUsingFunction:listCompare context:nil];
        //PSWrap* first = [column objectAtIndex:0];
        //NSLog(@"top[%d] = %ld %@", i, top, first.item.name);
        topOfTheParts[i] = top;
    }
    _totalNeeds = 0;
    for (int i = 0; i < _targetSkill.listKind.count; ++ i) {
        int point = _targetSkill.point[i];
        if (_targetSkill.positive[i]) {
            if (point >= 0)
                _totalNeeds += point;
        }else {
            if (point <= 0)
                _totalNeeds -= point; 
        }
    }
    for (int i = 0; i < _arrayColumnList.count; ++ i) {
        int needs = _totalNeeds;
        int needsBody = _totalNeeds;
        for (int j = i + 1; j < _arrayColumnList.count; ++ j) {
            needs -= topOfTheParts[j];
            if (topOfTheParts[0] > topOfTheParts[j]) {
                needsBody -= topOfTheParts[0];
            }else {
                needsBody -= topOfTheParts[j];
            }
        }
        _needAboutParts[i] = needs;
        _needAboutCopie[i] = needsBody;
        //NSLog(@"needsAboutParts %d = %d copie = %d", i, needs, needsBody);
    }
    //NSLog(@"totalNeeds = %d", _totalNeeds);
    _skipCount = 0;
}

-(int)getFailedColumn:(NSArray*)armors {
    int summary = 0;
    int failed = -1;
    BOOL hasBodyCopie = FALSE;
    for (int x = 0; x < armors.count; ++ x) {
        PSWrap* wrap = [armors objectAtIndex:x];
        if (wrap.item.isCopieSkill) {
            hasBodyCopie = TRUE;
            break;
        }
    }
    if (hasBodyCopie) {
        PSWrap* body = [armors objectAtIndex:0];
        for (int x = 0; x < armors.count; ++ x) {
            PSWrap* wrap = [armors objectAtIndex:x];
            if (wrap.item.isCopieSkill) {
                summary += body.maskedSkillsSummary;
            }else {
                summary += wrap.maskedSkillsSummary;
            }
            if (_needAboutCopie[x] > summary) {
                /*
                 NSLog(@"failed %d > %d", _needAboutCopie[x], summary);
                 for (int i = 0; i <= x; ++ i) {
                 PSWrap* wp = [result objectAtIndex:i];
                 if (wp.item.isCopieSkill) {
                 wp = [result objectAtIndex:0];
                 NSLog(@"%d copie = %@ %@ %d", wp.maskedSkillsSummary, wp.item.name, wp.maskedSkills, wp.item.slotCount);
                 }else {
                 NSLog(@"%d = %@ %@ %d", wp.maskedSkillsSummary, wp.item.name, wp.maskedSkills, wp.item.slotCount);
                 }
                 }
                 */
                failed = x;
                break;
            }else {
                /*
                 NSLog(@"ok %d <= %d", _needAboutCopie[x], summary);
                 for (int i = 0; i <= x; ++ i) {
                 PSWrap* wp = [result objectAtIndex:i];
                 if (wp.item.isCopieSkill) {
                 wp = [result objectAtIndex:0];
                 NSLog(@"%d copie = %@ %@ %d", wp.maskedSkillsSummary, wp.item.name, wp.maskedSkills, wp.item.slotCount);
                 }else {
                 NSLog(@"%d = %@ %@ %d", wp.maskedSkillsSummary, wp.item.name, wp.maskedSkills, wp.item.slotCount);
                 }
                 }
                 */
            }
        }
    }else {
        for (int x = 0; x < armors.count; ++ x) {
            PSWrap* wrap = [armors objectAtIndex:x];
            summary += wrap.maskedSkillsSummary;
            if (_needAboutParts[x] > summary) {
                failed = x;
                break;
            }
        }
    }
    return failed;
}


-(BOOL)fetchNext:(NSMutableArray *)result:(id<IProgress>)progress {
    BOOL ret;
    //NSLog(@"%ld", self.iteratorCount);
    while ((ret = [self fetchIntenal:result]) != FALSE) {
        if (progress.cancelFlag) {
            break;
        }
        int failed = [self getFailedColumn:result];
        if (failed >= 0) {
            BOOL hasBodyCopie = FALSE;
            for (int x = 0; x < result.count; ++ x) {
                PSWrap* wrap = [result objectAtIndex:x];
                if (wrap.item.isCopieSkill) {
                    hasBodyCopie = TRUE;
                    break;
                }
            }
            if (hasBodyCopie == FALSE && failed >= 1) {
                [self skipToNext: failed - 1];
            }else {
                [self skipToNext: failed - 0];
            }
            continue;
        }else {
            [self skipToNext: _arrayColumnList.count - 1];
            break;
        }
    }
    
    if (ret) {
        PSWrap* body = [result objectAtIndex: 0];
        PSWrap* head = [result objectAtIndex: 1];
        
        [body retain];
        [head retain];
        
        [result replaceObjectAtIndex:0 withObject:head];
        [result replaceObjectAtIndex:1 withObject:body];
        
        [body release];
        [head release];
    }
    
    return ret;
}

-(BOOL)fetchIntenal:(NSMutableArray *)result
{
    if (_endOfMatrix) {
        return FALSE;
    }
    //NSLog(@"fetch %d %d %d %d %d %d", _rowIndex[0], _rowIndex[1], _rowIndex[2], _rowIndex[3], _rowIndex[4], _rowIndex[5]);
    [result removeAllObjects]; 
    for (int i = 0; i < _arrayColumnList.count; ++i) {
        NSArray* e = (NSArray*)[_arrayColumnList objectAtIndex: i];
        [result addObject: [e objectAtIndex: _rowIndex[i]]];
    }
    return TRUE;
}

-(BOOL)skipToNext:(int)columnId {
    if (columnId <= 0) {
        _endOfMatrix = TRUE;
        return FALSE;
    }
    while (TRUE) {
        NSArray* e = (NSArray*)[_arrayColumnList objectAtIndex: columnId];
        _rowIndex[columnId]++;
        if (columnId == _arrayColumnList.count - 1) {
            
        }else {
            int n = 1;
            for (int j = columnId+ 1; j < _arrayColumnList.count; ++ j) {
                NSMutableArray* ar2 = [_arrayColumnList objectAtIndex: j];
                n *= ar2.count;
                n -= _rowIndex[j];
                //NSLog(@"ar2 %d row %d n %d", ar2.count, _rowIndex[j], n);
            }
            _skipCount += n;
        }
        for (int x = columnId + 1; x < _arrayColumnList.count; ++ x) {
            _rowIndex[x] = 0;
        }

        if (_rowIndex[columnId] >= e.count) {
            columnId--;
            if (columnId < 0) {
                _endOfMatrix = TRUE;
                return FALSE;
            }
            continue;
        }else {
            return TRUE;
        }
    }
}

@end
