<commit_before>//
//  BrickCollectionView.swift
//  BrickApp
//
//  Created by <NAME> on 5/26/16.
//  Copyright © 2016 Wayfair. All rights reserved.
//

import UIKit

/// Tuple describing the brick, its index and the index/identifier of the collectionbrick
public typealias BrickInfo = (brick: Brick, index: Int, collectionIndex: Int, collectionIdentifier: String)

/// Prefix used for custom registerd nibs, to avoid name collission
private let CustomNibPrefix = "CustomNib."

/// The BrickCollectionView class manages the views of bricks that a BrickSection represents
public class BrickCollectionView: UICollectionView {

    // MARK: - Public Properties

    /// Static reference to initialize all images that need to be downloaded
    public static var imageDownloader: ImageDownloader = NSURLSessionImageDownloader()

    /// The brick layout
    public var layout: BrickLayout {
        return collectionViewLayout as! BrickLayout
    }

    /// Override to check if the layout is a BrickLayout
    public override var collectionViewLayout: UICollectionViewLayout {
        willSet {
            if !(newValue is BrickLayout) {
                fatalError("BrickCollectionView: the layout needs to be of type `BrickLayout`")
            }
        }
    }

    // MARK: - Private Properties

    /// Index of the CollectionBrick that this BrickCollectionView is in
    internal var collectionInfo: CollectionInfo = CollectionInfo(index: 0, identifier: "") {
        didSet {
            section.invalidateCounts(in: collectionInfo)
        }
    }

    /// Section model. Starts with an empty brick section
    public private(set) var section: BrickSection = BrickSection(bricks: []) {
        didSet {
            section.invalidateCounts(in: collectionInfo)
            self.reloadData()
        }
    }

    /// Dictionary to keep track of what identifier to load for a certain brick
    internal private(set) var registeredBricks: [String:String] = [:]

    internal private(set) var isConfiguringCollectionBrick: Bool = false

    // MARK: - Overrides

    public override var frame: CGRect {
        didSet {
            // Current workaround: If the layout has behaviors, `shouldInvalidateLayoutForBoundsChange` is not called
            if frame != oldValue {
                self.invalidateOnBoundsChange()
            }
        }
    }

    // MARK: - Initializers

    public required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        initializeComponents()
    }

    public override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
        super.init(frame: frame, collectionViewLayout: layout)
        initializeComponents()
    }

    public init(frame: CGRect) {
        super.init(frame: frame, collectionViewLayout: BrickFlowLayout())
        initializeComponents()
    }

    private func initializeComponents() {
        autoreleasepool { // Setting the dataSource will result in not releasing the BrickCollectionView even when its being set to nil
            self.dataSource = self
        }
        layout.dataSource = self
        registerClass(BrickSectionCell.self, forCellWithReuseIdentifier: BrickSection.nibName)
    }

}

// MARK: - Setting up the models
extension BrickCollectionView {

    /// Sets the section for the BrickCollectionView
    ///
    /// - parameter section: Section to set
    public func setSection(section: BrickSection) {
        self.section = section
    }

    public func brick(at indexPath: NSIndexPath) -> Brick {
        self.section.invalidateIfNeeded(in: collectionInfo)
        guard let brick = section.brick(at: indexPath, in: collectionInfo) else {
            fatalError("Brick not found at indexPath: SECTION - \(indexPath.section) - ITEM: \(indexPath.item). This should never happen")
        }
        return brick
    }

    public func indexPathsForBricksWithIdentifier(identifier: String, index: Int? = nil) -> [NSIndexPath] {
        return self.section.indexPathsForBricksWithIdentifier(identifier, index: index, in: collectionInfo)
    }

    /// Register a brick class.
    /// If there is a class (`cellClass`) registered to this Brick, this will be registered to the UICollectionView.
    /// If there is no class, the nib (`nibName`) will be used to register
    ///
    /// - parameter brickClass: The brick class to register
    public func registerBrickClass(brickClass: Brick.Type) {
        let nibName = brickClass.nibName
        let cellIdentifier: String

        if let cellClass = brickClass.cellClass {
            cellIdentifier = nibName
            self.registerClass(cellClass, forCellWithReuseIdentifier: cellIdentifier)
        } else if let _ = brickClass.bundle.pathForResource(nibName, ofType: "nib") {
            let nib = UINib(nibName: nibName, bundle: brickClass.bundle)
            cellIdentifier = String(nib.hashValue)
            self.registerNib(nib, forCellWithReuseIdentifier: cellIdentifier)
        } else {
            fatalError("Nib or cell class not found")
        }
        
        if isConfiguringCollectionBrick {
            print("calling `registerBrickClass` in `configure(for cell: CollectionBrickCell)` is depercated. Use `registerBricks(for cell: CollectionBrickCell)` or `CollectionBrickCell(brickTypes: [Brick.Type])`. This will be a fatalError in a future release")
        }
        
        registeredBricks[nibName] = cellIdentifier
    }

    /// Register a brick class.
    /// If there is a class (`cellClass`) registered to this Brick, this will be registered to the UICollectionView.
    /// If there is no class, the nib (`nibName`) will be used to register
    ///
    /// - parameter brickClass: The brick class to register
    public func registerNib(nib: UINib, forBrickWithIdentifier identifier: String) {
        let cellIdentifier = String(nib.hashValue)
        self.registerNib(nib, forCellWithReuseIdentifier: cellIdentifier)
        
        if isConfiguringCollectionBrick {
            print("calling `registerNib` in `configure(for cell: CollectionBrickCell)` is depercated. Use `registerBricks(for cell: CollectionBrickCell)` or `CollectionBrickCell(brickTypes: [Brick.Type])`. This will be a fatalError in a future release")
        }
        
        registeredBricks[CustomNibPrefix + identifier] = cellIdentifier
    }

    /// Get the brick, index and collection index of a certain indexPath
    ///
    /// - parameter indexPath: IndexPath
    ///
    /// - returns: BrickInfo
    public func brickInfo(at indexPath: NSIndexPath) -> BrickInfo {
        guard let brickAndIndex = section.brickAndIndex(at: indexPath, in: collectionInfo) else {
            fatalError("Brick and index not found at indexPath: SECTION - \(indexPath.section) - ITEM: \(indexPath.item). This should never happen")
        }
        return (brickAndIndex.0, brickAndIndex.1, collectionInfo.index, collectionInfo.identifier)
    }

    // MARK: - Internal methods

    internal func resetRegisteredBricks() {
        registeredBricks = [:]
    }

    internal func identifierForBrick(brick: Brick, collectionView: UICollectionView) -> String {
        let identifier: String
        if brick is BrickSection {<commit_msg>Removed incorrect information in deprecation statement<commit_after>//
//  BrickCollectionView.swift
//  BrickApp
//
//  Created by <NAME> on 5/26/16.
//  Copyright © 2016 Wayfair. All rights reserved.
//

import UIKit

/// Tuple describing the brick, its index and the index/identifier of the collectionbrick
public typealias BrickInfo = (brick: Brick, index: Int, collectionIndex: Int, collectionIdentifier: String)

/// Prefix used for custom registerd nibs, to avoid name collission
private let CustomNibPrefix = "CustomNib."

/// The BrickCollectionView class manages the views of bricks that a BrickSection represents
public class BrickCollectionView: UICollectionView {

    // MARK: - Public Properties

    /// Static reference to initialize all images that need to be downloaded
    public static var imageDownloader: ImageDownloader = NSURLSessionImageDownloader()

    /// The brick layout
    public var layout: BrickLayout {
        return collectionViewLayout as! BrickLayout
    }

    /// Override to check if the layout is a BrickLayout
    public override var collectionViewLayout: UICollectionViewLayout {
        willSet {
            if !(newValue is BrickLayout) {
                fatalError("BrickCollectionView: the layout needs to be of type `BrickLayout`")
            }
        }
    }

    // MARK: - Private Properties

    /// Index of the CollectionBrick that this BrickCollectionView is in
    internal var collectionInfo: CollectionInfo = CollectionInfo(index: 0, identifier: "") {
        didSet {
            section.invalidateCounts(in: collectionInfo)
        }
    }

    /// Section model. Starts with an empty brick section
    public private(set) var section: BrickSection = BrickSection(bricks: []) {
        didSet {
            section.invalidateCounts(in: collectionInfo)
            self.reloadData()
        }
    }

    /// Dictionary to keep track of what identifier to load for a certain brick
    internal private(set) var registeredBricks: [String:String] = [:]

    internal private(set) var isConfiguringCollectionBrick: Bool = false

    // MARK: - Overrides

    public override var frame: CGRect {
        didSet {
            // Current workaround: If the layout has behaviors, `shouldInvalidateLayoutForBoundsChange` is not called
            if frame != oldValue {
                self.invalidateOnBoundsChange()
            }
        }
    }

    // MARK: - Initializers

    public required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        initializeComponents()
    }

    public override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
        super.init(frame: frame, collectionViewLayout: layout)
        initializeComponents()
    }

    public init(frame: CGRect) {
        super.init(frame: frame, collectionViewLayout: BrickFlowLayout())
        initializeComponents()
    }

    private func initializeComponents() {
        autoreleasepool { // Setting the dataSource will result in not releasing the BrickCollectionView even when its being set to nil
            self.dataSource = self
        }
        layout.dataSource = self
        registerClass(BrickSectionCell.self, forCellWithReuseIdentifier: BrickSection.nibName)
    }

}

// MARK: - Setting up the models
extension BrickCollectionView {

    /// Sets the section for the BrickCollectionView
    ///
    /// - parameter section: Section to set
    public func setSection(section: BrickSection) {
        self.section = section
    }

    public func brick(at indexPath: NSIndexPath) -> Brick {
        self.section.invalidateIfNeeded(in: collectionInfo)
        guard let brick = section.brick(at: indexPath, in: collectionInfo) else {
            fatalError("Brick not found at indexPath: SECTION - \(indexPath.section) - ITEM: \(indexPath.item). This should never happen")
        }
        return brick
    }

    public func indexPathsForBricksWithIdentifier(identifier: String, index: Int? = nil) -> [NSIndexPath] {
        return self.section.indexPathsForBricksWithIdentifier(identifier, index: index, in: collectionInfo)
    }

    /// Register a brick class.
    /// If there is a class (`cellClass`) registered to this Brick, this will be registered to the UICollectionView.
    /// If there is no class, the nib (`nibName`) will be used to register
    ///
    /// - parameter brickClass: The brick class to register
    public func registerBrickClass(brickClass: Brick.Type) {
        let nibName = brickClass.nibName
        let cellIdentifier: String

        if let cellClass = brickClass.cellClass {
            cellIdentifier = nibName
            self.registerClass(cellClass, forCellWithReuseIdentifier: cellIdentifier)
        } else if let _ = brickClass.bundle.pathForResource(nibName, ofType: "nib") {
            let nib = UINib(nibName: nibName, bundle: brickClass.bundle)
            cellIdentifier = String(nib.hashValue)
            self.registerNib(nib, forCellWithReuseIdentifier: cellIdentifier)
        } else {
            fatalError("Nib or cell class not found")
        }
        
        if isConfiguringCollectionBrick {
            print("calling `registerBrickClass` in `configure(for cell: CollectionBrickCell)` is deprecated. Use `registerBricks(for cell: CollectionBrickCell)` or `CollectionBrickCell(brickTypes: [Brick.Type])`. This will be a fatalError in a future release")
        }
        
        registeredBricks[nibName] = cellIdentifier
    }

    /// Register a brick class.
    /// If there is a class (`cellClass`) registered to this Brick, this will be registered to the UICollectionView.
    /// If there is no class, the nib (`nibName`) will be used to register
    ///
    /// - parameter brickClass: The brick class to register
    public func registerNib(nib: UINib, forBrickWithIdentifier identifier: String) {
        let cellIdentifier = String(nib.hashValue)
        self.registerNib(nib, forCellWithReuseIdentifier: cellIdentifier)
        
        if isConfiguringCollectionBrick {
            print("calling `registerNib` in `configure(for cell: CollectionBrickCell)` is deprecated. Use `registerBricks(for cell: CollectionBrickCell)`. This will be a fatalError in a future release")
        }
        
        registeredBricks[CustomNibPrefix + identifier] = cellIdentifier
    }

    /// Get the brick, index and collection index of a certain indexPath
    ///
    /// - parameter indexPath: IndexPath
    ///
    /// - returns: BrickInfo
    public func brickInfo(at indexPath: NSIndexPath) -> BrickInfo {
        guard let brickAndIndex = section.brickAndIndex(at: indexPath, in: collectionInfo) else {
            fatalError("Brick and index not found at indexPath: SECTION - \(indexPath.section) - ITEM: \(indexPath.item). This should never happen")
        }
        return (brickAndIndex.0, brickAndIndex.1, collectionInfo.index, collectionInfo.identifier)
    }

    // MARK: - Internal methods

    internal func resetRegisteredBricks() {
        registeredBricks = [:]
    }

    internal func identifierForBrick(brick: Brick, collectionView: UICollectionView) -> String {
        let identifier: String
        if brick is BrickSection {
{'fraction_non_alphanumeric': 0.08248994870372937, 'fraction_numerical': 0.001663662830999584, 'mean_word_length': 2.7806603773584904, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7313221', 'n_tokens_mistral': 3879, 'n_tokens_neox': 3771, 'n_words': 1220}