{-# 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