Require Import Metatheory.
Require Import Atom.
Require Import Environment.
Require Import debruijn.

Open Scope list_scope.

Notation env := (list (atom * type_DeBruijn)).

(* env is well-formed if each atom is bound at most once
 * and every binding is to a locally closed type
 *)
Inductive env_wf : env -> Prop :=
  | env_wf_empty :
    env_wf nil

  | env_wf_type : forall Γ x τ,
    env_wf Γ ->
    type_lc τ ->
    x `notin` (dom Γ) ->
    env_wf ((x,τ)::Γ)
.

Ltac gather_atoms :=
  let A := gather_atoms_with (fun x : atoms => x) in
  let B := gather_atoms_with (fun x : atom => singleton x) in
  let C := gather_atoms_with (fun x : list (atom * type_DeBruijn) => dom x) in
  let D := gather_atoms_with (fun x : expr_DeBruijn => expr_fv x) in
  constr:(A `union` B `union` C `union` D).


Tactic Notation "pick" "fresh" ident(x) :=
  let L := gather_atoms in
  (pick fresh x for L).

Tactic Notation
      "pick" "fresh" ident(atom_name) "and" "apply" constr(lemma) :=
  let L := gather_atoms in
  pick fresh atom_name excluding L and apply lemma.