Require Import Crypto.Util.Tactics.ConstrFail.

Ltac debuglevel := constr:(0%nat).

Ltac solve_debugfail tac :=
  solve [tac] ||
        ( let dbg := debuglevel in
          match dbg with
          | O => idtac
          | _ => match goal with |- ?G => idtac "couldn't prove" G end
          end;
          fail).

(** constr-based [idtac] *)
Class cidtac {T} (msg : T) := Build_cidtac : True.
Global Hint Extern 0 (cidtac ?msg) => idtac msg; exact I : typeclass_instances.
(** constr-based [idtac] *)
Class cidtac2 {T1 T2} (msg1 : T1) (msg2 : T2) := Build_cidtac2 : True.
Global Hint Extern 0 (cidtac2 ?msg1 ?msg2) => idtac msg1 msg2; exact I : typeclass_instances.
Class cidtac3 {T1 T2 T3} (msg1 : T1) (msg2 : T2) (msg3 : T3) := Build_cidtac3 : True.
Global Hint Extern 0 (cidtac3 ?msg1 ?msg2 ?msg3) => idtac msg1 msg2 msg3; exact I : typeclass_instances.
Class cidtac4 {T1 T2 T3 T4} (msg1 : T1) (msg2 : T2) (msg3 : T3) (msg4 : T4) := Build_cidtac4 : True.
Global Hint Extern 0 (cidtac4 ?msg1 ?msg2 ?msg3 ?msg4) => idtac msg1 msg2 msg3 msg4; exact I : typeclass_instances.

Class cfail {T} (msg : T) := Build_cfail : True.
Global Hint Extern 0 (cfail ?msg) => idtac "Error:" msg; exact I : typeclass_instances.
Class cfail2 {T1 T2} (msg1 : T1) (msg2 : T2) := Build_cfail2 : True.
Global Hint Extern 0 (cfail2 ?msg1 ?msg2) => idtac "Error:" msg1 msg2; exact I : typeclass_instances.
Class cfail3 {T1 T2 T3} (msg1 : T1) (msg2 : T2) (msg3 : T3) := Build_cfail3 : True.
Global Hint Extern 0 (cfail3 ?msg1 ?msg2 ?msg3) => idtac "Error:" msg1 msg2 msg3; exact I : typeclass_instances.

Ltac constr_run_tac tac :=
  let dummy := match goal with
               | _ => tac ()
               end in
  constr:(I).
Ltac constr_run_tac_fail tac :=
  let dummy := match goal with
               | _ => tac ()
               end in
  constr_fail.

Ltac cidtac msg :=
  constr_run_tac ltac:(fun _ => idtac msg).
Ltac cidtac2 msg1 msg2 :=
  constr_run_tac ltac:(fun _ => idtac msg1 msg2).
Ltac cidtac3 msg1 msg2 msg3 :=
  constr_run_tac ltac:(fun _ => idtac msg1 msg2 msg3).
Ltac cidtac4 msg1 msg2 msg3 msg4 :=
  constr_run_tac ltac:(fun _ => idtac msg1 msg2 msg3 msg4).
Ltac cfail msg :=
  constr_run_tac_fail ltac:(fun _ => idtac "Error:" msg).
Ltac cfail2 msg1 msg2 :=
  constr_run_tac_fail ltac:(fun _ => idtac "Error:" msg1 msg2).
Ltac cfail3 msg1 msg2 msg3 :=
  constr_run_tac_fail ltac:(fun _ => idtac "Error:" msg1 msg2 msg3).

Ltac idtac_goal := lazymatch goal with |- ?G => idtac "Goal:" G end.
Ltac idtac_context :=
  try (repeat match goal with H : _ |- _ => revert H end;
       idtac_goal;
       lazymatch goal with |- ?G => idtac "Context:" G end;
       fail).
