import unix;
import io.errors.*;



//
// RawFile
//

record RawFile (
    handle : Int,
    owned : Bool,
);


overload RegularRecord?(static RawFile) = false;



//
// stdinRawFile, stdoutRawFile, stderrRawFile
//

stdinRawFile()  = RawFile(0, false);
stdoutRawFile() = RawFile(1, false);
stderrRawFile() = RawFile(2, false);



//
// constructors, destroy
//

private openFlags(mode) {
    switch (mode)
    case (READ)
        return unix.O_RDONLY;
    case (READ_WRITE)
        return unix.O_RDWR;
    case (CREATE)
        return bitor(unix.O_RDWR, unix.O_CREAT, unix.O_TRUNC);
    case (APPEND)
        return bitor(unix.O_WRONLY, unix.O_CREAT, unix.O_APPEND);
    else {
        assert(false);
        return -1;
    }
}

[S | CCompatibleString?(S)]
overload RawFile(path:S, mode:OpenMode) {
    var flags = openFlags(mode);
    var handle = Int();
    if (bitand(flags, unix.O_CREAT) != 0)
        handle = unix.open(cstring(path), flags, octal(#"0644"));
    else
        handle = unix.open(cstring(path), flags);
    if (handle == -1)
        throw GenericIOError(unix.errno());
    return RawFile(handle, true);
}

[S | CCompatibleString?(S)]
overload RawFile(path:S) = RawFile(path, READ);

overload destroy(x:RawFile) {
    if (x.owned) {
        var result = unix.close(x.handle);
        assert(result != wrapCast(Type(result), -1) or unix.errno() != unix.EBADF);
    }
}


// moveUnsafe, resetUnsafe

overload BitwiseMovedType?(static RawFile) = true;

overload resetUnsafe(x:RawFile) {
    x <-- RawFile(-1, false);
}



//
// fileHandle
//

overload fileHandle(f:RawFile) = f.handle;



//
// read, write, flush
//

overload read(f:RawFile, buffer:Pointer[Byte], n:SizeT) : SizeT {
    var result = unix.read(f.handle, buffer, n);
    if (result == Type(result)(-1))
        throw GenericIOError(unix.errno());
    return SizeT(result);
}

overload write(f:RawFile, buffer:Pointer[Byte], n:SizeT) {
    var result = unix.write(f.handle, buffer, n);
    if (result == Type(result)(-1))
        throw GenericIOError(unix.errno());
}

overload flush(f:RawFile) {
}



//
// fileSize, seek
//

overload fileSize(f:RawFile) {
    var statBuf = unix.Struct_stat();
    var result = unix.fstat(f.handle, &statBuf);
    if (result == Type(result)(-1))
        throw GenericIOError(unix.errno());
    return Int64(statBuf.st_size);
}

private seekWhence(x) {
    switch (x)
    case (SEEK_SET) return unix.SEEK_SET;
    case (SEEK_CUR) return unix.SEEK_CUR;
    case (SEEK_END) return unix.SEEK_END;
    else {
        assert(false);
        return -1;
    }
}

overload seek(f:RawFile, offset, whence) {
    var result = unix.lseek(f.handle, Int64(offset), seekWhence(whence));
    if (result == Type(result)(-1))
        throw GenericIOError(unix.errno());
    return result;
}



//
// terminal?
//

overload terminal?(f:RawFile) {
    return Bool(unix.isatty(f.handle));
}
