using System;
using System.Collections.Generic;
using System.Linq;

namespace mxdemo.Mind
{
    #region models

    /// <summary>
    /// 选科组合
    /// </summary>
    public class SubjectGroup
    {
        public int Limitation { get; set; } // 人数限制
        public string Name { get; set; }
        public string Code { get; set; }
        public long Id { get; set; }
    }

    /// <summary>
    /// 志愿
    /// </summary>
    public class Preference
    {
        public int Rank { get; set; } // 第1、2、3志原 越小优先级越高
        public long GroupId {get;set;}
        public long StudentId { get; set; }
        public long Id { get; set; }
    }

    /// <summary>
    /// 得分情况
    /// </summary>
    public class GroupScore
    {
        public decimal Score { get; set; }
        public long StudentId { get; set; }
        public long GroupId { get; set; }
    }

    /// <summary>
    /// 学生信息
    /// </summary>
    public class Student
    {
        public long Id { get; set; }
        public string No { get; set; }
        public string Name { get; set; }
    }

    public class ElectiveElement
    {
        public Student Student { get; set; }
        public Preference Preference { get; set; }
        public SubjectGroup Group { get; set; }

        // 计算属性
        public int PreferenceRank { get; set; } // 志愿排名
        public int GroupRank { get; set; }  // 组合排名，即成绩排名（组合全校排名），同样越小优先级越高
    }

    #endregion

    public class ElectiveResult
    {
        public ElectiveResult(List<Student> students)
        {
            this.UnmatchedStudents = students;
        }

        public List<Student> MatchedStudents { get; set; }
            = new List<Student>();
        public Dictionary<SubjectGroup, List<ElectiveElement>> MatchedDetails { get; set; }
            = new Dictionary<SubjectGroup, List<ElectiveElement>>();
        public List<Student> UnmatchedStudents { get; set; }

        public void AppendMatched(ElectiveElement element)
        {
            // 应该还有更多细节需要考虑
            var appendStudent = element.Student;
            if (MatchedStudents.Contains(appendStudent)) return;

            if (UnmatchedStudents.Contains(appendStudent))
                RemoveUnmatched(appendStudent);

            var appendGroup = MatchedDetails.ContainsKey(element.Group)
                ? MatchedDetails[element.Group]
                : new List<ElectiveElement>();
            if (appendGroup.Count < element.Group.Limitation)
            {
                MatchedDetails[element.Group] = appendGroup;
                AddMatchedAndDetails(element);
            }
            else
            {
                AddUnmatched(appendStudent);
            }
        }

        private void AddMatchedAndDetails(ElectiveElement element)
        {
            // 应该还有更多细节需要考虑
            MatchedDetails[element.Group].Add(element);
            MatchedStudents.Add(element.Student);
        }

        private void AddUnmatched(Student student)
        {
            // 应该还有更多细节需要考虑
            UnmatchedStudents.Add(student);
        }

        private void RemoveUnmatched(Student student)
        {
            // 应该还有更多细节需要考虑
            UnmatchedStudents.Remove(student);
        }
    }

    public interface IElectiveStrategy
    {
        /// <summary>
        /// 从开放选科中取出按优先级分组的选科
        /// </summary>
        /// <returns></returns>
        List<List<SubjectGroup>> GetAllPrioritizedGroups();

        /// <summary>
        /// 参与这次运算的志愿优先级集合
        /// </summary>
        /// <returns></returns>
        List<List<int>> GetAllPrioritizedPreferenceRanks();

        /// <summary>
        /// 参与这次运算的所有学生
        /// </summary>
        /// <returns></returns>
        List<Student> GetAllStudents();
    }

    public abstract class ElectiveEngine: IComparer<ElectiveElement>
    { 
        protected IElectiveStrategy ElectiveStrategy { get; set; }


        protected ElectiveEngine(IElectiveStrategy electiveStrategy)
        {
            ElectiveStrategy = electiveStrategy;
        }

        public ElectiveResult Execute()
        {
            var prioritizedGroups = ElectiveStrategy.GetAllPrioritizedGroups();
            var prioritizedPreferenceRanks = ElectiveStrategy.GetAllPrioritizedPreferenceRanks();
            var allStudents = ElectiveStrategy.GetAllStudents();
            var electiveResult = new ElectiveResult(allStudents);

            foreach(var groups in prioritizedGroups)
            {
                foreach(var ranks in prioritizedPreferenceRanks)
                {
                    var students = electiveResult.UnmatchedStudents;
                    var elements = BuildElectiveElements(students, groups, ranks);
                    ExecuteCore(elements, electiveResult);
                }
            }
            return electiveResult;
        }

        private void ExecuteCore(List<ElectiveElement> elements, ElectiveResult context)
        {
            // 核心方法优先级排序, 已经按期望排好了录取顺序
            elements.Sort(this);  // this.IComparer<ElectiveElement>.Compare
            // 接下来考虑每个组合容量的问题
            foreach(var element in elements)
            {
                context.AppendMatched(element);
            }
        }

        protected virtual List<ElectiveElement> BuildElectiveElements(List<Student> students, List<SubjectGroup> groups, List<int> ranks)
        {
            // 根据组合与志愿 生成不同的选科元素
            // 可以按计算需要，相互挂一些引用，方便计算过程中的删除操作
            // TODO: 如果引入意向专业，也可以改写这个生成的逻辑
            throw new NotImplementedException();
        }

        /// <summary>
        /// 优先级比较
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public abstract int Compare(ElectiveElement x, ElectiveElement y);
    }

    public class PreferenceAheadElectiveEngine : ElectiveEngine
    {
        public PreferenceAheadElectiveEngine(IElectiveStrategy electiveStrategy)
            :base(electiveStrategy)
        {

        }

        public override int Compare(ElectiveElement x, ElectiveElement y)
        {
            // TODO： 容错处理
            if (x.PreferenceRank == y.PreferenceRank) return y.GroupRank - x.GroupRank;
            return y.PreferenceRank - x.PreferenceRank;
        }
    }

    public class ScoreAheadElectiveEngine : ElectiveEngine
    {
        public ScoreAheadElectiveEngine(IElectiveStrategy electiveStrategy)
            : base(electiveStrategy)
        {

        }

        public override int Compare(ElectiveElement x, ElectiveElement y)
        {
            // TODO： 容错处理
            if (x.GroupRank == y.GroupRank) return y.PreferenceRank - x.PreferenceRank;
            return y.GroupRank - x.GroupRank;
        }
    }
}
