// LocalThreadListStorage.cs

namespace Twin.IO
{
	using System;
	using System.IO;
	using System.Text;
	using System.Collections.Generic;
	using Twin.Text;
	using Twin.Bbs;

	/// <summary>
	/// [JfBXNÉXbhêðÛ¶·é@\ðñ
	/// </summary>
	public class LocalThreadListStorage : ThreadListStorage
	{
		private Cache cache;
		private Stream baseStream;
		private ThreadListParser dataParser;
		private ThreadListFormatter formatter;
		private Encoding encoding;
		private StorageMode mode;
		private BoardInfo boardInfo;

		private bool isOpen;
		private int position;
		private int index;

		/// <summary>
		/// Xg[ðJ¢Ä¢é©Ç¤©ð»f
		/// </summary>
		public override bool IsOpen {
			get { return isOpen; }
		}

		/// <summary>
		/// LbVf[^Ì·³ðæ¾
		/// </summary>
		public override int Length {
			get {
				if (isOpen) {
					return (int)baseStream.Length;
				}
				throw new InvalidOperationException("Xg[ªJ©êÄ¢Ü¹ñ");
			}
		}

		/// <summary>
		/// »ÝÌXg[ÌÊuðæ¾
		/// </summary>
		public override int Position {
			get {
				if (isOpen) {
					return position;
				}
				throw new InvalidOperationException("Xg[ªJ©êÄ¢Ü¹ñ");
			}
		}

		/// <summary>
		/// ÇÝÝ[hÅJ©êÄ¢é©Ç¤©ð\·
		/// </summary>
		public override bool CanRead {
			get { return (mode == StorageMode.Read); }
		}

		/// <summary>
		/// «Ý[hÅJ©êÄ¢é©Ç¤©ð\·
		/// </summary>
		public override bool CanWrite {
			get { return (mode == StorageMode.Write); }
		}

		/// <summary>
		/// LocalThreadListStorageNXÌCX^Xðú»
		/// </summary>
		/// <param name="cache">îÉÈéLbVîñ</param>
		/// <param name="parser">f[^ððÍ·éÆ«Égp·ép[T</param>
		/// <param name="formatter">«ÝÉgp·étH[}b^</param>
		/// <param name="enc">«ÝÉgp·éGR[_</param>
		public LocalThreadListStorage(Cache cache, ThreadListFormatter formatter, Encoding enc)
		{
			if (cache == null) {
				throw new ArgumentNullException("cache");
			}
			if (formatter == null) {
				throw new ArgumentNullException("formatter");
			}
			if (enc == null) {
				throw new ArgumentNullException("enc");
			}

			this.cache = cache;
			this.formatter = formatter;
			this.encoding = enc;
			this.isOpen = false;
		}

		/// <summary>
		/// LocalThreadListStorageNXÌCX^Xðú»
		/// </summary>
		/// <param name="cache"></param>
		public LocalThreadListStorage(Cache cache)
			: this(cache, new X2chThreadListFormatter(), Encoding.GetEncoding("Shift_Jis"))
		{
		}

		/// <summary>
		/// Xg[ðJ­
		/// </summary>
		/// <param name="board">«ÞÂÌwb_îñ</param>
		/// <param name="modeRead">Xg[WðJ­û@</param>
		public override bool Open(BoardInfo board, StorageMode mode)
		{
			if (board == null) {
				throw new ArgumentNullException("board");
			}
			if (isOpen) {
				throw new InvalidCastException("ùÉXg[ªJ©êÄ¢Ü·");
			}

			// «Ýæt@C¼
			string filePath = Path.Combine(cache.GetFolderPath(board), "subject.txt");

			// Xg[ðJ­
			if (mode == StorageMode.Read) {
				baseStream = StreamCreator.CreateReader(filePath, false);
			} else {
				baseStream = StreamCreator.CreateWriter(filePath, false, false);
			}

			// p[Tðú»
			dataParser = new X2chThreadListParser(board.Bbs, encoding);

			this.index = 1;
			this.position = 0;
			this.mode = mode;
			this.isOpen = true;
			this.boardInfo = board;

			return true;
		}

		/// <summary>
		/// fBXNÉLbVµÈªçXbhêðÇÝÞ
		/// </summary>
		/// <param name="headers"></param>
		/// <returns></returns>
		public override int Read(List<ThreadHeader> headers)
		{
			int byteParsed;
			return Read(headers, out byteParsed);
		}

		/// <summary>
		/// XbhîñðÇÝÞ
		/// </summary>
		/// <param name="headerCollects"></param>
		/// <returns></returns>
		public override int Read(List<ThreadHeader> headerCollects, out int byteParsed)
		{
			if (headerCollects == null) {
				throw new ArgumentNullException("headerCollects");
			}
			if (!isOpen) {
				throw new InvalidOperationException("Xg[ªJ©êÄ¢Ü¹ñ");
			}

			// obt@Éf[^ðÇÝÞ
			int readCount = baseStream.Read(buffer, 0, buffer.Length);

			// ðÍµÄRNVÉi[
			Array array = dataParser.Parse(buffer, readCount, out byteParsed);

			foreach (ThreadHeader header in array)
			{
				header.No = index++;
				header.BoardInfo = boardInfo;
				headerCollects.Add(header);
			}

			// ÀÛÉÇÝÜê½oCgðvZ
			position += readCount;

			return readCount;
		}

		/// <summary>
		/// resCollectionðt@CÉ«Þ
		/// </summary>
		/// <param name="headerCollects">XbhÌàeªi[³ê½List<ThreadHeader>NX</param>
		/// <returns>ÀÛÉ«Üê½oCg</returns>
		public override int Write(List<ThreadHeader> headerCollects)
		{
			if (!CanWrite) {
				throw new NotSupportedException("«ÝpÉJ©êÄ¢Ü¹ñ");
			}
			if (headerCollects == null) {
				throw new ArgumentNullException("headerCollects");
			}
			if (!isOpen) {
				throw new InvalidOperationException("Xg[ªJ©êÄ¢Ü¹ñ");
			}

			string textData = formatter.Format(headerCollects);
			byte[] byteData = encoding.GetBytes(textData);

			baseStream.Write(byteData, 0, byteData.Length);
			position += byteData.Length;

			return byteData.Length;
		}

		/// <summary>
		/// Xg[ðÂ¶é
		/// </summary>
		public override void Close()
		{
			if (baseStream != null)
				baseStream.Close();

			dataParser = null;
			baseStream = null;
			boardInfo = null;
			buffer = null;
			position = 0;
			isOpen = false;
		}

		/// <summary>
		/// gpµÄ¢é\[Xððú
		/// </summary>
		public override void Dispose()
		{
			Close();
		}
	}
}
