use std::str::Chars;

/// Define the BurritOS running time basic unit
pub struct BurritosTime {
    seconds: i64,
    nanos: i64
}

/// A unique identifier for a thread executed within a user program
pub struct ThreadId{
    id: u64
}

/// The system call interface.  These are the operations the BurritOS
/// kernel needs to support, to be able to run user programs.
pub struct TError {
    t: i32
}

/// A unique identifier for an open BurritOS file.
pub struct OpenFiledId{
    id: u64
}

/// System calls concerning semaphores management
pub struct SemId{
    id: u64
}

/// System calls concerning locks management
pub struct LockId{
    id: u64
}

/// System calls concerning conditions variables.
pub struct CondId{
    id: u64
}

extern "C" {

    ///Stop BurritOS, and print out performance stats
    fn Shutdown() -> ();

    /// Return the time spent running BurritOS
    /// ## Param
    /// - **t** a struct to define the time unit
    fn SysTime(t: BurritosTime) -> ();

    /// This user program is done
    /// ## Param
    /// - **status** status at the end of execution *(status = 0 means exited normally)*.
    fn Exit(status: i32) -> ();

    /// Run the executable, stored in the BurritOS file "name", and return the
    /// master thread identifier
    fn Exec(name: *const char) -> ThreadId;

    /// Create a new thread in the current process
    ///  Return thread identifier
    fn newThread(debug_name: *const char, func: i32, arg: i32) -> ThreadId;

    /// Only return once the the thread "id" has finished.
    fn Join (id: ThreadId) -> TError;

    /// Yield the CPU to another runnable thread, whether in this address space
    ///  or not.
    fn Yield() -> ();

    /// Print the last error message with the personalized one "mess"
    fn PError(mess: *const char) -> ();

    /// Create a BurritOS file, with "name"
    fn Create(name: *const char, size: i32) -> TError;

    /// Open the Nachos file "name", and return an "OpenFileId" that can
    ///  be used to read and write to the file.
    fn Open(name: *const char) -> OpenFiledId;

    /// Write "size" bytes from "buffer" to the open file.
    fn Write(buffer: *const char, size: i32, id: OpenFiledId) -> TError;

    /// Read "size" bytes from the open file into "buffer".
    ///  Return the number of bytes actually read -- if the open file isn't
    ///  long enough, or if it is an I/O device, and there aren't enough
    ///  characters to read, return whatever is available (for I/O devices,
    ///  you should always wait until you can return at least one character).
    fn Read(buffer: *const char, size: i32, id:OpenFiledId) -> TError;

    /// Seek to a specified offset into an opened file
    fn Seek(offset: i32, id: OpenFiledId) -> TError;

    /// Close the file, we're done reading and writing to it.
    fn Close(id: OpenFiledId) -> TError;

    /// Remove the file
    fn Remove(name: *const char) -> TError;

    ////////////////////////////////////////////////////
    /// system calls concerning directory management ///
    ////////////////////////////////////////////////////

    /// Create a new repertory
    /// Return a negative number if an error ocurred.
    fn mkdir(name: *const char) -> t_length;

    /// Destroy a repertory, which must be empty.
    /// Return a negative number if an error ocurred.
    fn Rmdir(name: *const char) -> TError;

    /// List the content of BurritOS FileSystem
    fn FSList() -> TError;

    /// Create a semaphore, initialising it at count.
    /// Return a Semid, which will enable to do operations on this
    /// semaphore
    fn SemCreate(debug_name: *const char, count: i32) -> SemId;

    /// Destroy a semaphore identified by sema.
    /// Return a negative number if an error occured during the destruction
    fn SemDestroy(sema: SemId) -> TError;

    /// Do the operation P() on the semaphore sema
    fn P(sema: SemId) -> TError;

    /// Do the operation V() on the semaphore sema
    fn V(sema: SemId) -> TError;

    /// Create a lock.
    /// Return an identifier
    fn LockCreate(debug_name: *const char) -> LockId;

    /// Destroy a lock.
    /// Return a negative number if an error ocurred
    /// during the destruction.
    fn LockDestroy(id: LockId) -> TError;

    /// Do the operation Acquire on the lock id.
    /// Return a negative number if an error ocurred.
    fn LockAcquire(id: LockId) -> TError;

    /// Do the operation Release  on the lock id.
    /// Return a negative number if an error ocurred.
    fn LockRelease(id: LockId) -> TError;

    /// Create a new condition variable
    fn CondCreate(debug_name: *const char) -> CondId;

    /// Destroy a condition variable.
    /// Return a negative number if an error ocurred.
    fn CondDestroy(id: CondId) -> TError;

    /// Do the operation Wait on a condition variable.
    /// Returns a negative number if an error ocurred.
    fn CondWait(id: CondId) -> TError;

    /// Do the operation Signal on a condition variable (wake up only one thread).
    /// Return a negative number if an error ocurred.
    fn CondSignal(id: CondId) -> TError;

    /// Do the operation Signal on a condition variable (wake up all threads).
    /// Return a negative number if an error ocurred.
    fn CondBroadcast(id: CondId) -> TError;

    ///////////////////////////////////////////////////////
    /// # System calls concerning serial port and console
    ///////////////////////////////////////////////////////

    ///Send the message on the serial communication link.
    /// Returns the number of bytes successfully sent.
    fn TtySend(mess: *const char) -> i32;

    /// Wait for a message comming from the serial communication link.
    /// The length of the buffer where the bytes will be copied is given as a parameter.
    /// Returns the number of characters actually received.
    fn TtyReceive(mess: *const char, length: i32) -> i32;

    /// Map an opened file in memory. Size is the size to be mapped in bytes.
    fn Mmap(id: OpenFiledId, size: i32) -> *mut ();

    /// For debug purpose
    fn Debug(param: i32)-> ();
}