/***************************************************************************
 *                           SequentialFileWriter.cs
 *                            -------------------
 *   begin                : May 1, 2002
 *   copyright            : (C) The RunUO Software Team
 *   email                : info@runuo.com
 *
 *   $Id: SequentialFileWriter.cs 4 2006-06-15 04:28:39Z mark $
 *
 ***************************************************************************/

/***************************************************************************
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 ***************************************************************************/

using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace Server {
	public sealed class SequentialFileWriter : Stream {
		private FileStream fileStream;
		private FileQueue fileQueue;

		private AsyncCallback writeCallback;

		private SaveMetrics metrics;

		public SequentialFileWriter( string path, SaveMetrics metrics ) {
			if ( path == null ) {
				throw new ArgumentNullException( "path" );
			}

			this.metrics = metrics;

			this.fileStream = FileOperations.OpenSequentialStream( path, FileMode.Create, FileAccess.Write, FileShare.None );

			fileQueue = new FileQueue(
				Math.Max( 1, FileOperations.Concurrency ),
				FileCallback
			);
		}

		public override long Position {
			get {
				return fileQueue.Position;
			}
			set {
				throw new InvalidOperationException();
			}
		}

		private void FileCallback( FileQueue.Chunk chunk ) {
			if ( FileOperations.AreSynchronous ) {
				fileStream.Write( chunk.Buffer, chunk.Offset, chunk.Size );

				if ( metrics != null ) {
					metrics.OnFileWritten( chunk.Size );
				}

				chunk.Commit();
			} else {
				if ( writeCallback == null ) {
					writeCallback = this.OnWrite;
				}

				fileStream.BeginWrite( chunk.Buffer, chunk.Offset, chunk.Size, writeCallback, chunk );
			}
		}

		private void OnWrite( IAsyncResult asyncResult ) {
			FileQueue.Chunk chunk = asyncResult.AsyncState as FileQueue.Chunk;

			fileStream.EndWrite( asyncResult );

			if ( metrics != null ) {
				metrics.OnFileWritten( chunk.Size );
			}

			chunk.Commit();
		}

		public override void Write( byte[] buffer, int offset, int size ) {
			fileQueue.Enqueue( buffer, offset, size );
		}

		public override void Flush() {
			fileQueue.Flush();
			fileStream.Flush();
		}

		protected override void Dispose( bool disposing ) {
			if ( fileStream != null ) {
				Flush();

				fileQueue.Dispose();
				fileQueue = null;

				fileStream.Close();
				fileStream = null;
			}

			base.Dispose( disposing );
		}

		public override bool CanRead {
			get { return false; }
		}

		public override bool CanSeek {
			get { return false; }
		}

		public override bool CanWrite {
			get { return true; }
		}

		public override long Length {
			get { return this.Position; }
		}

		public override int Read( byte[] buffer, int offset, int count ) {
			throw new InvalidOperationException();
		}

		public override long Seek( long offset, SeekOrigin origin ) {
			throw new InvalidOperationException();
		}

		public override void SetLength( long value ) {
			fileStream.SetLength( value );
		}
	}
}