import Foundation

let doNothing: () -> Void = { }

public enum NoValue {
}

public enum NoError {
}

public func identity<A>(a: A) -> A {
    return a
}

precedencegroup PipeRight {
    associativity: left
    higherThan: DefaultPrecedence
}

infix operator |> : PipeRight

public func |> <T, U>(value: T, function: ((T) -> U)) -> U {
    return function(value)
}

private final class SubscriberDisposable<T, E>: Disposable, CustomStringConvertible {
    private weak var subscriber: Subscriber<T, E>?
    
    private var lock = pthread_mutex_t()
    private var disposable: Disposable?
    
    init(subscriber: Subscriber<T, E>, disposable: Disposable?) {
        self.subscriber = subscriber
        self.disposable = disposable
        
        pthread_mutex_init(&self.lock, nil)
    }
    
    deinit {
        pthread_mutex_destroy(&self.lock)
    }
    
    func dispose() {
        var subscriber: Subscriber<T, E>?
        
        var disposeItem: Disposable?
        pthread_mutex_lock(&self.lock)
        disposeItem = self.disposable
        subscriber = self.subscriber
        self.subscriber = nil
        self.disposable = nil
        pthread_mutex_unlock(&self.lock)
        
        disposeItem?.dispose()
        subscriber?.markTerminatedWithoutDisposal()
    }
    
    public var description: String {
        return "SubscriberDisposable { disposable: \(self.disposable == nil ? "nil" : "hasValue") }"
    }
}

public final class Signal<T, E> {
    private let generator: (Subscriber<T, E>) -> Disposable
    
    public init(_ generator: @escaping(Subscriber<T, E>) -> Disposable) {
        self.generator = generator
    }
    
    public func start(next: ((T) -> Void)! = nil, error: ((E) -> Void)! = nil, completed: (() -> Void)! = nil) -> Disposable {
        let subscriber = Subscriber<T, E>(next: next, error: error, completed: completed)
        let disposable = self.generator(subscriber)
        let wrappedDisposable = subscriber.assignDisposable(disposable)
        return SubscriberDisposable(subscriber: subscriber, disposable: wrappedDisposable)
    }
    
    public func startStandalone(next: ((T) -> Void)! = nil, error: ((E) -> Void)! = nil, completed: (() -> Void)! = nil) -> Disposable {
        let subscriber = Subscriber<T, E>(next: next, error: error, completed: completed)
        let disposable = self.generator(subscriber)
        let wrappedDisposable = subscriber.assignDisposable(disposable)
        return SubscriberDisposable(subscriber: subscriber, disposable: wrappedDisposable)
    }
    
    public func startStrict(next: ((T) -> Void)! = nil, error: ((E) -> Void)! = nil, completed: (() -> Void)! = nil, file: String = #file, line: Int = #line) -> Disposable {
        let subscriber = Subscriber<T, E>(next: next, error: error, completed: completed)
        let disposable = self.generator(subscriber)
        let wrappedDisposable = subscriber.assignDisposable(disposable)
        return SubscriberDisposable(subscriber: subscriber, disposable: wrappedDisposable).strict(file: file, line: line)
    }
    
    public static func single(_ value: T) -> Signal<T, E> {
        return Signal<T, E> { subscriber in
            subscriber.putNext(value)
            subscriber.putCompletion()
            
            return EmptyDisposable
        }
    }
    
    public static func complete() -> Signal<T, E> {
        return Signal<T, E> { subscriber in
            subscriber.putCompletion()
            
            return EmptyDisposable
        }
    }
    
    public static func fail(_ error: E) -> Signal<T, E> {
        return Signal<T, E> { subscriber in
            subscriber.putError(error)
            
            return EmptyDisposable
        }
    }
    
    public static func never() -> Signal<T, E> {
        return Signal<T, E> { _ in
            return EmptyDisposable
        }
    }
}