//
//  SFSRoomManager.m
//  SFS2X
//
//  Original development by Infosfer Game Technologies Ltd. | http://www.infosfer.com.
//
//  Maintained and developed by A51 Integrated.
//  Copyright 2012 A51 Integrated | http://a51integrated.com. All rights reserved.
//

#import "SFSRoomManager.h"
#import "Room.h"
#import "ArrayUtil.h"
#import "SmartFox2XClient.h"

@implementation SFSRoomManager
@synthesize ownerZone = _ownerZone;


-(id)initWithSfs:(SmartFox2XClient *)sfs {

	if ((self = [super init]))
    {
		_groups = [NSMutableArray array];
		_roomsById = [NSMutableDictionary dictionary];
		_roomsByName = [NSMutableDictionary dictionary];
        _roomLock = [[NSObject alloc] init];
        _groupLock = [[NSObject alloc] init];
        _smartFox = sfs;
	}

	return self;
}


-(void)_removeRoom:(NSInteger)id_ name:(NSString *)name
{
    @synchronized (_roomLock)
    {
        [_roomsById removeObjectForKey:[NSNumber numberWithInt:(int)id_]];
        [_roomsByName removeObjectForKey:name];
    }
}

#pragma mark IRoomManager protocol


-(void)addRoom:(id <Room>)room addGroupIfMissing:(BOOL)addGroupIfMissing
{
    @synchronized (_roomLock)
    {
        [_roomsById setObject:room forKey:[NSNumber numberWithInt:(int)[room id]]];
        [_roomsByName setObject:room forKey:[room name]];
        
        if (addGroupIfMissing)
            [self addGroup:[room groupId]];
        
        else
            [room setIsManaged:NO];
    }
}

-(void)addGroup:(NSString *)groupId
{
    @synchronized (_groupLock)
    {
        [_groups addObject:groupId];
    }
}

-(id<Room>)replaceRoom:(id <Room>)room addToGroupIfMissing:(BOOL)addToGroupIfMissing
{
    id <Room> oldRoom = [self getRoomById:[room id]];
    
    @synchronized (_roomLock)
    {
        if (oldRoom != nil)
        {
            [oldRoom merge:room];
            return oldRoom;
        }
        
        else
        {
            [self addRoom:room addGroupIfMissing:addToGroupIfMissing];
            return room;
        }
    }
}

-(void)removeGroup:(NSString *)groupId
{
    @synchronized (_groupLock)
    {
        [ArrayUtil removeElement:_groups item:groupId];
    }
    
	NSArray *roomsInGroup = [self getRoomListFromGroup:groupId];
	
	for (id <Room> room in roomsInGroup)
    {
		if ([room conformsToProtocol:@protocol(Room)])
        {
			if (![room isJoined])
				[self removeRoom:room];
			
			else
				[room setIsManaged:NO];
		}
	}
}

-(BOOL)containsGroup:(NSString *)groupId
{
    @synchronized (_groupLock)
    {
        return ([_groups indexOfObject:groupId] != NSNotFound);
    }
}

-(BOOL)containsRoom:(id)idOrName
{
    @synchronized (_roomLock)
    {
        if ([idOrName isKindOfClass:[NSNumber class]])
            return [_roomsById objectForKey:idOrName] != nil;
        
        else
            return [_roomsByName objectForKey:idOrName] != nil;
    }
}

-(BOOL)containsRoomInGroup:(id)idOrName groupId:(NSString *)groupId
{
	NSArray *roomList = [self getRoomListFromGroup:groupId];
	BOOL found = NO;
	BOOL searchById = [idOrName isKindOfClass:[NSNumber class]];
	
	for (id <Room> room in roomList)
    {
		if ([room conformsToProtocol:@protocol(Room)])
        {
			
			if (searchById)
            {
				if ([room id] == [(NSNumber *)idOrName intValue])
                {
					found = YES;
					break;
				}
			}
            
			else
            {
				if ([[room name] isEqualToString:(NSString *)idOrName])
                {
					found = YES;
					break;
				}
			}
		}
	}
	
	return found;
}

-(void)changeRoomName:(id <Room>)room newName:(NSString *)newName
{
    @synchronized (_roomLock)
    {
        NSString *oldName = [room name];
        
        if ([oldName isEqualToString:newName])
            return;
        
        [room setName:newName];
        
        [_roomsByName setObject:room forKey:newName];
        [_roomsByName removeObjectForKey:oldName];
    }
}


-(void)changeRoomPasswordState:(id <Room>)room isPassProtected:(BOOL)isPassProtected {

	[room setPasswordProtected:isPassProtected];
}

-(void)changeRoomCapacity:(id <Room>)room maxUsers:(NSInteger)maxUsers maxSpect:(NSInteger)maxSpect {
	[room setMaxUsers:maxUsers];
	[room setMaxSpectators:maxSpect];
}

-(id <Room>)getRoomById:(NSInteger)id_
{
    @synchronized (_roomLock)
    {
        return [_roomsById objectForKey:[NSNumber numberWithInt:(int)id_]];
    }
}

-(id <Room>)getRoomByName:(NSString *)name
{
    @synchronized (_roomLock)
    {
        return [_roomsByName objectForKey:name];
    }
}

-(NSArray *)getRoomList
{
    @synchronized (_roomLock)
    {
        return [_roomsById allValues];
    }
}

-(NSInteger)getRoomCount
{
    @synchronized (_roomLock)
    {
        return [_roomsById count];
    }
}

-(NSArray *)getRoomGroups
{
    @synchronized (_groupLock)
    {
        return [_groups copy];
    }
}

-(NSArray *)getRoomListFromGroup:(NSString *)groupId
{
	NSMutableArray *roomList = [NSMutableArray array];
    
    @synchronized (_groupLock)
    {
        
        for (id <Room> room in [_roomsById allValues])
        {
            if ([room conformsToProtocol:@protocol(Room)])
            {
                if ([[room groupId] isEqualToString:groupId])
                    [roomList addObject:room];
            }
        }
    }
	
	return roomList;
}

-(NSMutableArray *)getJoinedRooms
{
	NSMutableArray *rooms = [NSMutableArray array];
	
    @synchronized (_roomLock)
    {
        for (id <Room> room in [_roomsById allValues])
        {
            if ([room conformsToProtocol:@protocol(Room)])
            {
                if ([room isJoined])
                    [rooms addObject:room];
            }
        }
    }
    
	return rooms;
}


-(NSArray *)getUserRooms:(id <User>)user
{
	NSMutableArray *rooms = [NSMutableArray array];

    @synchronized (_roomLock)
    {
        for (id <Room> room in [_roomsById allValues])
        {
            if ([room conformsToProtocol:@protocol(Room)])
            {
                if ([room containsUser:user])
                    [rooms addObject:room];
            }
        }
    }
    
	return rooms;
}

-(void)removeRoom:(id <Room>)room
{
	[self _removeRoom:[room id] name:[room name]];
}

-(void)removeRoomById:(NSInteger)id_
{
    id <Room> room = [self getRoomById:id_];
	
	if (room != nil)
		[self _removeRoom:id_ name:[room name]];
}

-(void)removeRoomByName:(NSString *)name
{
    id <Room> room = [self getRoomByName:name];
	
	if (room != nil)
		[self _removeRoom:[room id] name:name];
}

-(void)removeUser:(id <User>)user
{
    @synchronized (_roomLock)
    {
        /*
         Cycle through all non-MMO Rooms
         Updated Jan 28th 2020
         
         We should skip MMORooms because they manage themeselves differently, via the ProxyUpdate
         This means that every MMORoom (technically you can only be joined in 1 MMORoom at a time) will "clean itself" regardless.
         
         If we don't skip MMORooms, the system will remove the User from everywhere. This in turn causes problems for use cases where clients are joined
         in both MMO and non-MMO Rooms at the same time.
         Example --> https://www.smartfoxserver.com/forums/viewtopic.php?f=18&t=22126
         */
        
        for (id <Room> room in [_roomsById allValues])
        {
            if ([room conformsToProtocol:@protocol(Room)])
            {
                // Skip MMO Rooms
                if ([room isKindOfClass:[MMORoom class]])
                    continue;
                
                if ([room containsUser:user])
                    [room removeUser:user];
            }
        }
    }
}

-(SmartFox2XClient *)smartFox {
	return _smartFox;
}

@end
