# `XGW.ICE.Full`
[🔗](https://github.com/Lazarus404/xgw/blob/main/lib/xgw/ice/full.ex#L1)

Full ICE agent: gather candidates, run the checklist, nominate a pair.

## When to use full ICE

**ICE-lite** only answers inbound STUN checks (typical for a public server
with a known address). **Full ICE** also gathers local candidates, forms
pairs, and sends outbound connectivity checks -- required when the gateway
sits behind NAT or needs TURN relay paths itself.

Phase transitions:

```text
:gathering -> :checking -> :connected
```

Outbound and inbound STUN checks, nomination (`USE-CANDIDATE`), and trickle
candidate merges all live here. Pure helpers stay in `XGW.ICE` /
`XGW.ICE.Checklist`; this module owns agent state updates used by `XGW.Peer`.

## RFCs

* RFC 8445 -- full ICE agent (gather, checklist, checks, nomination, phases)
* RFC 8489 -- STUN Binding for connectivity checks
* RFC 7675 -- consent freshness after nomination

# `from`

```elixir
@type from() :: XGW.ICE.endpoint()
```

Remote UDP endpoint `{ip, port}`.

# `stun_result`

```elixir
@type stun_result() ::
  {:ignore, t()}
  | {:reply, binary(), t()}
  | {:send, binary(), from(), t()}
  | {:connected, binary(), t(), from()}
  | {:consent, t()}
```

Result of handling inbound STUN while checking or connected.

Includes consent refresh (`:consent`) and nomination (`:connected`).

# `t`

```elixir
@type t() :: %{
  phase: XGW.ICE.ice_phase(),
  role: XGW.ICE.role(),
  tiebreaker: binary(),
  ice_ufrag: String.t() | nil,
  ice_pwd: String.t() | nil,
  remote_ice_ufrag: String.t() | nil,
  remote_ice_pwd: String.t() | nil,
  bundle_mids: [String.t()],
  local_candidates: [XMediaLib.SDP.Candidate.t()],
  remote_candidates: [XMediaLib.SDP.Candidate.t()],
  remote_candidates_eoc?: boolean(),
  checklist: [XGW.ICE.Checklist.pair()],
  nominated_pair: XGW.ICE.Checklist.pair() | nil,
  selected: from() | nil,
  pending_triggered: XGW.ICE.Checklist.pair() | nil,
  last_consent_at: integer() | nil
}
```

Full ICE agent state.

# `add_local_candidate`

```elixir
@spec add_local_candidate(t(), XMediaLib.SDP.Candidate.t()) :: {:ok, t()}
```

Merges one local candidate (host, srflx, or relay trickle) into the agent.

# `add_remote_candidate`

```elixir
@spec add_remote_candidate(t(), map() | XMediaLib.SDP.Candidate.t()) ::
  {:ok, t()} | {:error, term()}
```

Adds a trickle remote candidate or records end-of-candidates.

# `check_ta_ms`

```elixir
@spec check_ta_ms() :: pos_integer()
```

Checklist tick interval in milliseconds (Ta).

# `checklist_inputs`

```elixir
@spec checklist_inputs(t()) :: %{
  local: [XMediaLib.SDP.Candidate.t()],
  remote: [XMediaLib.SDP.Candidate.t()]
}
```

Returns checklist inputs for pair formation.

# `extend_locals`

```elixir
@spec extend_locals(t(), [XMediaLib.SDP.Candidate.t()]) :: t()
```

Rebuilds the checklist after new local candidates arrive (srflx/relay trickle).

# `from_config`

```elixir
@spec from_config(map()) :: {:ok, t()} | {:error, :unsupported_setup}
```

Builds full ICE state from `XGW.Descriptions` config after negotiation.

# `handle_stun`

```elixir
@spec handle_stun(binary(), from(), t(), keyword()) :: stun_result()
```

Handles one STUN datagram (inbound checks and outbound responses).

Options:

  * `:transport` - `:udp` (default) or `:tcp` (ICE-TCP path; peer-reflexive
    remotes inherit this so checklist pairs can form)

# `ice_restart`

```elixir
@spec ice_restart(t()) :: t()
```

Resets checklist state for ICE restart (remote ufrag/pwd supplied by caller).

# `mark_completed`

```elixir
@spec mark_completed(t()) :: t()
```

Marks ICE processing complete after nomination (`:connected` -> `:completed`).

# `new`

```elixir
@spec new(keyword()) :: t()
```

Builds an empty full ICE agent (before `apply_descriptions`).

# `phase`

```elixir
@spec phase(t()) :: XGW.ICE.ice_phase()
```

Returns the ICE phase.

# `ready_for_checks?`

```elixir
@spec ready_for_checks?(t()) :: boolean()
```

Returns true when at least one local and one remote candidate exist.

# `seed_local_candidates`

```elixir
@spec seed_local_candidates(t(), [XMediaLib.SDP.Candidate.t()]) :: t()
```

Seeds local candidates from the local answer SDP.

# `seed_remote_candidates`

```elixir
@spec seed_remote_candidates(t(), [XMediaLib.SDP.Candidate.t()]) :: t()
```

Seeds remote candidates from the initial offer SDP.

# `start_checking`

```elixir
@spec start_checking(t()) :: t()
```

Builds the checklist and moves phase to `:checking`.

# `tick`

```elixir
@spec tick(t(), integer()) ::
  {:noop, t()} | {:send, binary(), from(), t(), non_neg_integer()}
```

Processes one checklist tick; may return an outbound STUN check to send.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
