using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Security;
namespace ImportExport
{
    #region ManagedIStream object
    /// 
    /// ManagedIStream wraps a .Net Stream object as a COM IStream interface
    /// 
    /// 
    [Guid("0000000c-0000-0000-C000-000000000046"),
     ClassInterface(ClassInterfaceType.None)]
    public class ManagedIStream : IStream, IDisposable
    {
        public ManagedIStream(Stream stream)
        {
            if (stream == null) throw new ArgumentNullException("stream parameter cannot be null");
            _stream = stream;
        }
        [SecurityCritical]
        void IStream.Read(Byte[] buffer, Int32 bufferSize, IntPtr bytesReadPtr)
        {
            Int32 bytesRead = _stream.Read(buffer, 0, (int)bufferSize);
            if (bytesReadPtr != IntPtr.Zero)
            {
                Marshal.WriteInt32(bytesReadPtr, bytesRead);
            }
        }
        [SecurityCritical]
        void IStream.Seek(Int64 offset, Int32 origin, IntPtr newPositionPtr)
        {
            SeekOrigin seekOrigin;
            switch (origin)
            {
                case StreamConsts.STREAM_SEEK_SET:
                    seekOrigin = SeekOrigin.Begin;
                    break;
                case StreamConsts.STREAM_SEEK_CUR:
                    seekOrigin = SeekOrigin.Current;
                    break;
                case StreamConsts.STREAM_SEEK_END:
                    seekOrigin = SeekOrigin.End;
                    break;
                default:
                    throw new ArgumentOutOfRangeException("origin parameter can only be STREAM_SEEK_SET / STREAM_SEEK_CUR / STREAM_SEEK_END");
            }
            long position = _stream.Seek(offset, seekOrigin);
            if (newPositionPtr != IntPtr.Zero)
            {
                Marshal.WriteInt64(newPositionPtr, position);
            }
        }
        void IStream.SetSize(Int64 libNewSize)
        {
            _stream.SetLength(libNewSize);
        }
        void IStream.Stat(out System.Runtime.InteropServices.ComTypes.STATSTG streamStats, int grfStatFlag)
        {
            streamStats = new System.Runtime.InteropServices.ComTypes.STATSTG();
            streamStats.type = (int)STGTY.STGTY_STREAM;
            streamStats.cbSize = _stream.Length;
            streamStats.grfMode = 0; // default value 
            if (_stream.CanRead && _stream.CanWrite)
            {
                streamStats.grfMode |= (int)STGM.READWRITE;
            }
            else if (_stream.CanRead)
            {
                streamStats.grfMode |= (int)STGM.READ;
            }
            else if (_stream.CanWrite)
            {
                streamStats.grfMode |= (int)STGM.WRITE;
            }
            else
            {
                // A stream that is neither readable nor writable is a closed stream.
                throw new IOException("Cannot access a closed stream");
            }
        }
        [SecurityCritical]
        void IStream.Write(Byte[] buffer, Int32 bufferSize, IntPtr bytesWrittenPtr)
        {
            _stream.Write(buffer, 0, bufferSize);
            if (bytesWrittenPtr != IntPtr.Zero)
            {
                // If fewer than bufferSize bytes had been written, an exception would
                // have been thrown, so it can be assumed we wrote bufferSize bytes.
                Marshal.WriteInt32(bytesWrittenPtr, bufferSize);
            }
        }
        public void Dispose()
        {
            _stream.Dispose();
            _stream = null;
        }
        #region Unimplemented methods
        void IStream.Clone(out IStream streamCopy)
        {
            streamCopy = null;
            throw new NotSupportedException();
        }
        void IStream.CopyTo(IStream targetStream, Int64 bufferSize, IntPtr buffer, IntPtr bytesWrittenPtr)
        {
            throw new NotSupportedException();
        }
        void IStream.Commit(Int32 flags)
        {
            //do nothing
        }
        void IStream.LockRegion(Int64 offset, Int64 byteCount, Int32 lockType)
        {
            throw new NotSupportedException();
        }
        void IStream.Revert()
        {
            throw new NotSupportedException();
        }
        void IStream.UnlockRegion(Int64 offset, Int64 byteCount, Int32 lockType)
        {
            throw new NotSupportedException();
        }
        
        #endregion Unimplemented methods
        #region Fields
        protected Stream _stream;
        #endregion Fields
    }
    #endregion ManagedIStream object
    #region FileIStream object
    /// 
    /// FileIStream wraps a file as an IStream object
    /// 
    public class FileIStream : ManagedIStream
    {
        public FileIStream(string FileName, bool CreateNew, bool Writable)
            : base(File.Open(FileName, CreateNew ? FileMode.Create : FileMode.Open, Writable ? FileAccess.ReadWrite : FileAccess.Read))
        {
            _stream.Position = 0;
        }
    }
    #endregion FileIStream object
    #region constants
    public sealed class StreamConsts
    {
        public const int LOCK_WRITE = 0x1;
        public const int LOCK_EXCLUSIVE = 0x2;
        public const int LOCK_ONLYONCE = 0x4;
        public const int STATFLAG_DEFAULT = 0x0;
        public const int STATFLAG_NONAME = 0x1;
        public const int STATFLAG_NOOPEN = 0x2;
        public const int STGC_DEFAULT = 0x0;
        public const int STGC_OVERWRITE = 0x1;
        public const int STGC_ONLYIFCURRENT = 0x2;
        public const int STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE = 0x4;
        public const int STREAM_SEEK_SET = 0x0;
        public const int STREAM_SEEK_CUR = 0x1;
        public const int STREAM_SEEK_END = 0x2;
    }
    [Flags]
    public enum STGM : int
    {
        DIRECT = 0x00000000,
        TRANSACTED = 0x00010000,
        SIMPLE = 0x08000000,
        READ = 0x00000000,
        WRITE = 0x00000001,
        READWRITE = 0x00000002,
        SHARE_DENY_NONE = 0x00000040,
        SHARE_DENY_READ = 0x00000030,
        SHARE_DENY_WRITE = 0x00000020,
        SHARE_EXCLUSIVE = 0x00000010,
        PRIORITY = 0x00040000,
        DELETEONRELEASE = 0x04000000,
        NOSCRATCH = 0x00100000,
        CREATE = 0x00001000,
        CONVERT = 0x00020000,
        FAILIFTHERE = 0x00000000,
        NOSNAPSHOT = 0x00200000,
        DIRECT_SWMR = 0x00400000,
    }
    public enum STATFLAG : uint
    {
        STATFLAG_DEFAULT = 0,
        STATFLAG_NONAME = 1,
        STATFLAG_NOOPEN = 2
    }
    public enum STGTY : int
    {
        STGTY_STORAGE = 1,
        STGTY_STREAM = 2,
        STGTY_LOCKBYTES = 3,
        STGTY_PROPERTY = 4
    }
    #endregion constants
}