//
//  SkillSet.m
//  MHP3SkillSim
//

#import "SkillPoint.h"
#import "SkillSet.h"
#import "Repository.h"
#import "PSMutex.h"

@implementation SkillSet

//@synthesize posAlloc = _posAlloc;
@synthesize listKind = _listKind;
//@synthesize count = _count;
@synthesize point = _point;
@synthesize positive = _positive;
@synthesize capacity = _capacity;

-(NSUInteger)hash {
    int hash = 0;
    for (int i = 0; i < _listKind.count; ++ i) {
        SkillKind* kind = [_listKind objectAtIndex:i];
        hash += _point[i] * kind.name.hash;
    }
    return hash;
}

-(BOOL)isEqual:(id)object {
    SkillSet* obj = object;
    if ([SkillSet compareSkillSet:self to:obj] == 0) {
        return TRUE;
    }
    return FALSE;
}

-(int)count {
    return _listKind.count;
}

-(void)removeAllObjects {
    NSLog(@"trace = %@", [NSThread callStackSymbols]);
    abort();
}

-(id)copyWithZone:(NSZone *)zone {
    [self retain];
    return self;
}

+(int)compareSkillSet:(id)a to:(id)b {
    SkillSet* set1 = a;
    SkillSet* set2 = b;
    if (set1 == set2) {
        return 0;
    }
    
    if (set1.listKind == set2.listKind) {
        for (int i = 0; i < set1.count; ++ i) {
            int point1 = set1.point[i];
            int point2 = set2.point[i];
            
            if (point1 < point2) {
                return -1;
            }
            if (point1 > point2) {
                return 1;
            }
        }
        return 0;
    }else if ([set1 isFixedBy: set2]) {
        for (int i = 0; i < set1.count; ++ i) {
            int point1 = set1.point[i];
            int point2 = set2.point[i];
            
            if (point1 < point2) {
                return -1;
            }
            if (point1 > point2) {
                return 1;
            }
        }
        return 0;
    }
    
    Repository* repo = [Repository mainRepository];
    SkillDB* skillDB = repo.skillDB;
    
    for (int i = 0; i < [skillDB countOfKind]; ++ i) {
        SkillKind* kind = [skillDB kindOfIndex: i];
        int p1 = [set1 findByKind:kind];
        int point1 = (p1 < 0) ? 0 : set1.point[p1];
        int p2 = [set2 findByKind:kind];
        int point2 = (p2 < 0) ? 0 : set2.point[p2];
        
        if (point1 == point2 && point1 == 0) {
            continue;
        }
        
        if (point1 < point2) {
            return -1;
        }
        if (point1 > point2) {
            return 1;
        }
    }
    return 0;
}

-(id)init
{
    self = [super init];
    if (self) {
        _capacity = 0;
        _point = nil;
        _positive = nil;
        _listKind = nil;
    }
    return self;
//    return [self initWithCapacity:10];
}

-(id)initWithCapacity:(int)x
{
    self = [super init];
    if (self) {
        _capacity = 0;
        _point = nil;
        _positive = nil;
        _listKind = nil;

        [self ensureCapacity:x];
        
        //_posAlloc = [[NSString alloc]initWithFormat:@"%@", [NSThread callStackSymbols]];
        
        //[self checkMemories];
    }
    else {
        [[PSMutex mainMutex]raiseMemoryError];
    }
    return self;
}

-(id)initWithColumn: (SkillSet*)filter {
    self = [super init];
    if (self) {
        [self set_all:filter];
        for (int i = 0; i < _listKind.count; ++ i) {
            _point[i] = 0;
        }
        //[self checkMemories];
    }
    else {
        [[PSMutex mainMutex]raiseMemoryError];
    }
    return self;
}

-(void)dealloc
{
    @try {
        [self cleanup];
        //[self checkMemories];
        //[_posAlloc release];
        [super dealloc];
    }
    @catch (NSException *exception) {
        NSLog(@"exception %@ %@", exception, [exception callStackSymbols]);
    }
}

- (void) cleanup
{
    _capacity = 0;
    if (_listKind != nil) {
        [_listKind release];
        _listKind = nil;
    }
    if (_point != nil) {
        free(_point);
        _point = nil;
    }
    if (_positive != nil) {
        free(_positive);
        _positive = nil;
    }
    //[self checkMemories];
}

-(void)ensureCapacity:(int)x {
    //[self checkMemories];
    if (x < 10) {
        x = 10;
    }
    if (x > _capacity) {
        if (_listKind == nil) {
            _listKind = [[NSMutableArray alloc]init];
            if (_listKind == nil) {
                [[PSMutex mainMutex]raiseMemoryError];
            }
        }
        if (_listKind.retainCount >= 2) {
            NSMutableArray* ar2 = [[NSMutableArray alloc]initWithArray:_listKind];
            [_listKind release];
            _listKind = ar2;
            if (ar2 == nil) {
                [[PSMutex mainMutex]raiseMemoryError];
            }
        }
        int* _newPoint = calloc(sizeof(int), x + 10);
        BOOL* _newPositive = calloc(sizeof(BOOL), x + 10);
        if (_newPoint != nil && _newPositive != nil) {
            for(int i = 0; i < _capacity; ++ i) {
                _newPoint[i] = _point[i];
                _newPositive[i] = _positive[i];
            }
            free(_point);
            free(_positive);
            _point = _newPoint;
            _positive = _newPositive;
            _capacity = x;
        }else {
            [[PSMutex mainMutex]raiseMemoryError];
            free(_point);
            free(_positive);
            _point = nil;
            _positive = nil;
        }
    }
    //[self checkMemories];
}

-(void)set: (SkillKind*) kind: (NSInteger) point: (BOOL) range {
    @try {
        //[self checkMemories];
        int x = [self findByKind:kind];
        
        if (x >= 0) {
            _point[x] = point;
            _positive[x] = range;
            return;
        }
        
        if (_capacity >= _listKind.count + 2) {
            if (_listKind.retainCount >= 2) {
                NSMutableArray* ar2 = [[NSMutableArray alloc]initWithArray:_listKind];
                [_listKind release];
                _listKind = ar2;
                if (ar2 == nil) {
                    [[PSMutex mainMutex]raiseMemoryError];
               }
            }
        }else {
            [self ensureCapacity: _listKind.count + 2];
        }
        
        int count = _listKind.count;
        [_listKind addObject:kind];
        _point[count] = point;
        _positive[count] = range;
    }
    @catch (NSException *exception) {
        NSLog(@"exception %@ %@", exception, [exception callStackSymbols]);
    }
}

-(BOOL) isFixedBy: (SkillSet*)filter {
    //[self checkMemories];
    if (self.count != filter.count) {
        return FALSE;
    }
    for (int i = 0; i < filter.count; ++i) {
        SkillKind *p1 = [_listKind objectAtIndex: i];
        SkillKind *p2 = [filter.listKind objectAtIndex: i];
        if (p1 != p2) {
            return FALSE;
        }
    }
    return TRUE;
}

-(int) findByKind: (SkillKind*) kind {
    //[self checkMemories];
    for(int i = 0; i < _listKind.count; ++ i) {
        SkillKind* p = [_listKind objectAtIndex: i];
        if (p == kind) {
            return i;
        }
    }
    return -1;
}

-(SkillSet*) fixColumnBy: (SkillSet*) filter {
    //[self checkMemories];
    SkillSet* obj = [[SkillSet alloc]initWithColumn:filter];
    for (int i = 0; i < filter.count; ++i) {
        SkillKind* kind = [filter.listKind objectAtIndex:i];
        int index = [self findByKind:kind];
        int point = index < 0 ? 0 : _point[index];
        BOOL range = index < 0 ? TRUE : filter.positive[i];
        
        obj.point[i] = point;
        obj.positive[i] = range;
    }
    return obj;
}
    
-(void) minus_fixed: (SkillSet*) slotSkill {
    //[self checkMemories];
    if (self.count != slotSkill.count) {
        NSException *exception = [[NSException alloc]initWithName:@"memoryerror" reason:@"nil" userInfo:nil];
        [exception raise];
    }
    for (int i = 0; i < _listKind.count; ++i) {
        SkillKind* p1 = [_listKind objectAtIndex:i];
        SkillKind* p2 = [slotSkill.listKind objectAtIndex: i];
        if (p1 != p2) {
            NSException *exception = [[NSException alloc]initWithName:@"memoryerror" reason:@"nil" userInfo:nil];
            [exception raise];
        }
        self.point[i] -= slotSkill.point[i];
    }
}

-(void) minus_only: (SkillSet*) slotSkill {
    //[self checkMemories];
    for (int i = 0; i < _listKind.count; ++i) {
        SkillKind* kind = [_listKind objectAtIndex:i];
        int x1 = i;
        int x2 = [slotSkill findByKind: kind];
        
        if (x2 >= 0) {
            self.point[x1] -= slotSkill.point[x2];
        }
    }
}

-(void) sum_only: (SkillSet*) slotSkill {
    //[self checkMemories];
    for (int i = 0; i < _listKind.count; ++i) {
        SkillKind* kind = [_listKind objectAtIndex:i];
        int x1 = i;
        int x2 = [slotSkill findByKind: kind];
        
        if (x2 >= 0) {
            self.point[x1] += slotSkill.point[x2];
        }
    }
}

-(void) sum_all: (SkillSet*) sum {
    //[self checkMemories];
    if (_listKind.count == 0) {
        [self set_all: sum];
        return;
    }
    if (sum.count == 0) {
        return;
    }
    if ([self isFixedBy: sum]) {
        [self sum_fixed: sum];
        return;
    }
    if (_listKind.retainCount >= 2) {
        NSMutableArray* ar2 = [[NSMutableArray alloc]initWithArray:_listKind];
        [_listKind release];
        _listKind = ar2;
    }
    for (int i = 0; i < sum.count; ++i) {
        SkillKind* kind = [sum.listKind objectAtIndex:i];
        int x1 = [self findByKind: kind];
        int x2 = [sum findByKind: kind];

        if (x1 >= 0) {
            self.point[x1] += sum.point[x2];
        }else {
            [self set:kind :sum.point[x2] :sum.positive[x2]];
        }
    }
}

-(void) sum_fixed: (SkillSet*) slotSkill {
    //[self checkMemories];
    if (_listKind.count == 0) {
        [self set_all: slotSkill];
        return;
    }
    if (slotSkill.count == 0) {
        return;
    }
    if (self.count != slotSkill.count) {
        NSLog(@"abort class error count %d != %d", self.count, slotSkill.count);
        return;
    }
    for (int i = 0; i < _listKind.count; ++i) {
        SkillKind* p1 = [_listKind objectAtIndex: i];
        SkillKind* p2 = [slotSkill.listKind objectAtIndex: i];
        if (p1 != p2) {
            NSException *exception = [[NSException alloc]initWithName:@"memoryerror" reason:@"nil" userInfo:nil];
            [exception raise];
        }
        self.point[i] += slotSkill.point[i];
    }
}

-(void) set_all: (SkillSet*) slotSkill {
    [self ensureCapacity: slotSkill.count];

    [_listKind release];
    _listKind = slotSkill.listKind;
    [_listKind retain];
    
    for (int i = 0; i < _listKind.count; ++i) {
        _point[i] = slotSkill.point[i];
        _positive[i] = slotSkill.positive[i];
    }
    //[self checkMemories];
}

-(void) set_only: (SkillSet*) slotSkill {
    //[self checkMemories];
    for (int i = 0; i < slotSkill.count; ++ i) {
        SkillKind *kind = [slotSkill.listKind objectAtIndex:i];
        int current = [self findByKind:kind];
        if (current >= 0) {
            _point[current] = slotSkill.point[i];
            //_positive[current] = slotSkill.positive[i];
        }
    }
}

-(BOOL)isOver: (SkillSet*)slotSkill {
    //[self checkMemories];
    for(int i = 0; i < slotSkill.count; ++ i) {
        int p1 = i;
        SkillKind* kind = [slotSkill.listKind objectAtIndex:i];
        int p2 = [self findByKind:kind];

        if (p2 < 0) {
            continue;
        }

        BOOL range = slotSkill.positive[p1];
        int point1 = slotSkill.point[p1];
        int point2 = self.point[p2];
        
        if (range) {
            if (point1 < point2) {
                return FALSE;
            }
        } else {
            if (point2 > point1) {
                return FALSE;
            }
        }
    }
    for(int i = 0; i < _listKind.count; ++ i) {
        int p1 = i;
        SkillKind* kind = [_listKind objectAtIndex:p1];
        int p2 = [slotSkill findByKind:kind];

        if (p2 < 0) {
            return FALSE;
        }
    }
    return TRUE;
}

-(void)skillToString: (NSMutableString*) str : (SEL) sorter: (BOOL) ignoreZero {
    //[self checkMemories];

    NSMutableArray* value = [[NSMutableArray alloc]init];
    
    for (int i = 0; i < _listKind.count; ++i) {
        SkillKind* kind = [_listKind objectAtIndex: i];
        int point = _point[i];
        BOOL positive = _positive[i];
        
        if (!ignoreZero || point != 0) {
            SkillPoint* p = [[SkillPoint alloc]init];
            p.skillKind = kind;
            p.skillPoint = point;
            p.positiveRange = positive;
            [value addObject:p];
            [p release];
        }
    }/*
    if (sorter != nil) {
        NSArray* array = [value sortedArrayUsingSelector:sorter];
        NSLog(@"done");
        [value removeAllObjects];
        [value addObjectsFromArray:array];
        [array release];
    }
    */
    [str appendString:@"【"];

    for(int i = 0; i < value.count; ++ i) {
        SkillPoint *p = [value objectAtIndex:i];
        if (i != 0) {
            [str appendString:@","];
        }
        SkillKind* kind0 = p.skillKind;
        [str appendString:kind0.name];

        if (p.skillPoint > 0) {
            [str appendString: @"+"];
        }
        [str appendFormat:@"%d", p.skillPoint];
        if (p.positiveRange == FALSE) {
            [str appendString: @"--"];
        }
    }
    [str appendString:@"】"];
    [value release];
}

-(NSMutableArray*) getNamedSkillNameForDisplay {
    //[self checkMemories];

    NSMutableArray* result = [[NSMutableArray alloc]init];
    Repository* repo = [Repository mainRepository];
    SkillDB* skillDB = repo.skillDB;
    
    for (int i = 0; i < _listKind.count; ++i) {
        SkillKind* kind = [_listKind objectAtIndex: i];
        int point = self.point[i];
        BOOL positive = self.positive[i];
        
        SkillPoint *p = [[SkillPoint alloc]init];
        p.skillKind = kind;
        p.skillPoint = point;
        p.positiveRange = positive;
    
        SkillPoint *name = [skillDB getNearSkillPoint: p];
        
        if (name != nil) {
            [result addObject: name.pointName];
        }
        [p release];
    }
    return result;
}

-(NSMutableArray*) getNamedSkillNameForSearch {
    //[self checkMemories];

    NSMutableArray* result = [[NSMutableArray alloc]init];
    Repository* repo = [Repository mainRepository];
    SkillDB* skillDB = repo.skillDB;
    
    for (int i = 0; i < _listKind.count; ++i) {
        SkillPoint* point = [self getAsSkillPoint:i];
        SkillPoint* name = [skillDB getMatchSkillPoint:point];
        if (name != nil) {
            [result addObject: name.pointName];
        } else if ((point.skillPoint % 5) != 0 && point.skillPoint < 0) {
            int x = point.skillPoint;
            while (x % 5 != 0) {
                x --;
            }
            SkillPoint* temp = [[SkillPoint alloc]init];
            temp.skillKind = point.skillKind;
            temp.skillPoint = x;
            temp.positiveRange = FALSE;
            name = [skillDB getMatchSkillPoint: temp];
            [temp release];
            if (name != nil) {
                NSString* jogai = [[NSString alloc]initWithFormat:@"%@を除外", name.pointName];
                [result addObject: jogai];
            }
        }
        [point release];
    }
    return result;
}

-(void) getNamedSkillSetForDisplay:(SkillSet*)dest {
    //[self checkMemories];

    Repository* repo = [Repository mainRepository];
    SkillDB* skillDB = repo.skillDB;
    [dest cleanup];

    for (int i = 0; i < self.count; ++ i) {
        SkillPoint* point = [self getAsSkillPoint:i];        
        SkillPoint* name = [skillDB getNearSkillPoint:point];
        if (name != nil) {
            [dest set: name.skillKind: name.skillPoint: name.positiveRange];
        }
        [point release];
    }
}

-(int)totalDiffPoint {
    //[self checkMemories];

    int total = 0;
    int point;
    int count = _listKind.count;
    for (int i = 0; i < count; ++i) {
        if (_positive[i]) {
            point = _point[i];
        } else {
            point = - _point[i];
        }
        if (point > 0) {
            total += point;
        }
    }
    return total;
}

-(int) maxDiffPoint {
    //[self checkMemories];

    int max = 0;
    int point;
    int count = _listKind.count;
    for (int i = 0; i < count; ++i) {
        if (_positive[i]) {
            point = _point[i];
        } else {
            point = - _point[i];
        }
        if (max < point) {
            max = point;
        }
    }
    return max;
}

-(BOOL) isZero {
    //[self checkMemories];

    int count = _listKind.count;
    for (int i = 0; i < count; ++i) {
        if (_point[i] != 0) {
            return FALSE;
        }
    }
    return TRUE;
}

-(SkillPoint *)getAsSkillPoint:(int)index {
    //[self checkMemories];

    SkillKind* kind = [_listKind objectAtIndex: index];
    SkillPoint* point = [[SkillPoint alloc]init];
    point.skillKind = kind;
    point.skillPoint = _point[index];
    point.positiveRange = _positive[index];
    return point;
}

-(int)indexOfKind:(SkillKind*)kind {
    for (int i = 0; i < _listKind.count; ++ i) {
        SkillKind* kind0 = [_listKind objectAtIndex:i];
        if (kind0 == kind) {
            return i;
        }
    }
    return -1;
}

-(void)removeAtIndex: (int)index {
    //[self checkMemories];

    int count = _listKind.count;
    if (index <= count - 1) {
        [_listKind removeObjectAtIndex:index];
        for (int i = index; i < count - 1; ++ i) {
            _point[i] = _point[i + 1];
            _positive[i] = _positive[i + 1];
        }
    }
}

-(SkillSet*) canonical {
    //[self checkMemories];

    SkillSet* newset = [[SkillSet alloc]init];
    int count = _listKind.count;
    for (int i = 0; i < count; ++ i) {
        SkillKind* kind = [_listKind objectAtIndex:i];
        int point = _point[i];
        BOOL range = _positive[i];
        
        if (point != 0) {
            [newset set:kind :point :range];
        }
    }
    return newset;
}

-(void)sortByOrdinary {
    NSMutableArray* ar = [[NSMutableArray alloc]init];
    
    for (int i = 0; i < self.count; ++ i) {
        SkillKind* kind = [self.listKind objectAtIndex:i];
        BOOL positive = self.positive[i]; 
        int point = self.point[i];
        
        SkillPoint* sp = [[SkillPoint alloc]init];
        sp.skillKind = kind;
        sp.skillPoint = point;
        sp.positiveRange = positive;
        
        [ar addObject:sp];
        [sp release];
    }
    
    [SkillPoint sortFull: ar];
    
    [_listKind removeAllObjects];
    for (int i = 0; i < ar.count; ++ i) {
        SkillPoint* sp = [ar objectAtIndex: i];
        [_listKind addObject: sp.skillKind];
        _point[i] = sp.skillPoint;
        _positive[i] = sp.positiveRange;
    }
    
    [ar release];
}

-(void)sortByPower {
    NSMutableArray* ar = [[NSMutableArray alloc]init];
    
    for (int i = 0; i < self.count; ++ i) {
        SkillKind* kind = [self.listKind objectAtIndex:i];
        BOOL positive = self.positive[i]; 
        int point = self.point[i];
        
        SkillPoint* sp = [[SkillPoint alloc]init];
        sp.skillKind = kind;
        sp.skillPoint = point;
        sp.positiveRange = positive;
        
        [ar addObject:sp];
        [sp release];
    }
    
    [SkillPoint sortPoint: ar];

    [_listKind removeAllObjects];
    for (int i = 0; i < ar.count; ++ i) {
        SkillPoint* sp = [ar objectAtIndex: i];
        [_listKind addObject: sp.skillKind];
        _point[i] = sp.skillPoint;
        _positive[i] = sp.positiveRange;
    }
    
    [ar release];
}

-(NSString *)description {
    NSMutableString* str = [[NSMutableString alloc]init];
    [self skillToString:str :nil :FALSE];
    [str autorelease];
    return str;
}

-(SkillSet*)skillSetOfChange:(SkillSet*)originalSkills {
    SkillSet* skills = [[SkillSet alloc]init];
    SkillSet* armorSkills = self;
    
    for (int i = 0; i < armorSkills.count; ++ i) {
        SkillKind* kind = [armorSkills.listKind objectAtIndex:i];
        int point = armorSkills.point[i];
        int point2 = 0;
        BOOL positive = armorSkills.positive[i];
        BOOL positive2 = FALSE;
        
        int z = [originalSkills findByKind:kind];
        BOOL needCopy = FALSE;
        if (z >= 0) {
            point2 = originalSkills.point[z];
            positive2 = originalSkills.positive[z];
            
            if (point2 == 0) {
                needCopy = TRUE;
            }
            else if (positive != positive2) {
                needCopy = TRUE;
            }else if (positive) {
                if (point > point2) {
                    needCopy = TRUE;
                }
            }else {
                if (point < point2) {
                    needCopy = TRUE;
                }
            }
        }else {
            needCopy = TRUE;
        }
        if (needCopy) {
            [skills set:kind :point :positive];
        }
    }
    
    return skills;
}

@end
