import Foundation
import SwiftUI
import Combine

class AnglePickerViewModel: ObservableObject {

    struct Angles: Hashable, Equatable {
        var start: Double
        var closing: Double
    }

    struct Pie: Hashable, Equatable {
        var startAngle: Double
        var closingAngle: Double
        var clockwise: Bool
        var label: String
        var object: AnyHashable
        var icon: NSImage?
        var color: Color
    }

    struct Config: Equatable {
        let totalValue: CGFloat
        let knobRadius: CGFloat
        let radius: CGFloat
        let offset: CGFloat
    }

    @Published var selectedAngles: Angles = Angles(start: 0, closing: 0)
    @Published var pies: [Pie] = []
    @Published var config: Config
    @Published var active: Bool = false
    @Published var knobColor = Color.primary
    @Published var color = Color.secondary
    @Published var borderColor = Color.secondary
    var isEditable: Bool = true

    
    class GestureState: ObservableObject {
        @Published var startingAngle: Angle? = nil
        @Published var closingAngle: Angle? = nil
        @Published var cursorAngle: Angle? = nil
        
        var publisher: AnyPublisher<AnglePickerViewModel.GestureState, Never> {
            Publishers.Merge3(
                $startingAngle.map { _ in () },
                $closingAngle.map { _ in () },
                $cursorAngle.map { _ in () }
            ).compactMap { [weak self] _ in
                return self
            }.eraseToAnyPublisher()
        }
    }
    
    init(config: Config = Config(
        totalValue: 359.0,
        knobRadius: 5.0,
        radius: 350,
        offset: -20
    )) {
        self.config = config
    }
    
    var publisher: AnyPublisher<AnglePickerViewModel, Never> {
        Publishers.Merge4(
            $selectedAngles.map { _ in () },
            $pies.map { _ in () },
            $config.map { _ in () },
            $active.map { _ in () }
        ).compactMap { [weak self] _ in
            return self ?? nil
        }.eraseToAnyPublisher()
    }
    
    func bind(to other: AnglePickerViewModel, inverse: Bool = false) -> Set<AnyCancellable> {
        return bind(to: other, inverse: inverse, on: DispatchQueue.main)
    }
    
    func bind<TScheduler: Scheduler>(to other: AnglePickerViewModel, inverse: Bool = false, on scheduler: TScheduler) -> Set<AnyCancellable> {
        if inverse {
            return other.bind(to: self, inverse: false, on: scheduler)
        }
        selectedAngles = other.selectedAngles
        pies = other.pies
        config = other.config
        active = other.active
        return [
            other.$selectedAngles
                .receive(on: scheduler)
                .filter { [weak self] in
                    $0 != self?.selectedAngles
                }.sink { [weak self] in
                    self?.selectedAngles = $0
                },
            self.$selectedAngles
                .receive(on: scheduler)
                .filter { [weak other] in
                    other?.selectedAngles != $0
                }.sink { [weak other] in
                    other?.selectedAngles = $0
                },
            other.$pies
                .receive(on: scheduler)
                .filter { [weak self] in
                    $0 != self?.pies
                }.sink { [weak self] in
                    self?.pies = $0
                },
            self.$pies
                .receive(on: scheduler)
                .filter { [weak other] in
                    other?.pies != $0
                }.sink { [weak other] in
                    other?.pies = $0
                },
            other.$config
                .receive(on: scheduler)
                .filter { [weak self] in
                    $0 != self?.config
                }.sink { [weak self] in
                    self?.config = $0
                },
            self.$config
                .receive(on: scheduler)
                .filter { [weak other] in
                    other?.config != $0
                }.sink { [weak other] in
                    other?.config = $0
                },
            other.$active
                .receive(on: scheduler)
                .filter { [weak self] in
                    $0 != self?.active
                }.sink { [weak self] in
                    self?.active = $0
                },
            self.$active
                .receive(on: scheduler)
                .filter { [weak other] in
                    other?.active != $0
                }.sink { [weak other] in
                    other?.active = $0
                }
            
        ]
        
    }
    
}
