<commit_before>import UIKit

class MPhotos
{
    typealias AlbumId = String
    typealias PhotoId = String
    
    enum Status:Int
    {
        case waiting
        case synced
    }
    
    static let sharedInstance:MPhotos = MPhotos()
    static let kThumbnailSize:CGFloat = 100
    let defaultAlbum:MPhotosItemDefault
    private(set) var albumItems:[AlbumId:MPhotosItemUser]
    private(set) var photoDeletables:[MPhotosItemPhoto]
    private(set) var albumReferences:[MPhotosItemReference]
    private(set) var photos:[PhotoId:MPhotosItemPhoto]
    private var loading:Bool
    
    private init()
    {
        loading = false
        defaultAlbum = MPhotosItemDefault()
        albumItems = [:]
        photoDeletables = []
        albumReferences = []
        photos = [:]
        
        loadPhotos()
    }
    
    //MARK: private
    
    private func asyncLoadAlbums()
    {
        guard
            
            let userId:MSession.UserId = MSession.sharedInstance.user.userId
            
        else
        {
            return
        }
        
        let parentUser:String = FDatabase.Parent.user.rawValue
        let propertyAlbums:String = FDatabaseModelUser.Property.albums.rawValue
        let path:String = "\(parentUser)/\(userId)/\(propertyAlbums)"
        defaultAlbum.clearReferences()
        
        FMain.sharedInstance.database.listenOnce(
            path:path,
            modelType:FDatabaseModelAlbumList.self)
        { (albumList) in
            
            DispatchQueue.global(qos:DispatchQoS.QoSClass.background).async
            {
                guard
                    
                    let albumsMap:[AlbumId:FDatabaseModelAlbum] = albumList?.items
                    
                else
                {
                    self.asyncLoadPhotos()
                    
                    return
                }
                
                self.compareAlbums(albumsMap:albumsMap)
            }
        }
    }
    
    private func compareAlbums(albumsMap:[AlbumId:FDatabaseModelAlbum])
    {
        var items:[AlbumId:MPhotosItemUser] = [:]
        var references:[MPhotosItemReference] = []
        let albumsIds:[AlbumId] = Array(albumsMap.keys)
        
        for albumId:AlbumId in albumsIds
        {
            guard
                
                let firebaseAlbum:FDatabaseModelAlbum = albumsMap[albumId]
                
            else
            {
                continue
            }
            
            let albumName:String = firebaseAlbum.name
            let newItem:MPhotosItemUser = MPhotosItemUser(
                albumId:albumId,
                firebaseAlbum:firebaseAlbum)
            let albumReference:MPhotosItemReference = MPhotosItemReference(
                albumId:albumId,
                name:albumName)
            
            items[albumId] = newItem
            references.append(albumReference)
        }
        
        references.sort
        { (referenceA, referenceB) -> Bool in
            
            let before:Bool
            let nameA:String = referenceA.name
            let nameB:String = referenceB.name
            let comparison:ComparisonResult = nameA.compare(nameB)
            
            switch comparison
            {
                case ComparisonResult.orderedDescending:
                
                    before = false
                    
                    break
                
                default:
                
                    before = true
                    
                    break
            }
            
            return before
        }
        
        self.albumItems = items
        self.albumReferences = references
        
        asyncLoadPhotos()
    }
    
    private func asyncLoadPhotos()
    {
        guard
            
            let userId:MSession.UserId = MSession.sharedInstance.user.userId
            
        else
        {
            return
        }
        
        let parentUser:String = FDatabase.Parent.user.rawValue
        let propertyPhotos:String = FDatabaseModelUser.Property.photos.rawValue
        let path:String = "\(parentUser)/\(userId)/\(propertyPhotos)"
        
        FMain.sharedInstance.database.listenOnce(
            path:path,
            modelType:FDatabaseModelPhotoList.self)
        { (photoList) in
            
            guard
                
                let photosMap:[PhotoId:FDatabaseModelPhoto] = photoList?.items
                
            else
            {
                self.photoDeletables = []
                self.albumReferences = []
                self.photosLoaded()
                
                return
            }
            
            self.comparePhotos(photosMap:photosMap)
        }
    }
    
    private func comparePhotos(photosMap:[PhotoId:FDatabaseModelPhoto])
    {
        var items:[PhotoId:MPhotosItemPhoto] = [:]
        var deletables:[MPhotosItemPhoto] = []
        let photosIds:[PhotoId] = Array(photosMap.keys)
        
        for photoId:PhotoId in photosIds
        {
            guard
                
                let firebasePhoto:FDatabaseModelPhoto = photosMap[photoId]
                
            else
            {
                continue
            }
            
            let photoStatus:MPhotos.Status = firebasePhoto.status
            let photoCreated:TimeInterval = firebasePhoto.created
            let kiloBytes:Int = firebasePhoto.size
            let albumId:AlbumId = firebasePhoto.albumId
            let album:MPhotosItem?
            
            if albumId.isEmpty
            {
                album = defaultAlbum
            }
            else
            {
                album = albumItems[albumId]
            }
            
            if photoStatus == Status.synced
            {
                if let loadedItem:MPhotosItemPhoto = self.photos[photoId]
                {
                    items[photoId] = loadedItem
                }
                else
                {
                    let newItem:MPhotosItemPhoto = MPhotosItemPhoto(
                        photoId:photoId,
                        firebasePicture:firebasePhoto)
                    items[photoId] = newItem
                }
                
                let photoReference:MPhotosItemPhotoReference = MPhotosItemPhotoReference(
                    photoId:photoId,
                    created:photoCreated)
                
                album?.addReference(
                    reference:photoReference,
                    kiloBytes:kiloBytes)
            }
            else
            {
                let deleteItem:MPhotosItemPhoto = MPhotosItemPhoto(
                    photoId:photoId,
                    firebasePicture:firebasePhoto)
                deletables.append(deleteItem)
            }
        }
        
        self.photos = items
        self.photoDeletables = deletables
        
        sortAlbums()
    }
    
    private func sortAlbums()
    {
        for albumReference:MPhotosItemReference in albumReferences
        {
            guard
            
                let album:MPhotosItem = albumItems[albumReference.albumId]
            
            else
            {
                continue
            }
            
            album.sortPhotos()
        }
        
        photosLoaded()
    }
    
    private func photosLoaded()
    {
        loading = false
        NotificationCenter.default.post(
            name:Notification.photosLoaded,
            object:nil)
    }
    
    private func asyncCleanResources()
    {
        let allPhotos:[PhotoId] = Array(photos.keys)
        
        for photoId:PhotoId in allPhotos
        {
            let photo:MPhotosItemPhoto? = photos[photoId]
            photo?.resources.cleanResources()
        }
    }
    
    //MARK: public
    
    func loadPhotos()
    {
        DispatchQueue.global(qos:DispatchQoS.QoSClass.background).async
        {
            if !self.loading
            {
                self.loading = true
                self.asyncLoadAlbums()
            }
        }
    }
    
    func cleanResources()
    {
        DispatchQueue.global(qos:DispatchQoS.QoSClass.background).async
        {
            self.asyncCleanResources()
        }
    }
}
<commit_msg>async fetching photos and compare<commit_after>import UIKit

class MPhotos
{
    typealias AlbumId = String
    typealias PhotoId = String
    
    enum Status:Int
    {
        case waiting
        case synced
    }
    
    static let sharedInstance:MPhotos = MPhotos()
    static let kThumbnailSize:CGFloat = 100
    let defaultAlbum:MPhotosItemDefault
    private(set) var albumItems:[AlbumId:MPhotosItemUser]
    private(set) var photoDeletables:[MPhotosItemPhoto]
    private(set) var albumReferences:[MPhotosItemReference]
    private(set) var photos:[PhotoId:MPhotosItemPhoto]
    private var loading:Bool
    
    private init()
    {
        loading = false
        defaultAlbum = MPhotosItemDefault()
        albumItems = [:]
        photoDeletables = []
        albumReferences = []
        photos = [:]
        
        loadPhotos()
    }
    
    //MARK: private
    
    private func asyncLoadAlbums()
    {
        guard
            
            let userId:MSession.UserId = MSession.sharedInstance.user.userId
            
        else
        {
            return
        }
        
        let parentUser:String = FDatabase.Parent.user.rawValue
        let propertyAlbums:String = FDatabaseModelUser.Property.albums.rawValue
        let path:String = "\(parentUser)/\(userId)/\(propertyAlbums)"
        defaultAlbum.clearReferences()
        
        FMain.sharedInstance.database.listenOnce(
            path:path,
            modelType:FDatabaseModelAlbumList.self)
        { (albumList) in
            
            DispatchQueue.global(qos:DispatchQoS.QoSClass.background).async
            {
                guard
                    
                    let albumsMap:[AlbumId:FDatabaseModelAlbum] = albumList?.items
                    
                else
                {
                    self.asyncLoadPhotos()
                    
                    return
                }
                
                self.compareAlbums(albumsMap:albumsMap)
            }
        }
    }
    
    private func compareAlbums(albumsMap:[AlbumId:FDatabaseModelAlbum])
    {
        var items:[AlbumId:MPhotosItemUser] = [:]
        var references:[MPhotosItemReference] = []
        let albumsIds:[AlbumId] = Array(albumsMap.keys)
        
        for albumId:AlbumId in albumsIds
        {
            guard
                
                let firebaseAlbum:FDatabaseModelAlbum = albumsMap[albumId]
                
            else
            {
                continue
            }
            
            let albumName:String = firebaseAlbum.name
            let newItem:MPhotosItemUser = MPhotosItemUser(
                albumId:albumId,
                firebaseAlbum:firebaseAlbum)
            let albumReference:MPhotosItemReference = MPhotosItemReference(
                albumId:albumId,
                name:albumName)
            
            items[albumId] = newItem
            references.append(albumReference)
        }
        
        references.sort
        { (referenceA, referenceB) -> Bool in
            
            let before:Bool
            let nameA:String = referenceA.name
            let nameB:String = referenceB.name
            let comparison:ComparisonResult = nameA.compare(nameB)
            
            switch comparison
            {
                case ComparisonResult.orderedDescending:
                
                    before = false
                    
                    break
                
                default:
                
                    before = true
                    
                    break
            }
            
            return before
        }
        
        self.albumItems = items
        self.albumReferences = references
        
        asyncLoadPhotos()
    }
    
    private func asyncLoadPhotos()
    {
        guard
            
            let userId:MSession.UserId = MSession.sharedInstance.user.userId
            
        else
        {
            return
        }
        
        let parentUser:String = FDatabase.Parent.user.rawValue
        let propertyPhotos:String = FDatabaseModelUser.Property.photos.rawValue
        let path:String = "\(parentUser)/\(userId)/\(propertyPhotos)"
        
        FMain.sharedInstance.database.listenOnce(
            path:path,
            modelType:FDatabaseModelPhotoList.self)
        { (photoList) in
            
            DispatchQueue.global(qos:DispatchQoS.QoSClass.background).async
            {
                guard
                    
                    let photosMap:[PhotoId:FDatabaseModelPhoto] = photoList?.items
                    
                else
                {
                    self.photoDeletables = []
                    self.albumReferences = []
                    self.photosLoaded()
                    
                    return
                }
                
                self.comparePhotos(photosMap:photosMap)
            }
        }
    }
    
    private func comparePhotos(photosMap:[PhotoId:FDatabaseModelPhoto])
    {
        var items:[PhotoId:MPhotosItemPhoto] = [:]
        var deletables:[MPhotosItemPhoto] = []
        let photosIds:[PhotoId] = Array(photosMap.keys)
        
        for photoId:PhotoId in photosIds
        {
            guard
                
                let firebasePhoto:FDatabaseModelPhoto = photosMap[photoId]
                
            else
            {
                continue
            }
            
            let photoStatus:MPhotos.Status = firebasePhoto.status
            let photoCreated:TimeInterval = firebasePhoto.created
            let kiloBytes:Int = firebasePhoto.size
            let albumId:AlbumId = firebasePhoto.albumId
            let album:MPhotosItem?
            
            if albumId.isEmpty
            {
                album = defaultAlbum
            }
            else
            {
                album = albumItems[albumId]
            }
            
            if photoStatus == Status.synced
            {
                if let loadedItem:MPhotosItemPhoto = self.photos[photoId]
                {
                    items[photoId] = loadedItem
                }
                else
                {
                    let newItem:MPhotosItemPhoto = MPhotosItemPhoto(
                        photoId:photoId,
                        firebasePicture:firebasePhoto)
                    items[photoId] = newItem
                }
                
                let photoReference:MPhotosItemPhotoReference = MPhotosItemPhotoReference(
                    photoId:photoId,
                    created:photoCreated)
                
                album?.addReference(
                    reference:photoReference,
                    kiloBytes:kiloBytes)
            }
            else
            {
                let deleteItem:MPhotosItemPhoto = MPhotosItemPhoto(
                    photoId:photoId,
                    firebasePicture:firebasePhoto)
                deletables.append(deleteItem)
            }
        }
        
        self.photos = items
        self.photoDeletables = deletables
        
        sortAlbums()
    }
    
    private func sortAlbums()
    {
        for albumReference:MPhotosItemReference in albumReferences
        {
            guard
            
                let album:MPhotosItem = albumItems[albumReference.albumId]
            
            else
            {
                continue
            }
            
            album.sortPhotos()
        }
        
        photosLoaded()
    }
    
    private func photosLoaded()
    {
        loading = false
        NotificationCenter.default.post(
            name:Notification.photosLoaded,
            object:nil)
    }
    
    private func asyncCleanResources()
    {
        let allPhotos:[PhotoId] = Array(photos.keys)
        
        for photoId:PhotoId in allPhotos
        {
            let photo:MPhotosItemPhoto? = photos[photoId]
            photo?.resources.cleanResources()
        }
    }
    
    //MARK: public
    
    func loadPhotos()
    {
        DispatchQueue.global(qos:DispatchQoS.QoSClass.background).async
        {
            if !self.loading
            {
                self.loading = true
                self.asyncLoadAlbums()
            }
        }
    }
    
    func cleanResources()
    {
        DispatchQueue.global(qos:DispatchQoS.QoSClass.background).async
        {
            self.asyncCleanResources()
        }
    }
}

{'fraction_non_alphanumeric': 0.06380316930775647, 'fraction_numerical': 0.00035744072441320146, 'mean_word_length': 1.1875162887672661, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1866259', 'n_tokens_mistral': 4312, 'n_tokens_neox': 3974, 'n_words': 643}