#import "Hotkey.h"
#import "HotkeyApp.h"
#import "CycleCommand.h"
#import "HotkeyPanel.h"


BOOL keyCombinationEqual(keyCombination *a,keyCombination *b){
    return
        ((a->keyCode==b->keyCode)&&
         (a->shift==b->shift)&&
         (a->command==b->command)&&
         (a->option==b->option)&&
         (a->control==b->control));
}


@implementation Hotkey

-(id)initWithNum:(int)n{
    [super init];

    _hotkeyNum=n;
    _hotkey=(EventHotKeyRef *)(calloc(_hotkeyNum,sizeof(EventHotKeyRef)));
    _fld=(id *)(calloc(_hotkeyNum,sizeof(id)));
    _hotkeyMethod=(SEL *)(calloc(_hotkeyNum,sizeof(SEL)));
    _keyCombination=(keyCombination *)(calloc(_hotkeyNum,sizeof(keyCombination)));
    _cycle=[[NSMutableArray alloc] initWithCapacity:0];
        
    return self;
}

-(IBAction)setBtn:(id)sender{
    if(_keyCombination[[sender tag]].sat){
        [_shiftCheck setState:(_keyCombination[[sender tag]].shift?NSOnState:NSOffState)];
        [_ctrlCheck setState:(_keyCombination[[sender tag]].control?NSOnState:NSOffState)];
        [_cmdCheck setState:(_keyCombination[[sender tag]].command?NSOnState:NSOffState)];
        [_optCheck setState:(_keyCombination[[sender tag]].option?NSOnState:NSOffState)];
    }else{
        [_shiftCheck setState:NSOffState];
        [_ctrlCheck setState:NSOffState];
        [_cmdCheck setState:NSOffState];
        [_optCheck setState:NSOffState];
    }
    [[HotkeyApp sharedApplication] beginSheet:_hotkeyPanel
        modalForWindow:_settingPanel
        modalDelegate:self
        didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:)
        contextInfo:[[NSNumber numberWithInt:[sender tag]] retain]];
}

-(IBAction)delBtn:(id)sender{
    [[HotkeyApp sharedApplication] endSheet:_hotkeyPanel returnCode:2];
    [_hotkeyPanel close];
}

-(IBAction)cancelBtn:(id)sender{
    [[HotkeyApp sharedApplication] endSheet:_hotkeyPanel returnCode:0];
    [_hotkeyPanel close];
}

-(void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo{
    int ht=[(NSNumber *)contextInfo intValue];
    keyCombination kc;
    [(NSNumber *)contextInfo release];
    
    if(!returnCode) return;
    if(returnCode==2){
        [self unregHotKey:ht];
        return;
    }
    kc.keyCode=[_hotkeyPanel keyCode];
    kc.keyStr=[[_hotkeyPanel keyStr] copy];
    kc.shift=[_shiftCheck state]==NSOnState;
    kc.control=[_ctrlCheck state]==NSOnState;
    kc.command=[_cmdCheck state]==NSOnState;
    kc.option=[_optCheck state]==NSOnState;
    kc.sat=YES;
    
    //NSLog(@"kc.keyStr = %@ , [hotkeyPanel keyStr] = %@",kc.keyStr,[hotkeyPanel keyStr]);
    [self regHotkey:ht keyCombination:kc];
}

-(void)regHotkey:(int)ht keyCombination:(keyCombination)kc{
    unsigned int keyModifier=0;

    //NSLog(@"kc.keyStr = %@",kc.keyStr);
    // Make key code
    if(!kc.sat) return;
    
    if(_hotkey[ht])
        [self unregHotKey:ht];
    
    // Make key modifier
    if (kc.shift) keyModifier|=NSShiftKeyMask;
    if (kc.control) keyModifier|=NSControlKeyMask;
    if (kc.command) keyModifier|=NSCommandKeyMask;
    if (kc.option) keyModifier|=NSAlternateKeyMask;
    
    // Register hot key
    if(keyModifier){
        
        NSArray *ary=[self sameKeyCombinationArray:&kc];
        if([ary count]){
            if([ary count]==1){
                id tmp=[CycleCommand cycleWithOutlet:self];
                int x=[[ary objectAtIndex:0] intValue];
                [tmp addWithNum:x selector:_hotkeyMethod[x]];
                [tmp addWithNum:ht selector:_hotkeyMethod[ht]];
                [_cycle addObject:tmp];
                if(_hotkey[x]) [(HotkeyApp *)[HotkeyApp sharedApplication] unregisterHotKey:_hotkey[x]];
                _hotkey[x]=[(HotkeyApp *)[HotkeyApp sharedApplication] 
                        registerHotKeyCode:kc.keyCode 
                        withModifier:keyModifier 
                        target:tmp 
                        selector:@selector(cycleSelector)];
            }else{
                NSEnumerator *enu=[_cycle objectEnumerator];
                id obj;
                int x=[[ary objectAtIndex:0] intValue];
                while(obj=[enu nextObject]){
                    if([obj contain:x]){
                        [obj addWithNum:ht selector:_hotkeyMethod[ht]];
                        _hotkey[ht]=[(HotkeyApp *)[HotkeyApp sharedApplication] 
                            registerHotKeyCode:kc.keyCode 
                            withModifier:keyModifier 
                            target:obj 
                            selector:@selector(cycleSelector)];
                        break;
                    }
                }
            }
        }else{
            _hotkey[ht]=[(HotkeyApp *)[HotkeyApp sharedApplication] 
                        registerHotKeyCode:kc.keyCode 
                        withModifier:keyModifier 
                        target:self 
                        selector:_hotkeyMethod[ht]];
        }
        [_fld[ht] setStringValue:
            [NSString stringWithFormat:@"%@%@%@%@%@",
                kc.command?@"cmd+":@"",
                kc.option?@"opt+":@"",
                kc.shift?@"shft+":@"",
                kc.control?@"ctl+":@"",
                kc.keyStr]];
        _keyCombination[ht]=kc;
    }
}

-(void)unregHotKey:(int)ht{
    NSArray *ary=[self sameKeyCombinationArray:&_keyCombination[ht]];
    
    if([ary count]==1){
        int x=[[ary objectAtIndex:0] intValue];
        unsigned int keyModifier=0;
        NSEnumerator *enu=[_cycle objectEnumerator];
        id obj;
        
        while(obj=[enu nextObject]) if([obj contain:x]) break;
        
        if (_keyCombination[ht].shift) keyModifier|=NSShiftKeyMask;
        if (_keyCombination[ht].control) keyModifier|=NSControlKeyMask;
        if (_keyCombination[ht].command) keyModifier|=NSCommandKeyMask;
        if (_keyCombination[ht].option) keyModifier|=NSAlternateKeyMask;
        if(_hotkey[x]) [(HotkeyApp *)[HotkeyApp sharedApplication] unregisterHotKey:_hotkey[x]];
        _hotkey[x]=[(HotkeyApp *)[HotkeyApp sharedApplication] 
                    registerHotKeyCode:_keyCombination[x].keyCode 
                    withModifier:keyModifier 
                    target:self 
                    selector:_hotkeyMethod[x]];
        [_cycle removeObject:obj];
    }else if([ary count]>0){
        int x=[[ary objectAtIndex:0] intValue];
        NSEnumerator *enu=[_cycle objectEnumerator];
        id obj;
        
        while(obj=[enu nextObject]) if([obj contain:x]) break;
        if(_hotkey[ht]){
            _hotkey[x]=_hotkey[ht];
            _hotkey[ht]=NULL;
        }
        [obj removeWithNum:ht selector:_hotkeyMethod[ht]];
    }
    
    if(_hotkey[ht]) [(HotkeyApp *)[HotkeyApp sharedApplication] unregisterHotKey:_hotkey[ht]];
    _keyCombination[ht].sat=NO;
    _keyCombination[ht].keyCode=0;
    [_keyCombination[ht].keyStr release];
    [_fld[ht] setStringValue:@""];
}

-(NSDictionary *)hotkeyDictionary{
    NSMutableDictionary *res=[[NSMutableDictionary alloc] initWithCapacity:_hotkeyNum];
    int i;
    
    for(i=0;i<_hotkeyNum;i++){
        keyCombination kc=_keyCombination[i];
        if(kc.sat){
            NSDictionary *tmp=[NSDictionary dictionaryWithObjectsAndKeys:
                [NSNumber numberWithUnsignedShort:kc.keyCode],@"KeyCode",
                kc.keyStr,@"KeyStr",
                [NSNumber numberWithBool:kc.shift],@"Shift",
                [NSNumber numberWithBool:kc.command],@"Command",
                [NSNumber numberWithBool:kc.option],@"Option",
                [NSNumber numberWithBool:kc.control],@"Control",
                nil];
            [res setObject:tmp forKey:[NSString stringWithFormat:@"%d",i]];
        }
    }
    return [res autorelease];
}

-(void)regWithDictionary:(NSDictionary *)dic{
    int i;
    
    for(i=0;i<_hotkeyNum;i++){
        id tmp=nil;
        if(tmp=[dic objectForKey:[NSString stringWithFormat:@"%d",i]]){
            keyCombination kc;
    
            kc.keyCode=[[tmp objectForKey:@"KeyCode"] unsignedShortValue];
            kc.keyStr=[[tmp objectForKey:@"KeyStr"] copy];
            kc.shift=[[tmp objectForKey:@"Shift"] boolValue];
            kc.command=[[tmp objectForKey:@"Command"] boolValue];
            kc.option=[[tmp objectForKey:@"Option"] boolValue];
            kc.control=[[tmp objectForKey:@"Control"] boolValue];
            kc.sat=YES;
            [self regHotkey:i keyCombination:kc];
        }
    }
}

-(NSArray *)sameKeyCombinationArray:(keyCombination *)kc{
    int i;
    NSMutableArray *res=[[NSMutableArray alloc] initWithCapacity:0];
    
    for(i=0;i<_hotkeyNum;i++)
        if(keyCombinationEqual(&_keyCombination[i],kc)) [res addObject:[NSNumber numberWithInt:i]];
    return [res autorelease];
}

-(void)dealloc{
    free(_hotkey);
    free(_fld);
    free(_keyCombination);
    free(_hotkeyMethod);
    [_cycle release];
    [super dealloc];
}

@end
