//  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 Tokenizer tokenizer = new Tokenizer();
        private Connector connector = new Connector();
        private Viterbi viterbi = new Viterbi();
        private Writer writer = new Writer();
        private NBestGenerator nBest = new NBestGenerator();

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

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

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

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

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

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

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

            if (param.OutputFormatType == "dump")
            {
                this.LatticeLevel = MeCabLatticeLevel.Two;
                this.AllMorphs = true;
            }
        }

        /// <summary>
        /// 解析を行う
        /// </summary>
        /// <param name="str">解析対象の文字列</param>
        /// <returns>解析結果の文字列</returns>
        public unsafe string Parse(string str)
        {
            if (str == null) throw new ArgumentNullException("str");

            fixed (char* pStr = str)
                return this.Parse(pStr, str.Length);
        }

        /// <summary>
        /// 解析を行う
        /// </summary>
        /// <param name="str">解析対象の文字列へのポインタ</param>
        /// <param name="len">解析対象の文字列の長さ</param>
        /// <returns>解析結果の文字列</returns>
        public unsafe string Parse(char* str, int len)
        {
            MeCabNode n = this.ParseToNode(str, len);
            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 unsafe MeCabNode ParseToNode(string str)
        {
            if (str == null) throw new ArgumentNullException("str");

            fixed (char* pStr = str)
                return this.ParseToNode(pStr, str.Length);
        }

        /// <summary>
        /// 解析を行う
        /// </summary>
        /// <param name="str">解析対象の文字列へのポインタ</param>
        /// <param name="len">解析対象の文字列の長さ</param>
        /// <returns>文頭の形態素</returns>
        public unsafe MeCabNode ParseToNode(char* str, int len)
        {
            return this.viterbi.Analyze(str, len);
        }

        /// <summary>
        /// 解析結果を確からしいものから順番に取得する場合の初期化を行う
        /// </summary>
        /// <param name="str">解析対象の文字列</param>
        public unsafe void ParseNBestInit(string str)
        {
            if (str == null) throw new ArgumentNullException("str");

            fixed (char* pStr = str)
                this.ParseNBestInit(pStr, str.Length);
        }

        /// <summary>
        /// 解析結果を確からしいものから順番に取得する場合の初期化を行う
        /// </summary>
        /// <param name="str">解析対象の文字列へのポインタ</param>
        /// <param name="len">解析対象の文字列の長さ</param>
        public unsafe void ParseNBestInit(char* str, int len)
        {
            if (this.LatticeLevel == 0)
                throw new InvalidOperationException("Please set one or more to LatticeLevel.");

            MeCabNode n = this.ParseToNode(str, len);
            this.nBest.Set(n);
        }

        /// <summary>
        /// 解析結果を確からしいものから順番に取得する
        /// </summary>
        /// <returns>解析結果の形態素</returns>
        public MeCabNode NextNode()
        {
            return this.nBest.Next();
        }

        /// <summary>
        /// 解析結果を確からしいものから順番に取得する
        /// </summary>
        /// <returns>解析結果の文字列</returns>
        public string Next()
        {
            MeCabNode n = this.NextNode();
            if (n == null) return null;
            StringBuilder os = new StringBuilder();
            this.writer.Write(os, n);
            return os.ToString();
        }

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

            fixed (char* pStr = str)
                return this.ParseNBest(n, pStr, str.Length);
        }

        /// <summary>
        /// ParseのN-Best解出力version
        /// </summary>
        /// <param name="n">必要な解析結果の個数</param>
        /// <param name="str">解析対象の文字列へのポインタ</param>
        /// <param name="len">解析対象の文字列の長さ</param>
        /// <returns>解析結果の文字列</returns>
        public unsafe string ParseNBest(int n, char* str, int len)
        {
            if (n == 1) return this.Parse(str, len);

            this.ParseNBestInit(str, len);
            StringBuilder os = new StringBuilder();

            for (int i = 0; i < n; i++)
            {
                MeCabNode node = this.NextNode();
                if (node == null) break;
                this.writer.Write(os, node);
            }
            return os.ToString();
        }

        /// <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;
        }

        /// <summary>
        /// 使用されているリソースを開放する（予約）
        /// </summary>
        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {

            }
        }

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