using System;
using System.Web;
using System.Collections.Generic;
using System.Text;
using Lucene.Net.Index;
using Lucene.Net.Documents;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Search;
using Lucene.Net.QueryParsers;
using Lucene.Net.Analysis;

namespace JumboTCMS.Utils.LuceneHelp
{
    /// <summary>
    /// 搜索内容
    /// </summary>
    public class SearchItem
    {
        public string TableName
        {
            get;
            set;
        }
        /// <summary>
        /// 编号
        /// </summary>
        public string Id
        {
            get;
            set;
        }
        /// <summary>
        /// 频道ID
        /// </summary>
        public string ChannelId
        {
            get;
            set;
        }
        /// <summary>
        /// 栏目ID
        /// </summary>
        public string ClassId
        {
            get;
            set;
        }
        /// <summary>
        /// 栏目名称
        /// </summary>
        public string ClassName
        {
            get;
            set;
        }
        /// <summary>
        /// 标题
        /// </summary>
        public string Title
        {
            get;
            set;
        }
        /// <summary>
        /// 链接地址
        /// </summary>
        public string Url
        {
            get;
            set;
        }
        /// <summary>
        /// 缩略图
        /// </summary>
        public string Img
        {
            get;
            set;
        }
        /// <summary>
        /// 简介
        /// </summary>
        public string Summary
        {
            get;
            set;
        }
        /// <summary>
        /// 正文
        /// </summary>
        public string Content
        {
            get;
            set;
        }
        /// <summary>
        /// 标签
        /// </summary>
        public string Tags
        {
            get;
            set;
        }
        /// <summary>
        /// 更新日期
        /// </summary>
        public string AddDate
        {
            get;
            set;
        }
        /// <summary>
        /// 更新年份
        /// </summary>
        public string Year
        {
            get;
            set;
        }
    }
    public class SearchIndex
    {
        /// <summary>
        /// 获得总数
        /// </summary>
        /// <param name="type"></param>
        /// <param name="channelid"></param>
        /// <param name="classid"></param>
        /// <param name="year"></param>
        /// <param name="keywords"></param>
        /// <returns></returns>
        public static int GetCount(string type, string channelid, string classid, string year, String keywords)
        {
            if (keywords.Length == 0)
            {
                keywords = "jUmBoT";
            }
            DateTime start = DateTime.Now;
            string[] types = type.Split(',');
            int _type_num = types.Length;
            IndexSearcher[] searchers = new IndexSearcher[_type_num];
            for (int i = 0; i < _type_num; i++)
            {
                searchers[i] = new IndexSearcher(HttpContext.Current.Server.MapPath("~/_data/index/" + types[i] + "/"));
            }
            MultiSearcher search = new MultiSearcher(searchers);

            BooleanQuery bq = new BooleanQuery();
            if (channelid != "0")
            {
                Term Term_channel = new Term("channelid", channelid);
                var termQuery1 = new TermQuery(Term_channel);
                bq.Add(termQuery1, BooleanClause.Occur.MUST);//添加到条件
            }
            if (JumboTCMS.Utils.Validator.StrToInt(year, 0) > 1900)
            {
                Term Term_year = new Term("year", year);
                var termQuery2 = new TermQuery(Term_year);
                bq.Add(termQuery2, BooleanClause.Occur.MUST);//添加到条件
            }
            string[] fields = { "title", "tags", "summary", "content", "fornull" };
            MultiFieldQueryParser parser = new MultiFieldQueryParser(fields, new StandardAnalyzer());//要查询的字段
            Query query = parser.Parse(keywords);
            bq.Add(query, BooleanClause.Occur.MUST);//添加到条件
            Hits hits = search.Search(bq);
            return hits.Length();
        }
        /// <summary>
        /// 分布检索
        /// </summary>
        /// <param name="type">多个用,隔开</param>
        /// <param name="channelid"></param>
        /// <param name="keywords">已经使用分词工具处理过</param>
        /// <param name="pageLen"></param>
        /// <param name="pageNo"></param>
        /// <param name="recCount"></param>
        /// <param name="eventTime"></param>
        /// <returns></returns>
        public static List<SearchItem> Search(string type, string channelid, string classid, string year, String keywords, int pageLen, int pageNo, out int recCount, out double eventTime, ref Dictionary<string, int> channelAggregate, ref Dictionary<string, int> yearAggregate)
        {
            if (keywords.Length == 0)
            {
                keywords = "jUmBoT";
            }
            DateTime start = DateTime.Now;
            string[] types = type.Split(',');
            int _type_num = types.Length;
            IndexSearcher[] searchers = new IndexSearcher[_type_num];
            for (int i = 0; i < _type_num; i++)
            {
                searchers[i] = new IndexSearcher(HttpContext.Current.Server.MapPath("~/_data/index/" + types[i] + "/"));
            }
            MultiSearcher search = new MultiSearcher(searchers);

            BooleanQuery bq0 = new BooleanQuery();//不分组的
            BooleanQuery bq1 = new BooleanQuery();//按频道分组
            BooleanQuery bq2 = new BooleanQuery();//按年份分组
            if (channelid != "0")
            {
                Term Term_channel = new Term("channelid", channelid);
                var termQuery1 = new TermQuery(Term_channel);
                bq0.Add(termQuery1, BooleanClause.Occur.MUST);//添加到条件
                bq2.Add(termQuery1, BooleanClause.Occur.MUST);//添加到条件
            }
            if (JumboTCMS.Utils.Validator.StrToInt(year, 0) > 1900)
            {
                Term Term_year = new Term("year", year);
                var termQuery2 = new TermQuery(Term_year);
                bq0.Add(termQuery2, BooleanClause.Occur.MUST);//添加到条件
                //bq1.Add(termQuery2, BooleanClause.Occur.MUST);//添加到条件
            }
            string[] fields = { "title", "tags", "summary", "content", "fornull" };
            MultiFieldQueryParser parser = new MultiFieldQueryParser(fields, new StandardAnalyzer());//要查询的字段
            Query query = parser.Parse(keywords);
            bq0.Add(query, BooleanClause.Occur.MUST);//添加到条件
            bq1.Add(query, BooleanClause.Occur.MUST);//添加到条件
            bq2.Add(query, BooleanClause.Occur.MUST);//添加到条件

            Sort sort = new Sort(new SortField("adddate", SortField.DOC, false)); //排序
            Hits hits = search.Search(bq0, sort);
            List<SearchItem> result = new List<SearchItem>();
            recCount = hits.Length();
            //for (int i = 0; i < _type_num; i++)
            if (_type_num == 1)
            {
                SimpleFacets.Facet(bq1, searchers[0], "channelid", channelAggregate);
                SimpleFacets.Facet(bq2, searchers[0], "year", yearAggregate);
            }
            if (recCount > 0)
            {
                int i = (pageNo - 1) * pageLen;
                while (i < recCount && result.Count < pageLen)
                {
                    SearchItem news = null;
                    try
                    {
                        news = new SearchItem();
                        news.Id = hits.Doc(i).Get("id");
                        news.ChannelId = hits.Doc(i).Get("channelid");
                        news.ClassId = hits.Doc(i).Get("classid");
                        news.TableName = hits.Doc(i).Get("tablename");
                        news.Img = hits.Doc(i).Get("img");
                        news.Title = hits.Doc(i).Get("title");
                        news.Summary = hits.Doc(i).Get("summary");
                        news.Tags = hits.Doc(i).Get("tags");
                        news.Url = hits.Doc(i).Get("url");
                        news.AddDate = hits.Doc(i).Get("adddate");
                        news.Year = hits.Doc(i).Get("year");
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);
                    }
                    finally
                    {
                        result.Add(news);
                        i++;
                    }
                }
                search.Close();
                TimeSpan duration = DateTime.Now - start;
                eventTime = duration.TotalMilliseconds;
                return result;
            }
            else
            {
                TimeSpan duration = DateTime.Now - start;
                eventTime = duration.TotalMilliseconds;
                return null;
            }
        }
    }
    /// <summary>
    /// 简单分组
    /// </summary>
    public class SimpleFacets
    {
        public static void Facet(BooleanQuery bq, IndexSearcher s, string field, Dictionary<string, int> dics)
        {
            StringIndex stringIndex = FieldCache_Fields.DEFAULT.GetStringIndex(s.GetIndexReader(), field);

            int[] c = new int[stringIndex.lookup.Length];

            FacetCollector results = new FacetCollector(c, stringIndex);

            s.Search(bq, results);

            DictionaryEntryQueue queue = new DictionaryEntryQueue(stringIndex.lookup.Length);

            for (int i = 1; i < stringIndex.lookup.Length; i++)
            {
                if (c[i] > 0 && stringIndex.lookup[i] != null && stringIndex.lookup[i] != "0")
                {
                    queue.Insert(new FacetEntry(stringIndex.lookup[i], -c[i]));
                }
            }
            int dictionaryEntrySize = queue.Size();
            for (int j = dictionaryEntrySize - 1; j >= 0; j--)
            {
                FacetEntry entry = queue.Pop() as FacetEntry;
                dics.Add(entry.Value, -entry.Count);
            }
        }
        public static void Facet(Query query, IndexSearcher s, string field, Dictionary<string, int> dics)
        {
            StringIndex stringIndex = FieldCache_Fields.DEFAULT.GetStringIndex(s.GetIndexReader(), field);

            int[] c = new int[stringIndex.lookup.Length];

            FacetCollector results = new FacetCollector(c, stringIndex);

            s.Search(query, results);

            DictionaryEntryQueue queue = new DictionaryEntryQueue(stringIndex.lookup.Length);

            for (int i = 1; i < stringIndex.lookup.Length; i++)
            {
                if (c[i] > 0 && stringIndex.lookup[i] != null && stringIndex.lookup[i] != "0")
                {
                    queue.Insert(new FacetEntry(stringIndex.lookup[i], -c[i]));
                }
            }
            int dictionaryEntrySize = queue.Size();
            for (int j = dictionaryEntrySize - 1; j >= 0; j--)
            {
                FacetEntry entry = queue.Pop() as FacetEntry;
                dics.Add(entry.Value, -entry.Count);
            }
        }
        /// <summary>Helper class for order the resulting array in value order 
        /// </summary> 
        private sealed class DictionaryEntryQueue : Lucene.Net.Util.PriorityQueue
        {
            internal DictionaryEntryQueue(int size)
                : base()
            {
                Initialize(size);
            }

            public override bool LessThan(object a, object b)
            {
                FacetEntry de1 = (FacetEntry)a;
                FacetEntry de2 = (FacetEntry)b;
                return (int)de1.Count < (int)de2.Count;
            }
        }

        /// <summary>class for work in the priority queue and avoid some boxing. 
        /// </summary> 
        private class FacetEntry
        {
            // Fields 
            private int count;
            private string value;

            // Methods 
            public FacetEntry(string v, int c)
            {
                this.value = v;
                this.count = c;
            }

            public int Count
            {
                get { return count; }
                set { count = value; }
            }

            public string Value
            {
                get { return this.value; }
                set { this.value = value; }
            }

        }

        /// <summary>collector that count the hits for every token 
        /// </summary> 
        private class FacetCollector : HitCollector
        {
            // Fields 
            private int[] counter;
            private StringIndex si;

            // Methods 
            public FacetCollector(int[] c, StringIndex s)
            {
                this.counter = c;
                this.si = s;
            }

            public override void Collect(int doc, float score)
            {
                this.counter[this.si.order[doc]]++;
            }
        }
    }
}
