/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * This source file is part of the Cangjie project, licensed under Apache-2.0
 * with Runtime Library Exception.
 *
 * See https://cangjie-lang.cn/pages/LICENSE for license information.
 */

// The Cangjie API is in Beta. For details on its capabilities and limitations, please refer to the README file.

package std.sync

public class SyncCounter {
    // Since there is a `prop` to read this field, we should make it atomic.
    private var countValue: AtomicInt64
    private let syncList: SyncList

    /**
     * Construct a counter with an initial value.
     * @throw IllegalArgumentException, if `count` is negative.
     */
    public init(count: Int64) {
        if (count < 0) {
            throw IllegalArgumentException("Negative count.")
        }
        countValue = AtomicInt64(count)
        syncList = SyncList()
    }

    /**
     * Get the remaining count.
     */
    public prop count: Int64 {
        get() {
            return countValue.load()
        }
    }

    /**
     * Decrease the count.
     * If the count becomes zero, wake up all blocked threads.
     * If the count is equal or less than zero, it has no effects.
     */
    public func dec(): Unit {
        while (true) {
            let cur = countValue.load()
            if (cur <= 0) {
                return
            }
            if (countValue.compareAndSwap(cur, cur - 1)) {
                if (cur == 1) { // Case 1: The count becomes zero
                    syncList.notifyAll()
                }
                // Case 2: Otherwise, there are still available counts; do nothing
                return
            }
        }
    }

    /**
     * Wait until the count becomes zero or `timeout` passed.
     * If the count is zero, it will never be blocked.
     * All events before `dec()` happens-before events after `waitUntilZero()`.
     * NOTE: `timeout` should be `Duration.Max` by default,
     * however, the value `Duration.Max` is not supported currently.
     */
    public func waitUntilZero(timeout!: Duration = Duration.Max): Unit {
        syncList.waitIf({=> countValue.load() > 0}, timeout: timeout)
    }
}