/*
 * 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.
 */

/**
 * @file
 *
 * This is a class for ConsoleWriter.
 */

package std.console

import std.io.OutputStream
import std.sync.*

foreign func CJ_CONSOLE_Write(fd: Int32, c: CPointer<UInt8>, len: Int64, newLine: Bool): Unit

foreign func CJ_CONSOLE_Flush(fd: Int32): Unit

@Deprecated[message: "This class migrate to the std.env."]
public class ConsoleWriter <: OutputStream {
    private let mutex: ReentrantMutex = ReentrantMutex()

    /* Write buffer size is 512 bytes */
    private let bufferSize: Int64 = 512

    /* Write buffer */
    private let buffer_: Array<UInt8> = Array<UInt8>(this.bufferSize, repeat: 0)

    private let fd_: Int32

    /**
     * Init the ConsoleWriteStream, initialize the buffer.
     */
    init(fd: Int32) {
        this.fd_ = fd
    }

    public func flush(): Unit {
        synchronized(mutex) {
            unsafe { CJ_CONSOLE_Flush(this.fd_) }
        }
    }

    public func write(buffer: Array<Byte>): Unit {
        synchronized(mutex) {
            directWrite(buffer, buffer.size)
        }
    }

    public func writeln(buffer: Array<Byte>): Unit {
        synchronized(mutex) {
            directWrite(buffer, buffer.size, newline: true)
        }
    }

    public func write(v: String): Unit {
        if (v.isEmpty()) {
            return
        }
        write(unsafe { v.rawData() })
    }

    public func write<T>(v: T): Unit where T <: ToString {
        write(v.toString())
    }

    public func write(v: Bool): Unit {
        write(v.toString())
    }

    public func write(v: Int8): Unit {
        writeSigned(Int64(v), newline: false)
    }

    public func write(v: Int16): Unit {
        writeSigned(Int64(v), newline: false)
    }

    public func write(v: Int32): Unit {
        writeSigned(Int64(v), newline: false)
    }

    public func write(v: Int64): Unit {
        writeSigned(v, newline: false)
    }

    public func write(v: UInt8): Unit {
        writeUnsigned(UInt64(v), newline: false)
    }

    public func write(v: UInt16): Unit {
        writeUnsigned(UInt64(v), newline: false)
    }

    public func write(v: UInt32): Unit {
        writeUnsigned(UInt64(v), newline: false)
    }

    public func write(v: UInt64): Unit {
        writeUnsigned(v, newline: false)
    }

    public func write(v: Float16): Unit {
        writeFloat(Float64(v), newline: false)
    }

    public func write(v: Float32): Unit {
        writeFloat(Float64(v), newline: false)
    }

    public func write(v: Float64): Unit {
        writeFloat(v, newline: false)
    }

    public func write(v: Rune): Unit {
        writeRune(v, newline: false)
    }

    public func writeln(v: String): Unit {
        writeln(unsafe { v.rawData() })
    }

    public func writeln<T>(v: T): Unit where T <: ToString {
        writeln(v.toString())
    }

    public func writeln(v: Bool): Unit {
        writeln(v.toString())
    }

    public func writeln(v: Int8): Unit {
        writeSigned(Int64(v), newline: true)
    }

    public func writeln(v: Int16): Unit {
        writeSigned(Int64(v), newline: true)
    }

    public func writeln(v: Int32): Unit {
        writeSigned(Int64(v), newline: true)
    }

    public func writeln(v: Int64): Unit {
        writeSigned(v, newline: true)
    }

    public func writeln(v: UInt8): Unit {
        writeUnsigned(UInt64(v), newline: true)
    }

    public func writeln(v: UInt16): Unit {
        writeUnsigned(UInt64(v), newline: true)
    }

    public func writeln(v: UInt32): Unit {
        writeUnsigned(UInt64(v), newline: true)
    }

    public func writeln(v: UInt64): Unit {
        writeUnsigned(v, newline: true)
    }

    public func writeln(v: Float16): Unit {
        writeFloat(Float64(v), newline: true)
    }

    public func writeln(v: Float32): Unit {
        writeFloat(Float64(v), newline: true)
    }

    public func writeln(v: Float64): Unit {
        writeFloat(v, newline: true)
    }

    public func writeln(v: Rune): Unit {
        writeRune(v, newline: true)
    }

    private func writeRune(v: Rune, newline!: Bool): Unit {
        var itemBytes: Array<UInt8> = Array<UInt8>(6, repeat: 0)
        let len = Rune.intoUtf8Array(v, itemBytes, 0)
        if (newline) {
            writeln(itemBytes[..len])
        } else {
            write(itemBytes[..len])
        }
    }

    private func writeSigned(v: Int64, newline!: Bool): Unit {
        synchronized(mutex) {
            var writePos: Int64
            unsafe {
                let cp = acquireArrayRawData(this.buffer_)
                writePos = CJ_BUFFER_Int64ToCPointer(v, cp.pointer, this.bufferSize)
                releaseArrayRawData(cp)
            }
            if (newline) {
                this.buffer_[writePos] = b'\n'
                writePos++
            }
            directWrite(this.buffer_, writePos)
        }
    }

    private func writeUnsigned(v: UInt64, newline!: Bool): Unit {
        synchronized(mutex) {
            var writePos: Int64
            unsafe {
                let cp = acquireArrayRawData(this.buffer_)
                writePos = CJ_BUFFER_UInt64ToCPointer(v, cp.pointer, this.bufferSize)
                releaseArrayRawData(cp)
            }
            if (newline) {
                this.buffer_[writePos] = b'\n'
                writePos++
            }
            directWrite(this.buffer_, writePos)
        }
    }

    private func writeFloat(v: Float64, newline!: Bool): Unit {
        synchronized(mutex) {
            var writePos: Int64
            unsafe {
                let cp = acquireArrayRawData(this.buffer_)
                writePos = CJ_BUFFER_Float64ToCPointer(v, cp.pointer, this.bufferSize)
                releaseArrayRawData(cp)
            }
            if (newline) {
                this.buffer_[writePos] = b'\n'
                writePos++
            }
            directWrite(this.buffer_, writePos)
        }
    }

    /**
     * Write buffer data into stdOut.
     *
     * @param arr write Array.
     * @param off The write data is placed into b from the index.
     * @param cnt write cnt bytes.
     *
     * @return Int64
     *
     * @since 0.24.1
     */
    private func directWrite(arr: Array<UInt8>, cnt: Int64, newline!: Bool = false): Int64 {
        unsafe {
            var pos: Int64 = 0
            var ptr = acquireArrayRawData(arr)

            while (cnt - pos >= Int64(Int32.Max)) {
                CJ_CONSOLE_Write(this.fd_, ptr.pointer + pos, Int64(Int32.Max), false)
                pos += Int64(Int32.Max)
            }

            CJ_CONSOLE_Write(this.fd_, ptr.pointer + pos, cnt - pos, newline)
            releaseArrayRawData(ptr)
        }
        return cnt
    }
}