text: ﻿using System;
  using EventStore.Common.Utils;
  using EventStore.Core.TransactionLog.Checkpoint;
  using EventStore.Core.TransactionLog.LogRecords;
  
  namespace EventStore.Core.TransactionLog.Chunks {
  	public class TFChunkWriter : ITransactionFileWriter {
  		public ICheckpoint Checkpoint {
  			get { return _writerCheckpoint; }
  		}
  
  		public TFChunk.TFChunk CurrentChunk {
  			get { return _currentChunk; }
  		}
  
  		private readonly TFChunkDb _db;
  		private readonly ICheckpoint _writerCheckpoint;
  
  		private TFChunk.TFChunk _currentChunk;
  
  		public TFChunkWriter(TFChunkDb db) {
  			Ensure.NotNull(db, "db");
  
  			_db = db;
  			_writerCheckpoint = db.Config.WriterCheckpoint;
  			_currentChunk = db.Manager.GetChunkFor(_writerCheckpoint.Read());
  			if (_currentChunk == null)
  				throw new InvalidOperationException("No chunk given for existing position.");
  		}
  
  		public void Open() {
  			// DO NOTHING
  		}
  
  		public bool Write(LogRecord record, out long newPos) {
  			var result = _currentChunk.TryAppend(record);
  			if (result.Success)
  				_writerCheckpoint.Write(result.NewPosition + _currentChunk.ChunkHeader.ChunkStartPosition);
  			else
  				CompleteChunk(); // complete updates checkpoint internally
  			newPos = _writerCheckpoint.ReadNonFlushed();
  			return result.Success;
  		}
  
  		public void CompleteChunk() {
  			var chunk = _currentChunk;
  			_currentChunk = null; // in case creation of new chunk fails, we shouldn't use completed chunk for write
  
  			chunk.Complete();
  
  			_writerCheckpoint.Write(chunk.ChunkHeader.ChunkEndPosition);
  			_writerCheckpoint.Flush();
  
  			_currentChunk = _db.Manager.AddNewChunk();
  		}
  
  		public void CompleteReplicatedRawChunk(TFChunk.TFChunk rawChunk) {
  			_currentChunk = null; // in case creation of new chunk fails, we shouldn't use completed chunk for write
  
  			rawChunk.CompleteRaw();
  			_db.Manager.SwitchChunk(rawChunk, verifyHash: true, removeChunksWithGreaterNumbers: true);
  
  			_writerCheckpoint.Write(rawChunk.ChunkHeader.ChunkEndPosition);
  			_writerCheckpoint.Flush();
  
  			_currentChunk = _db.Manager.AddNewChunk();
  		}
  
  		public void Dispose() {
  			Close();
  		}
  
  		public void Close() {
  			Flush();
  		}
  
  		public void Flush() {
  			if (_currentChunk == null) // the last chunk allocation failed
  				return;
  			_currentChunk.Flush();
  			_writerCheckpoint.Flush();
  		}
  	}
  }
  