using System;
using System.Collections.Generic;
using System.Text;
using ChaKi.Entity.Corpora.Annotations;
using NHibernate;
using ChaKi.Entity.Corpora;

namespace ChaKi.Service.Common
{
    internal class Util
    {
        private static readonly List<Segment> EmptySegmentList = new List<Segment>();

        public static IList<Group> RetrieveWordGroups(ISession sess, Sentence sen)
        {
            // まずm_Sentenceに関連する同格・並列Segmentを検索
            IQuery q = sess.CreateQuery(
                string.Format("from Segment seg where seg.Doc.ID={0} and seg.Sentence.ID={1} and (seg.Tag.Name='{2}' or seg.Tag.Name='{3}')",
                    sen.ParentDoc.ID, sen.ID, "Apposition", "Parallel"));
            IList<Segment> segs = q.List<Segment>();
            if (segs.Count == 0)
            {
                return Group.EmptyList;
            }

            // 次にSQLクエリにより、group_memberテーブルから上で得られたSegmentに関係するGroupIDリストを得る.
            ISQLQuery sq = sess.CreateSQLQuery(
                string.Format("SELECT DISTINCT group_id FROM group_member WHERE member_id in {0} AND object_type='{1}' ORDER BY group_id ASC",
                BuildSegmentIDList(segs), Tag.SEGMENT))
                .AddScalar("group_id", NHibernateUtil.Int32);
            IList<int> groupids = sq.List<int>();

            // 最後にGroupIDからGroupオブジェクトを得る
            q = sess.CreateQuery(
                string.Format("from Group g where (g.Tag.Name='{0}' or g.Tag.Name='{1}') and g.ID in {2} order by g.ID",
                    "Apposition", "Parallel", BuildIDList(groupids)));
            return q.List<Group>();
        }

        /// <summary>
        /// 文節・同格・並列以外のSEGMENTリストを得る
        /// </summary>
        /// <param name="sess"></param>
        /// <param name="sen"></param>
        /// <returns></returns>
        public static IList<Segment> RetrieveMiscSegments(ISession sess, Sentence sen)
        {
            IQuery q = sess.CreateQuery(
                string.Format("from Segment seg where seg.Doc.ID={0} and seg.Sentence.ID={1} and (seg.Tag.Name!='{2}' and seg.Tag.Name!='{3}' and seg.Tag.Name!= '{4}')",
                    sen.ParentDoc.ID, sen.ID, "Apposition", "Parallel", "Bunsetsu"));
            return q.List<Segment>();
        }

        /// <summary>
        /// 文節へ係るもの以外のLINKリストを得る
        /// </summary>
        /// <param name="sess"></param>
        /// <param name="sen"></param>
        /// <returns></returns>
        public static IList<Link> RetrieveMiscLinks(ISession sess, Sentence sen)
        {
            IQuery q = sess.CreateQuery(
                string.Format("from Link lnk where lnk.FromSentence.ID={0} and lnk.From.Tag.Name != 'Bunsetsu' and lnk.To.Tag.Name != 'Bunsetsu'",
                    sen.ID));
            return q.List<Link>();
        }

        /// <summary>
        /// 与えられたLexemeのリストからLexeme IDを抜き出して
        /// INクエリ条件の右辺："(id1,id2,..)" 形式に変換する。
        /// </summary>
        /// <param name="lexemeList"></param>
        /// <returns></returns>
        public static string BuildLexemeIDList(IList<Lexeme> lexemeList)
        {
            if (lexemeList == null)
            {
                return string.Empty;    // 空の条件Boxにより全Lexemeにマッチした場合は""を返す。
            }
            StringBuilder sb = new StringBuilder("(");
            StringConnector connector = new StringConnector(",");
            foreach (Lexeme lex in lexemeList)
            {
                sb.Append(connector.Get());
                sb.Append(lex.ID);
            }
            sb.Append(")");
            return sb.ToString();
        }

        public static string BuildSentenceIDList(IList<Sentence> sentences)
        {
            StringBuilder sb = new StringBuilder("(");
            StringConnector connector = new StringConnector(",");
            foreach (Sentence sen in sentences)
            {
                sb.Append(connector.Get());
                sb.AppendFormat("{0}", sen.ID);
            }
            sb.Append(")");
            return sb.ToString();
        }

        public static string BuildSegmentIDList(IList<Segment> segments)
        {
            StringBuilder sb = new StringBuilder("(");
            StringConnector connector = new StringConnector(",");
            foreach (Segment seg in segments)
            {
                sb.Append(connector.Get());
                sb.Append(seg.ID);
            }
            sb.Append(")");
            return sb.ToString();
        }

        public static string BuildIDList(IList<int> ids)
        {
            StringBuilder sb = new StringBuilder("(");
            StringConnector connector = new StringConnector(",");
            foreach (int id in ids)
            {
                sb.Append(connector.Get());
                sb.Append(id);
            }
            sb.Append(")");
            return sb.ToString();
        }

        public static string BuildStringList(IList<string> strs)
        {
            if (strs == null || strs.Count == 0)
            {
                return null;
            }
            StringBuilder sb = new StringBuilder("(");
            StringConnector connector = new StringConnector(",");
            foreach (string s in strs)
            {
                sb.Append(connector.Get());
                sb.AppendFormat("'{0}'", s);
            }
            sb.Append(")");
            return sb.ToString();
        }

        public static string EscapeQuote(string s)
        {
            if (s == null)
            {
                return string.Empty;
            }
            else
            {
                return s.Replace("'", "''");
            }
        }

        public static long GetNextAvailableID(ISession session, string table)
        {
            long id;
            try
            {
                id = session.CreateSQLQuery(string.Format("SELECT min(id)+1 FROM {0} WHERE id+1 NOT IN (SELECT id FROM {0})", table))
                    .UniqueResult<long>();
            }
            catch (Exception ex)
            {
                return 0;
            }
            return id;
        }
    }
}
