add a container to the registered database (using the unary parametricity translation)

Require Import MetaCoq.Template.All.

From MetaCoq.PCUIC Require Import
     PCUICAst PCUICAstUtils PCUICInduction
     PCUICLiftSubst PCUICEquality
     PCUICUnivSubst PCUICTyping PCUICGeneration.

From MetaCoq.PCUIC Require Import PCUICToTemplate.
From MetaCoq.PCUIC Require Import TemplateToPCUIC.

From MetaCoq.Translations Require Import translation_utils.
From MetaCoq.Translations Require Import param_original.

Require Import List String.
Import ListNotations MonadNotation Nat.
Require Import MetaCoq.Template.Pretty.
Require Import MetaCoq.PCUIC.PCUICPretty.

Open Scope string_scope.

Require Import helperGen.
Require Import removeNonAug.

Definition nameAppend (n:name) (s:ident) :=
  match n with
    nAnon => nNamed s
  | nNamed s2 => nNamed (append s2 s)
  end.

(*
manage nested, guarded
A:T
P: A -> T
H: forall a:A, P a

P:A->T
P2: forall a:A, P a -> T
H: forall a:A, forall x:P a, P2 a x

All rT ...
isAll rT P
pAll rT P f

All nat (fun n => rT)
isAll nat TA (fun n => rT) (fun n => P)
pAll nat TA TP (fun n => rT) (fun n => P) (fun n => f)
P a is rT

use eta conversion
f = fun x:rT, f x

vec: T -> N -> T
vecAss: forall (X:T) (P:X->T) (n:nat), vec X n -> T
vecProof: forall (X:T) (P:X->T) (H:forall x^X, P x) (n:nat) (xs:vec X n), is_vec P n xs

assumption not needed
only application body
and isProof, args

appBody args -> T for assumption
and 
appBody args (with one argument missing) for proof

cases:
tProd => augment
tSort => body
_ => None

needed ideas:
what with nesting
list Type

applied: list A
only if container in Parameter

T1: X -> list X -> Type

usage:
rtB: T1 rT xs
isT1 rT P xs (isL rT P)

could be ignored


this function is very close to the one in destruct_lemma
*)


(* assumption also indicates if proof is generated *)
Fixpoint augmentType (ind:term) (assumption:option term) (args fullargs:list term) (t:term) : term :=
  match t with
  | tProd na t1 t2 =>
      let sortType :=
      match t1 with
        tSort _ => true
      | _ => false
      end in
      (* let newArgs := if sortType then (if assumption then 2 else 1) else 0 in *)
      let arguments :=
        vass na t1::
        if sortType then
          vass (nameAppend na "P") (tProd nAnon (* P:X->Type *)
            (tRel 0)
            (trans <% Type %>)
          )::
          (
            if assumption then
              [vass (nameAppend na "H")
              (tProd (nNamed "x") (* H:forall (x:X), P x *)
                (tRel 1)
                (tApp (tRel 1) (tRel 0))
              ) ]
            else
              []
          )
        else
          []
      in
      let newArgs := #|arguments| - 1 in
      it_mkProd_or_LetIn (rev arguments)
      (
          augmentType ind assumption
          (map (lift0 (S newArgs)) args ++ [tRel newArgs]) (* X *)
          (map (lift0 (S newArgs)) fullargs ++
          [tRel newArgs] ++
          if Nat.eqb newArgs 0 then
            []
          else
            [tRel (newArgs-1)]
          )
          (* tRel newArgs;tRel (newArgs-1)) *)
          (* X, P *) (* can be invalid if assumption=None *)
          t2
      )
  | _ =>
      tProd nAnon
        (mkApps ind args)
        (
          match assumption with
            None => trans <% Type %>
          | Some ass => mkApps ass (map (lift0 1) fullargs ++ [tRel 0])
          (* | Some ass => mkApps ass (fullargs) *)
          end
        )
  end.

Unset Strict Unquote Universe Mode.

Print Ast.tInd.
Print inductive.
Print typed_term.

Print tmEval.

Definition getIndProp (kname:kername) (TL:tsl_table) :=
      H <- tmAbout kname;;
      (match H with
        Some ((IndRef (mkInd kn n)) as R) =>
        match lookup_tsl_table TL R with
        | Some(Ast.tInd (mkInd kname idx) u) =>
            tmReturn (kname,idx,u)
            (* tmMsg ("New inductive: "++kname) *)
        | _ => tmFail "No inductive was not found in the translation"
        end
      | _ => tmFail "The inductive was not found in the translation"
      end).

Print IndRef.
Print tmUnquote.
Print typed_term.

Definition addType {X} (t:X) :TemplateMonad unit :=
  tq <- tmQuote t;;
  match tq with
  | Ast.tInd ((mkInd kname idx) as ind) _ =>
      mind <- tmQuoteInductive (inductive_mind ind);;
      let name :=
        match nth_error (Ast.ind_bodies mind) (inductive_ind ind) with
        | None => "noName"
        | Some oind => Ast.ind_name oind
        end
      in

      inst <- tmInferInstance None (registered t);;
      match inst with
        my_Some _ => tmMsg "An instance was already found. The new instance will overwrite the old one."
      | _ => tmReturn tt
      end;;
      T <- tmQuote X;;

      TL <- TranslateRec emptyTC t;;

      newInd <- getIndProp kname TL.2;;

      let (newIndP1,u) := newInd : kername*nat*Instance.t in
      let (kname,idx) := newIndP1 in

      kname2' <- cleanInd kname idx u;;
      kname2 <- tmEval all (kname2');;
      H2 <- tmAbout kname2;;
      newInd2 <- (match H2 with
        Some ((IndRef (mkInd kn n)) as R) =>
        tmReturn (kn,n)
      | _ => tmFail "no inductive after cleaning"
      end);;
      let (kname3,idx3) := newInd2 : kername * nat in
      let h := Ast.tInd (mkInd kname3 idx3) u in

      let assTt := augmentType (trans tq) None [] [] (trans T) in
      assT <- tmUnquoteTyped Type (PCUICToTemplate.trans assTt);;
      assI <- tmUnquoteTyped (assT:Type) h;;
      (* tp <- tmUnquote h;; *)
      (* let (assT,assI) := tp : typed_term in *)
      (* print_nf assT';; *)
      (* print_nf assT;; *)
      (* print_nf assI;; *)

      (* let (assTE,assIE) := assI : typed_term in *)
      (* assIE <- tmEval all (assI.(my_projT2));; *)

      (* tmMsg "Please provide the assumption predicate.";; *)
      (* nameString <- tmEval lazy (append name "_assumption");; *)
      (* newName <- tmFreshName nameString;; (1* T_assumption *1) *)
      (* let assTt := augmentType (trans tq) None   (trans T) in *)
      (* assT <- tmUnquoteTyped Type (PCUICToTemplate.trans assTt);; *)
      (* assI2 <- tmLemma newName (assT : Type);; *)

      tmMsg "Please provide a proof for the predicate.";;
      nameString2 <- tmEval lazy (append name "_proof");;
      newName2 <- tmFreshName nameString2;; (* T_proof *)
      (* h2 <- tmQuote assI2;; *)
      let proofTt := augmentType (trans tq) (Some (trans h)) [] [] (trans T) in
      (* print_nf h;; *)
      (* print_nf proofTt;; *)
      (* tmReturn tt *)
      (* tmMsg "T1";; *)
      proofT <- tmUnquoteTyped Type (PCUICToTemplate.trans proofTt);;
      print_nf proofT;;
      (* tmMsg "T1.5";; *)
      proofI <- tmLemma newName2 (proofT : Type);;
      (* proofI <- tmLemma newName2 False;; *)
      (* proofI <- tmLemma "H" nat;; *)
      (* proofI <- tmLemma "noName" (proofT : Type);; *)
      (* tmMsg "T2";; *)

      nameString3 <- tmEval lazy (append name "_inst");;
      newName3 <- tmFreshName nameString3;; (* T_inst *)
      tmDefinition newName3 (
        {|
            (* assumptionType := assTE; *)
            (* assumptionType := assT; *)
            assumption := @assI;
            proof := @proofI
        |} : registered t
      );;
      tmExistingInstance newName3;;

      tmMsg (append (append "New instance " newName3) " was created")

      (* tmMsg "Fin" *)
  | _ =>
      @tmFail unit "inductive type is expected"
  end.