//  MeCab -- Yet Another Part-of-Speech and Morphological Analyzer
//
//  Copyright(C) 2001-2006 Taku Kudo <taku@chasen.org>
//  Copyright(C) 2004-2006 Nippon Telegraph and Telephone Corporation
using System;
using System.Collections.Generic;
using System.Text;
using NMeCab.Core;

namespace NMeCab
{
	public class MeCabTagger : IDisposable
	{
		private readonly Viterbi viterbi = new Viterbi ();
		private readonly Writer writer = new Writer ();

        #region Mode

		/// <summary>
		/// 部分解析モード
		/// </summary>
		public bool Partial {
			get {
				this.ThrowIfDisposed ();
				return this.viterbi.Partial;
			}
			set {
				this.ThrowIfDisposed ();
				this.viterbi.Partial = value;
			}
		}

		/// <summary>
		/// ソフト分かち書きの温度パラメータ
		/// </summary>
		public float Theta {
			get {
				this.ThrowIfDisposed ();
				return this.viterbi.Theta;
			}
			set {
				this.ThrowIfDisposed ();
				this.viterbi.Theta = value;
			}
		}

		/// <summary>
		/// ラティスレベル(どの程度のラティス情報を解析時に構築するか)
		/// </summary>
		/// <value>
		/// 0: 最適解のみが出力可能なレベル (デフォルト, 高速) 
		/// 1: N-best 解が出力可能なレベル (中速) 
		/// 2: ソフトわかち書きが可能なレベル (低速) 
		/// </value>
		public MeCabLatticeLevel LatticeLevel {
			get {
				this.ThrowIfDisposed ();
				return this.viterbi.LatticeLevel;
			}
			set {
				this.ThrowIfDisposed ();
				this.viterbi.LatticeLevel = value;
			}
		}

		/// <summary>
		/// 全出力モード
		/// </summary>
		/// <value>
		/// true: 全出力
		/// false: ベスト解のみ
		/// </value>
		public bool AllMorphs {
			get {
				this.ThrowIfDisposed ();
				return this.viterbi.AllMorphs;
			}
			set {
				this.ThrowIfDisposed ();
				this.viterbi.AllMorphs = value;
			}
		}

		/// <summary>
		/// 解析結果のフォーマット
		/// </summary>
		public string OutPutFormatType {
			get {
				this.ThrowIfDisposed ();
				return this.writer.OutputFormatType;
			}
			set {
				this.ThrowIfDisposed ();
				this.writer.OutputFormatType = value;
			}
		}

        #endregion

        #region Constractor

		/// <summary>
		/// コンストラクタ
		/// </summary>
		private MeCabTagger ()
		{
		}

        #endregion

        #region Open/Create

		/// <summary>
		/// MeCabTaggerを開く
		/// </summary>
		/// <param name="param">初期化パラメーター</param>
		private void Open (MeCabParam param)
		{
			this.viterbi.Open (param);

			this.writer.Open (param);
		}

		/// <summary>
		/// MeCabTaggerのインスタンスを生成する
		/// </summary>
		/// <returns>MeCabTaggerのインスタンス</returns>
		public static MeCabTagger Create ()
		{
			MeCabParam param = new MeCabParam ();
			param.LoadDicRC ();
			return MeCabTagger.Create (param);
		}

		/// <summary>
		/// MeCabTaggerのインスタンスを生成する
		/// </summary>
		/// <param name="param">初期化パラメーター</param>
		/// <returns>MeCabTaggerのインスタンス</returns>
		public static MeCabTagger Create (MeCabParam param)
		{
			MeCabTagger tagger = new MeCabTagger ();
			tagger.Open (param);
			return tagger;
		}

        #endregion

        #region Parse

		/// <summary>
		/// 解析を行う
		/// </summary>
		/// <param name="str">解析対象の文字列</param>
		/// <returns>解析結果の文字列</returns>
		public string Parse (string str)
		{
			MeCabNode n = this.ParseToNode (str);
			if (n == null)
				return null;
			StringBuilder os = new StringBuilder ();
			this.writer.Write (os, n);
			return os.ToString ();
		}

		/// <summary>
		/// 解析を行う
		/// </summary>
		/// <param name="str">解析対象の文字列</param>
		/// <returns>文頭の形態素</returns>
		public MeCabNode ParseToNode (string str)
		{
			if (str == null)
				throw new ArgumentNullException ("str");

			this.ThrowIfDisposed ();
			
			return this.viterbi.Analyze (str);
		}

        #endregion

        #region NBest

		/// <summary>
		/// 解析を行い結果を確からしいものから順番に取得する
		/// </summary>
		/// <param name="str">解析対象の文字列</param>
		/// <returns>文頭の形態素を確からしい順に取得するための列挙子</returns>
		public IEnumerable<MeCabNode> ParseNBestToNode (string str)
		{
			if (this.LatticeLevel == 0)
				throw new InvalidOperationException ("Please set one or more to LatticeLevel.");

			MeCabNode n = this.ParseToNode (str);
			NBestGenerator nBest = new NBestGenerator ();
			nBest.Set (n);
			return nBest.GetEnumerator ();
		}

		/// <summary>
		/// ParseのN-Best解出力version
		/// </summary>
		/// <param name="n">必要な解析結果の個数</param>
		/// <param name="str">解析対象の文字列</param>
		/// <returns>解析結果の文字列</returns>
		public string ParseNBest (int n, string str)
		{
			if (n <= 0)
				throw new ArgumentOutOfRangeException ("n", "");

			if (n == 1)
				return this.Parse (str);

			StringBuilder os = new StringBuilder ();
			foreach (MeCabNode node in this.ParseNBestToNode(str)) {
				this.writer.Write (os, node);
				if (--n == 0)
					break;
			}
			return os.ToString ();
		}

        #endregion

        #region Dispose

		private bool disposed;

		/// <summary>
		/// 使用中のリソースを開放する
		/// </summary>
		public void Dispose ()
		{
			this.Dispose (true);
			GC.SuppressFinalize (this);
		}

		protected virtual void Dispose (bool disposing)
		{
			if (disposed)
				return;

			if (disposing) {
				this.viterbi.Dispose (); //Nullチェック不要
			}

			this.disposed = true;
		}

		~MeCabTagger ()
		{
			this.Dispose (false);
		}

		private void ThrowIfDisposed ()
		{
			Console.WriteLine ("ThrowIfDisposed");
			if (this.disposed)
				throw new ObjectDisposedException (this.GetType ().FullName);
		}

        #endregion
	}
}
