using System;
using ChaKi.Entity.Corpora;
using ChaKi.Entity.Corpora.Annotations;
using NHibernate;
using NHibernate.Mapping;
using System.Collections.Generic;
using System.Text;
using ChaKi.Service.Readers;
using System.IO;

namespace ChaKi.Service.DependencyEdit
{
    internal class OperationUpdateAllBunsetsu : Operation
    {
        private Sentence m_Sentence;
        private string m_InputString;
        private CabochaBunsetsuList m_BunsetsuListOld;
        private CabochaBunsetsuList m_BunsetsuListNew;

        public OperationUpdateAllBunsetsu(Sentence sen, string inputString)
        {
            m_Sentence = sen;

            m_BunsetsuListOld = null;

            // inputStringを元に、編集後の形に対応するCabochaBunsetsuListを生成・保存
            m_InputString = inputString;
            using (var rdr = new StringReader(inputString))
            {
                m_BunsetsuListNew = CabochaReader.ToBunsetsuList(sen, rdr);
            }
            if (m_BunsetsuListNew == null)
            {
                throw new Exception("OperationUpdateAllBunsetsu: Error while parsing Cabocha output string.");
            }
        }

        public override void Execute(DepEditContext ctx)
        {
            if (m_BunsetsuListOld == null)
            {
                // 現在のBunsetsu Segment/LinkからCabochaBunsetsuListを生成してセーブする.
                m_BunsetsuListOld = CabochaBunsetsuList.CreateFromSentence(m_Sentence, ctx.Session);
            }
            // 現在の文節Segment/係り受けLinkをすべて削除する.
            RemoveBunsetsuSegmentLink(ctx);

            // m_BunsetsuListNewに基づいて新しい文節Segment/係り受けLinkを生成する.
            ApplyBunsetsuList(ctx, m_Sentence, m_BunsetsuListNew);

            ctx.Flush();
        }

        public override void UnExecute(DepEditContext ctx)
        {
            // 現在の文節Segment/係り受けLinkをすべて削除する.
            RemoveBunsetsuSegmentLink(ctx);

            // m_BunsetsuListOldに基づいて新しい文節Segment/係り受けLinkを生成する.
            ApplyBunsetsuList(ctx, m_Sentence, m_BunsetsuListOld);

            ctx.Flush();
        }

        // m_Sentenceに付加されているBunsetsu Segment/Linkをすべて削除する.
        private void RemoveBunsetsuSegmentLink(DepEditContext ctx)
        {
            ctx.Session.Delete(string.Format("from Link where FromSentence.ID={0} or ToSentence.ID={0}", m_Sentence.ID));
            ctx.Session.Delete(string.Format("from Segment where Tag.Name='Bunsetsu' and Sentence.ID={0}", m_Sentence.ID));
            IQuery q = ctx.Session.CreateQuery(string.Format("from Word where Sen.ID={0} order by StartChar", m_Sentence.ID));
            var words = q.List<Word>();
            foreach (var w in words)
            {
                w.Bunsetsu = null;
            }
        }

        // bunsetsuListに基づいて新たにSegment/Linkを生成し、WordにBunsetsuを設定する
        private void ApplyBunsetsuList(DepEditContext ctx, Sentence sen, CabochaBunsetsuList bunsetsuList)
        {
            var bunsetsuTag = FindBunsetsuTag(ctx);
            var segments = new Dictionary<int, Segment>();

            int sentenceStartChar = sen.StartChar;
            foreach (var b in bunsetsuList.Values)
            {
                // 新しいSegment
                Segment seg = new Segment();
                seg.StartChar = sentenceStartChar + b.StartPos;
                seg.EndChar = sentenceStartChar + b.EndPos;
                seg.Tag = bunsetsuTag;
                seg.Doc = sen.ParentDoc;
                seg.Proj = ctx.Proj;
                seg.User = ctx.User;
                seg.Version = ctx.TSVersion;
                seg.Sentence = sen;
                // segをDBに保存
                ctx.Save(seg);
                foreach (var w in b.Words)
                {
                    w.Bunsetsu = seg;
                }
                segments.Add(b.BunsetsuPos, seg);
            }

            // SegmentがそろったのでLinkを生成する
            foreach (var b in bunsetsuList.Values)
            {
                Link newlink = new Link();
                if (b.DependsTo < 0)
                {
                    continue; 
                }
                newlink.From = segments[b.BunsetsuPos];
                newlink.To = segments[b.DependsTo];
                newlink.Tag = FindLinkTag(b.DependsAs, ctx);
                newlink.FromSentence = sen;
                newlink.ToSentence = sen;
                newlink.Proj = ctx.Proj;
                newlink.User = ctx.User;
                newlink.Version = ctx.TSVersion;
                ctx.Save(newlink);
            }
            ctx.Flush();
        }

        public override string ToIronRubyStatement(DepEditContext ctx)
        {
            return string.Format("#svc.OperationUpdateAllBunsetsu : not supported.");
        }

        public override string ToString()
        {
            return string.Format("{{OperationUpdateAllBunsetsu \"{0}\"}}\n", m_InputString);
        }
    }
}
