<commit_before>//
//  CardDetailsViewController.swift
//  MyCards
//
//  Created by <NAME> on 16/10/16.
//

import UIKit

final class CardDetailsViewController: UIViewController {

    fileprivate var history: [Card] = []
    fileprivate var card: Card {
        didSet {
            front.image = card.front
            back.image = card.back
            name.text = card.name
            doneButton.isEnabled = card.isValid
        }
    }
    fileprivate var mode: Mode {
        didSet {
            configureNavigationItem()
            configureModeForViews()
        }
    }
    fileprivate let worker: CoreDataWorkerProtocol
    fileprivate let loader: ResourceLoading

    // MARK: Views
    // codebeat:disable[TOO_MANY_IVARS]
    fileprivate lazy var editButton: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem:
        .edit, target: self, action: #selector(editTapped))
    fileprivate lazy var cancelButton: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem:
        .cancel, target: self, action: #selector(cancelTapped))
    fileprivate lazy var doneButton: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem:
        .done, target: self, action: #selector(doneTapped))
    fileprivate lazy var front: CardView = CardView(image: self.card.front).with {
        $0.tapped = { [unowned self] in self.cardTapped(.front) }
    }
    fileprivate lazy var back: CardView = CardView(image: self.card.back) .with {
        $0.tapped = { [unowned self] in self.cardTapped(.back) }
    }
    fileprivate lazy var name: UITextField = UITextField.makeNameField().with {
        $0.text = self.card.name
        $0.delegate = self
        $0.addTarget(self, action: #selector(nameChanged(sender:)), for: .editingChanged)
    }
    fileprivate lazy var toolbar: UIToolbar = UIToolbar.constrained().with {
        let delete = UIBarButtonItem(barButtonSystemItem:
            .trash, target: self, action: #selector(removeTapped))
        let flexible = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
        $0.items = [flexible, delete, flexible]
    }
    // codebeat:enable[TOO_MANY_IVARS]

    init(card: Card,
         mode: Mode = .normal,
         worker: CoreDataWorkerProtocol = CoreDataWorker(),
         loader: ResourceLoading = NetworkLoader.shared) {
        self.card = card
        self.mode = mode
        self.worker = worker
        self.loader = loader
        self.history.append(self.card)
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("NSCoding not supported")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        configureViews()
        configureNavigationItem()
        configureModeForViews()
        configureConstraints()
    }
}

extension CardDetailsViewController {
    fileprivate func configureViews() {
        view.backgroundColor = .white
        view.addSubview(name)
        view.addSubview(front)
        view.addSubview(back)
        view.addSubview(toolbar)
    }

    fileprivate func configureConstraints() {
        view.subviews.forEach { $0.translatesAutoresizingMaskIntoConstraints = false }
        var constraints: [NSLayoutConstraint] = []
        constraints.append(name.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 40))
        constraints.append(name.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20))
        constraints.append(name.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20))
        constraints.append(front.leadingAnchor.constraint(equalTo: name.leadingAnchor, constant: 0))
        constraints.append(front.trailingAnchor.constraint(equalTo: name.trailingAnchor, constant: 0))
        constraints.append(front.topAnchor.constraint(equalTo: name.bottomAnchor, constant: 20))
        constraints.append(front.heightAnchor.constraint(equalTo: front.widthAnchor, multiplier: 1 / .cardRatio))
        constraints.append(back.topAnchor.constraint(equalTo: front.bottomAnchor, constant: 20))
        constraints.append(back.leadingAnchor.constraint(equalTo: front.leadingAnchor, constant: 0))
        constraints.append(back.trailingAnchor.constraint(equalTo: front.trailingAnchor, constant: 0))
        constraints.append(back.heightAnchor.constraint(equalTo: front.heightAnchor, constant: 0))
        constraints.append(toolbar.heightAnchor.constraint(equalToConstant: 40))
        constraints.append(toolbar.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor))
        constraints.append(toolbar.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor))
        constraints.append(toolbar.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor))
        NSLayoutConstraint.activate(constraints)
    }

    fileprivate func configureNavigationItem() {
        title = mode == .create ? .AddNewCard : .CardDetails
        doneButton.isEnabled = card.isValid
        switch mode {
        case .normal:
            navigationItem.rightBarButtonItems = [editButton]
        case .edit, .create:
            navigationItem.rightBarButtonItems = [doneButton]
        }
        navigationItem.leftBarButtonItem = cancelButton
    }

    fileprivate func configureModeForViews() {
        let editMode = mode != .normal
        name.resignFirstResponder()
        name.isUserInteractionEnabled = editMode
        name.placeholder = editMode ? .EnterCardName : .NoName
        front.photoCamera.isHidden = !editMode
        back.photoCamera.isHidden = !editMode
        toolbar.isHidden = !(mode == .edit)
    }

    @objc fileprivate func doneTapped() {
        guard card.isValid else { return }
        persist(card)
        switch mode {
        case .create: dismiss()
        default: mode = .normal
        }
    }

    @objc fileprivate func removeTapped() {
        worker.remove(entities: [card]) { [weak self] error in
            error.flatMap { print("\($0)") }
            self?.dismiss()
        }
    }

    @objc fileprivate func cancelTapped() {
        switch mode {
        case .create, .normal: dismiss()
        case .edit:
            history.last.flatMap { card = $0 }
            mode = .normal
        }
    }

    @objc fileprivate func editTapped() {
        mode = .edit
    }

    @objc fileprivate func nameChanged(sender textField: UITextField) {
        guard let name = textField.text else { return }
        card = Card(identifier: card.identifier,
                    name: name,
                    front: card.front,<commit_msg>Fixes wrong thread for UI operation when a card is removed<commit_after>//
//  CardDetailsViewController.swift
//  MyCards
//
//  Created by <NAME> on 16/10/16.
//

import UIKit

final class CardDetailsViewController: UIViewController {

    fileprivate var history: [Card] = []
    fileprivate var card: Card {
        didSet {
            front.image = card.front
            back.image = card.back
            name.text = card.name
            doneButton.isEnabled = card.isValid
        }
    }
    fileprivate var mode: Mode {
        didSet {
            configureNavigationItem()
            configureModeForViews()
        }
    }
    fileprivate let worker: CoreDataWorkerProtocol
    fileprivate let loader: ResourceLoading

    // MARK: Views
    // codebeat:disable[TOO_MANY_IVARS]
    fileprivate lazy var editButton: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem:
        .edit, target: self, action: #selector(editTapped))
    fileprivate lazy var cancelButton: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem:
        .cancel, target: self, action: #selector(cancelTapped))
    fileprivate lazy var doneButton: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem:
        .done, target: self, action: #selector(doneTapped))
    fileprivate lazy var front: CardView = CardView(image: self.card.front).with {
        $0.tapped = { [unowned self] in self.cardTapped(.front) }
    }
    fileprivate lazy var back: CardView = CardView(image: self.card.back) .with {
        $0.tapped = { [unowned self] in self.cardTapped(.back) }
    }
    fileprivate lazy var name: UITextField = UITextField.makeNameField().with {
        $0.text = self.card.name
        $0.delegate = self
        $0.addTarget(self, action: #selector(nameChanged(sender:)), for: .editingChanged)
    }
    fileprivate lazy var toolbar: UIToolbar = UIToolbar.constrained().with {
        let delete = UIBarButtonItem(barButtonSystemItem:
            .trash, target: self, action: #selector(removeTapped))
        let flexible = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
        $0.items = [flexible, delete, flexible]
    }
    // codebeat:enable[TOO_MANY_IVARS]

    init(card: Card,
         mode: Mode = .normal,
         worker: CoreDataWorkerProtocol = CoreDataWorker(),
         loader: ResourceLoading = NetworkLoader.shared) {
        self.card = card
        self.mode = mode
        self.worker = worker
        self.loader = loader
        self.history.append(self.card)
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("NSCoding not supported")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        configureViews()
        configureNavigationItem()
        configureModeForViews()
        configureConstraints()
    }
}

extension CardDetailsViewController {
    fileprivate func configureViews() {
        view.backgroundColor = .white
        view.addSubview(name)
        view.addSubview(front)
        view.addSubview(back)
        view.addSubview(toolbar)
    }

    fileprivate func configureConstraints() {
        view.subviews.forEach { $0.translatesAutoresizingMaskIntoConstraints = false }
        var constraints: [NSLayoutConstraint] = []
        constraints.append(name.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 40))
        constraints.append(name.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20))
        constraints.append(name.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20))
        constraints.append(front.leadingAnchor.constraint(equalTo: name.leadingAnchor, constant: 0))
        constraints.append(front.trailingAnchor.constraint(equalTo: name.trailingAnchor, constant: 0))
        constraints.append(front.topAnchor.constraint(equalTo: name.bottomAnchor, constant: 20))
        constraints.append(front.heightAnchor.constraint(equalTo: front.widthAnchor, multiplier: 1 / .cardRatio))
        constraints.append(back.topAnchor.constraint(equalTo: front.bottomAnchor, constant: 20))
        constraints.append(back.leadingAnchor.constraint(equalTo: front.leadingAnchor, constant: 0))
        constraints.append(back.trailingAnchor.constraint(equalTo: front.trailingAnchor, constant: 0))
        constraints.append(back.heightAnchor.constraint(equalTo: front.heightAnchor, constant: 0))
        constraints.append(toolbar.heightAnchor.constraint(equalToConstant: 40))
        constraints.append(toolbar.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor))
        constraints.append(toolbar.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor))
        constraints.append(toolbar.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor))
        NSLayoutConstraint.activate(constraints)
    }

    fileprivate func configureNavigationItem() {
        title = mode == .create ? .AddNewCard : .CardDetails
        doneButton.isEnabled = card.isValid
        switch mode {
        case .normal:
            navigationItem.rightBarButtonItems = [editButton]
        case .edit, .create:
            navigationItem.rightBarButtonItems = [doneButton]
        }
        navigationItem.leftBarButtonItem = cancelButton
    }

    fileprivate func configureModeForViews() {
        let editMode = mode != .normal
        name.resignFirstResponder()
        name.isUserInteractionEnabled = editMode
        name.placeholder = editMode ? .EnterCardName : .NoName
        front.photoCamera.isHidden = !editMode
        back.photoCamera.isHidden = !editMode
        toolbar.isHidden = !(mode == .edit)
    }

    @objc fileprivate func doneTapped() {
        guard card.isValid else { return }
        persist(card)
        switch mode {
        case .create: dismiss()
        default: mode = .normal
        }
    }

    @objc fileprivate func removeTapped() {
        worker.remove(entities: [card]) { [weak self] error in
            error.flatMap { print("\($0)") }
            DispatchQueue.main.async {
                self?.dismiss()
            }
        }
    }

    @objc fileprivate func cancelTapped() {
        switch mode {
        case .create, .normal: dismiss()
        case .edit:
            history.last.flatMap { card = $0 }
            mode = .normal
        }
    }

    @objc fileprivate func editTapped() {
        mode = .edit
    }

    @objc fileprivate func nameChanged(sender textField: UITextField) {
        guard let name = textField.text else { return }
        card = Card(identifier: card.identifier,
                    name: name,
                    front: card.front,
{'fraction_non_alphanumeric': 0.09788953749438707, 'fraction_numerical': 0.004939380332285586, 'mean_word_length': 2.7876984126984126, 'pattern_counts': {'":': 0, '<': 5, '<?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': '5892391', 'n_tokens_mistral': 3732, 'n_tokens_neox': 3657, 'n_words': 766}