using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;

namespace CategoryTheory
{
    /// <summary>
    /// Standard category operations
    /// </summary>
    public class CategoryOperations
    {
        /// <summary>
        /// Gets arrow to equalizer
        /// </summary>
        /// <param name="equalizer">The qualizer</param>
        /// <param name="arrow">Arrow to object</param>
        /// <returns>Arrow to equalizer</returns>
        public static ICategoryArrow GetArrowToEqualizer(ICategoryArrow equalizer, ICategoryArrow arrow)
        {
            ICategoryObject s = arrow.Source;
            ICategoryObject t = equalizer.Source;
            ICategoryArrow[,] arrows = new ICategoryArrow[1, 3];
            arrows[0, 0] = arrow;
            arrows[0, 1] = arrow.Source.Id;
            arrows[0, 2] = equalizer;
            IDiagramRestoredObject r = s as IDiagramRestoredObject;
            FindResults res;
            return r.RestoreArrow(t, arrows, out res);
        }


        /// <summary>
        /// Gets arrow to coequalizer
        /// </summary>
        /// <param name="coequalizer">The qualizer</param>
        /// <param name="arrow">Arrow to object</param>
        /// <returns>Arrow from equalizer</returns>
        public static ICategoryArrow GetArrowFromCoequalizer(ICategoryArrow coequalizer, ICategoryArrow arrow)
        {
            ICategoryObject s = arrow.Target;
            ICategoryObject t = coequalizer.Source;
            ICategoryArrow[,] arrows = new ICategoryArrow[1, 3];
            arrows[0, 0] = arrow;
            arrows[0, 1] = coequalizer;
            arrows[0, 2] = arrow.Source.Id;
            IDiagramRestoredObject r = s as IDiagramRestoredObject;
            FindResults res;
            return r.RestoreArrow(s, arrows, out res);
        }


        /// <summary>
        /// Gets colimit
        /// </summary>
        /// <param name="category">The category</param>
        /// <param name="arrows">Diagram arrows</param>
        /// <param name="coequalizer">Coequalizer</param>
        /// <param name="objectArrows">Arrows to colimit</param>
        /// <param name="productArrows">Sum arrows</param>
        /// <returns>The colimit</returns>
        public static ICategoryObject GetColim(ICategory category, ArrayList arrows,
            ref ICategoryArrow coequalizer, ref ArrayList objectArrows, ref ArrayList productArrows)
        {
            ArrayList objects = new ArrayList();
            ArrayList sources = new ArrayList();
            IDirectSumCategory direct = category as IDirectSumCategory;
            ICoequalizerCategory coequ = category as ICoequalizerCategory;
            foreach (ICategoryArrow arrow in arrows)
            {
                if (!objects.Contains(arrow.Source))
                {
                    objects.Add(arrow.Source);
                }
                if (!objects.Contains(arrow.Target))
                {
                    objects.Add(arrow.Source);
                }
                sources.Add(arrow.Source);
            }
            ArrayList productSourceArrows = new ArrayList();
            productArrows = new ArrayList();
            ICategoryObject product = direct.GetDirectSum(objects, productArrows);
            ICategoryObject productSource = direct.GetDirectSum(sources, productSourceArrows);
            ArrayList coequList = new ArrayList();
            ArrayList arrList = new ArrayList();
            foreach (ICategoryArrow arr in productArrows)
            {
                ICategoryObject target = arr.Target;
                foreach (ICategoryObject t in sources)
                {
                    if (t == target)
                    {
                        arrList.Add(arr);
                    }
                }
            }
            ICategoryArrow first = direct.GetArrowFromDirectSum(product, productSource, arrList);
            foreach (ICategoryArrow arr in arrows)
            {
                int num = objects.IndexOf(arr.Target);
                ICategoryArrow pr = productArrows[num] as ICategoryArrow;
                ICategoryArrow eq = pr.Compose(category, arr);
                coequList.Add(eq);
            }
            ICategoryArrow second = direct.GetArrowFromDirectSum(product, productSource, coequList);
            coequalizer = coequ.GetCoequalizer(first, second);
            objectArrows = new ArrayList();
            foreach (ICategoryArrow arrow in productArrows)
            {
                ICategoryArrow arr = coequalizer.Compose(category, arrow);
                objectArrows.Add(arr);
            }
            return coequalizer.Target;
        }

        /// <summary>
        /// Composition with identical morphism
        /// </summary>
        /// <param name="first">First morphism</param>
        /// <param name="second">Second morphism</param>
        /// <returns>The composition</returns>
        static public ICategoryArrow CompositionWithId(ICategoryArrow first, ICategoryArrow second)
        {
            if (first == first.Target.Id)
            {
                return second;
            }
            if (second == second.Source.Id)
            {
                return first;
            }
            return null;
        }
    }

}
