import Data.Category
import Data.Category.Bicategory
import Data.Category.Bifunctor
import Data.Category.Functor
import Data.Category.Functor.Category
import Data.Category.NaturalTransformation
 
import Syntax.PreorderReasoning
 
import Data.Sigma
 
horzId : {o1, o2, o3 : Type} ->
    {a : Category o1} -> {b : Category o2} -> {c : Category o3} ->
    (f1 : a ->> b) -> (f2 : b ->> c) ->
    identity {f = f1} -⨾- identity {f = f2} `NTEq` identity {f = f1 ⨾⨾ f2}
horzId (MkFunctor fo1 fm1 fi1 fc1) (MkFunctor fo2 fm2 fi2 fc2) = MkNTEq $ \x =>  Calc $
    |~ (|:>) c (fm2 (fo1 x) (fo1 x) (b.id (fo1 x))) (fm2 (fo1 x) (fo1 x) (fm1 x x (a.id x)))
    ~~ (|:>) c (c.id (fo2 (fo1 x))) (fm2 (fo1 x) (fo1 x) (fm1 x x (a.id x)))
        ...(cong (\xn => (|:>) c xn (fm2 (fo1 x) (fo1 x) (fm1 x x (a.id x))))
                (fi2 (fo1 x)))
    ~~ fm2 (fo1 x) (fo1 x) (fm1 x x (a.id x)) ...(c.idLeft _ _ _)
 
horizontalMorNT : (a, b, c : Σ Type Category) ->
                  Bifunctor (FunctorCat a.π2 b.π2) (FunctorCat b.π2 c.π2) (FunctorCat a.π2 c.π2)
horizontalMorNT (o1 ## c1) (o2 ## c2) (o3 ## c3) = MkFunctor
    (uncurry (⨾⨾))
    (\f1, f2, m => m.π1 -⨾- m.π2)
    (\(f1 && f2) => ntEqToEq $ horzId f1 f2)
    (\a, b, c, f, g => sym $ ntEqToEq $ interchange _ _ _ _)
 
SetBicategory : Bicategory (Σ Type Category)
SetBicategory = MkBiCat
    (\x, y => x.π2 ->> y.π2)
    (\x, y => FunctorCat x.π2 y.π2)
    horizontalMorNT