{-# OPTIONS --without-K --safe #-}
module Cats.Functor where

open import Level
open import Relation.Binary using (_Preserves_⟶_)

open import Cats.Category.Base
open import Cats.Util.Function using () renaming (_∘_ to _⊙_)
open import Cats.Util.SetoidMorphism using (_⇒_ ; IsInjective ; IsSurjective)
open import Cats.Util.SetoidMorphism.Iso using
  ( IsIso ; _≅_ ; IsIso→≅ ; Injective∧Surjective→Iso ; Iso→Injective
  ; Iso→Surjective )

import Cats.Category.Constructions.Iso as Iso

open Iso.Iso


infixr 9 _∘_


record Functor {lo la l≈ lo′ la′ l≈′}
  (C : Category lo la l≈)
  (D : Category lo′ la′ l≈′)
  : Set (lo  la  l≈  lo′  la′  l≈′)
  where
  private
    module C = Category C
    module C≅ = Iso.Build C
    module D = Category D
    module D≅ = Iso.Build D

  field
    fobj : C.Obj  D.Obj
    fmap :  {A B}  A C.⇒ B  fobj A D.⇒ fobj B
    fmap-resp :  {A B}  fmap {A} {B} Preserves C._≈_  D._≈_
    fmap-id :  {A}  fmap (C.id {A}) D.≈ D.id
    fmap-∘ :  {A B C} {f : B C.⇒ C} {g : A C.⇒ B}
       fmap f D.∘ fmap g D.≈ fmap (f C.∘ g)


  sfmap :  {a b}  C.Hom a b  D.Hom (fobj a) (fobj b)
  sfmap = record
      { arr = fmap
      ; resp = fmap-resp
      }


  fobj-resp : fobj Preserves C≅._≅_  D≅._≅_
  fobj-resp {i} {j} x≅y
      = record
      { forth = fmap (forth x≅y)
      ; back = fmap (back x≅y)
      ; back-forth
          = begin
              fmap (back x≅y) D.∘ fmap (forth x≅y)
            ≈⟨ fmap-∘ 
              fmap (back x≅y C.∘ forth x≅y)
            ≈⟨  fmap-resp (back-forth x≅y) 
              fmap C.id
            ≈⟨ fmap-id 
              D.id
            
      ; forth-back
          = begin
            fmap (forth x≅y) D.∘ fmap (back x≅y)
          ≈⟨ fmap-∘ 
            fmap (forth x≅y C.∘ back x≅y)
          ≈⟨ fmap-resp (forth-back x≅y) 
            fmap C.id
          ≈⟨ fmap-id 
            D.id
          
      }
    where
      open D.≈-Reasoning

open Functor public


id :  {lo la l≈} {C : Category lo la l≈}  Functor C C
id {C = C} = record
    { fobj = λ x  x
    ; fmap = λ f  f
    ; fmap-resp = λ eq  eq
    ; fmap-id = ≈.refl
    ; fmap-∘ = ≈.refl
    }
  where
    open Category C


_∘_ :  {lo la l≈ lo′ la′ l≈′ lo″ la″ l≈″}
   {C : Category lo la l≈}
   {D : Category lo′ la′ l≈′}
   {E : Category lo″ la″ l≈″}
   Functor D E  Functor C D  Functor C E
_∘_ {E = E} F G = record
    { fobj = fobj F  fobj G
    ; fmap = fmap F  fmap G
    ; fmap-resp = fmap-resp F  fmap-resp G
    ; fmap-id = ≈.trans (fmap-resp F (fmap-id G)) (fmap-id F)
    ; fmap-∘ = ≈.trans (fmap-∘ F) (fmap-resp F (fmap-∘ G))
    }
  where
    open Category E


module _ {lo la l≈ lo′ la′ l≈′}
  {C : Category lo la l≈} {D : Category lo′ la′ l≈′}
  (F : Functor C D)
  where

  private
    module C = Category C
    module D = Category D


  IsFaithful : Set (lo  la  l≈  l≈′)
  IsFaithful =  {a b}  IsInjective (sfmap F {a} {b})


  IsFull : Set (lo  la  la′  l≈′)
  IsFull =  {a b}  IsSurjective (sfmap F {a} {b})


  IsEmbedding : Set (lo  la  l≈  la′  l≈′)
  IsEmbedding =  {a b}  IsIso (sfmap F {a} {b})


  Embedding→≅ : IsEmbedding   {a b}  C.Hom a b  D.Hom (fobj F a) (fobj F b)
  Embedding→≅ embedding = IsIso→≅ (sfmap F) embedding


  Full∧Faithful→Embedding : IsFull  IsFaithful  IsEmbedding
  Full∧Faithful→Embedding full faithful
      = Injective∧Surjective→Iso faithful full


  Embedding→Full : IsEmbedding  IsFull
  Embedding→Full embedding = Iso→Surjective embedding


  Embedding→Faithful : IsEmbedding  IsFaithful
  Embedding→Faithful embedding = Iso→Injective embedding