//
// System.IO/FileStream.cs
//
// Author:
//   Dietmar Maurer (dietmar@ximian.com)
//
// (C) 2001 Ximian, Inc.  http://www.ximian.com
//
using System;
using System.Runtime.InteropServices;
using System.Private;

// fixme: I do not know how to handle errno when calling PInvoke functions
// fixme: emit the correct exceptions everywhere

namespace System.IO
{

	public class FileStream : Stream
	{
			
		private IntPtr fd;
		private FileAccess acc;
		private bool owner;
		
		private int getUnixFlags (FileMode mode, FileAccess access)
		{
			int flags = 0;

			switch (access) {
			case FileAccess.Read:
				flags = Wrapper.O_RDONLY;
				break;
			case FileAccess.Write:
				flags = Wrapper.O_WRONLY;
				break;
			case FileAccess.ReadWrite:
				flags = Wrapper.O_RDWR;
				break;
			}
			
			switch (mode) {
			case FileMode.Append:
				flags |= Wrapper.O_APPEND;
				break;
			case FileMode.Create:
				flags |= Wrapper.O_CREAT;
				break;
			case FileMode.CreateNew:
				flags |= Wrapper.O_CREAT |  Wrapper.O_EXCL;
				break;
			case FileMode.Open:
				break;
			case FileMode.OpenOrCreate:
				flags |= Wrapper.O_CREAT;				
				break;
			case FileMode.Truncate:
				flags |= Wrapper.O_TRUNC;
				break;
			}

			return flags;
		}
		
		public FileStream (IntPtr fd, FileAccess access)
			: this (fd, access, true, 0, false) {}

		public FileStream (IntPtr fd, FileAccess access, bool ownsHandle)
			: this (fd, access, ownsHandle, 0, false) {}
		
		public FileStream (IntPtr fd, FileAccess access, bool ownsHandle, int bufferSize)
			: this (fd, access, ownsHandle, bufferSize, false) {}
		
		public FileStream (IntPtr fd, FileAccess access, bool ownsHandle,
				   int bufferSize, bool isAsync)
		{
			fd = fd;
			acc = access;
			owner = ownsHandle;
		}
		
		public FileStream (string name, FileMode mode)
			: this (name, mode, FileAccess.ReadWrite, FileShare.ReadWrite, 0, false) {}

		public FileStream (string name, FileMode mode, FileAccess access)
			: this (name, mode, access, FileShare.ReadWrite, 0, false) {}

		public FileStream (string name, FileMode mode, FileAccess access, FileShare share)
			: this (name, mode, access, share, 0, false) {}
		
		public FileStream (string name, FileMode mode, FileAccess access,
				   FileShare share, int buferSize)
			: this (name, mode, access, share, 0, false) {}

		// fixme: implement all share, buffer, async
		public FileStream (string name, FileMode mode, FileAccess access, FileShare share,
				   int buferSize, bool useAsync)
		{
			int flags = getUnixFlags (mode, access);
			
			if ((int)(fd = Wrapper.open (name, flags, 0x1a4)) == -1)
				throw new IOException();

			acc = access;
			owner = true;
		}
		
		public override bool CanRead
		{
			get {
				switch (acc) {
				case FileAccess.Read:
				case FileAccess.ReadWrite:
					return true;
				case FileAccess.Write:
				default:
					return false;
				}
			}
		}

		public override bool CanSeek
		{
                        get {
				// fixme: not alway true
                                return true;
                        }
                }

                public override bool CanWrite
		{
                        get {
				switch (acc) {
				case FileAccess.Write:
				case FileAccess.ReadWrite:
					return true;
				default:
					return false;
				}
                        }
                }

		unsafe public override long Length
		{
			get {
				stat fs;

				Wrapper.fstat (fd, &fs);
				return fs.st_size;
			}
		}

		public override long Position
		{
			get {
				return Wrapper.seek (fd, 0,  Wrapper.SEEK_CUR);
			}
			set {
				Wrapper.seek (fd, value, Wrapper.SEEK_SET);
			}
		}

		public override void Flush ()
		{
		}

		public override void Close ()
		{
			if (owner && Wrapper.close (fd) != 0)
				throw new IOException();
		}

		public unsafe override int Read (byte[] buffer,
					  int offset,
					  int count)
		{
			int res;

			fixed (void *p = &buffer [offset]) {
				res = Wrapper.read (fd, p, count);
			}
			
			return res;
		}

		public unsafe override int ReadByte ()
		{
			byte val;
			
			if (Wrapper.read (fd, &val, 1) != 1)
				throw new IOException();
			
			return val;
		}

		public override long Seek (long offset,
					   SeekOrigin origin)
		{
			int off = (int)offset;
			
			switch (origin) {
			case SeekOrigin.End:
				return Wrapper.seek (fd, Wrapper.SEEK_END, off);
			case SeekOrigin.Current:
				return Wrapper.seek (fd, Wrapper.SEEK_CUR, off);
			default:
				return Wrapper.seek (fd, Wrapper.SEEK_SET, off);
			}
		}

		public override void SetLength (long value)
		{
			int res;

			if ((res = Wrapper.ftruncate (fd, value)) == -1)
				throw new IOException();

			
		}

		public unsafe override void Write (byte[] buffer,
						   int offset,
						   int count)
		{
			int res;
			
			fixed (void *p = &buffer [offset]) {
				res = Wrapper.write (fd, p, count);
			}
			
			if (res != count)
				throw new IOException();
		}

		public unsafe override void WriteByte (byte value)
		{
			if (Wrapper.write (fd, &value, 1) != 1)
				throw new IOException();
		}

	}
}
