defmodule Retex.Node.Type do
  @moduledoc """
  The NodeType if part of the alpha network, the discrimination part of the network
  that check if a specific class exists. If this is the case, it propagates the activations
  down to the select node types. They will select an attribute and check for its existance.
  """
  defstruct class: nil, id: nil
  @type t :: %Retex.Node.Type{}

  def new(class) do
    item = %__MODULE__{class: class}
    %{item | id: Retex.hash(item)}
  end

  defimpl Retex.Protocol.Activation do
    def activate(
          %Retex.Node.Type{class: class} = neighbor,
          %Retex{graph: _graph} = rete,
          %Retex.Wme{identifier: "$" <> _identifier = var} = wme,
          bindings,
          tokens
        ) do
      new_bindings = Map.merge(bindings, %{var => class})

      rete
      |> Retex.create_activation(neighbor, wme)
      |> Retex.add_token(neighbor, wme, new_bindings, tokens)
      |> Retex.continue_traversal(new_bindings, neighbor, wme)
    end

    def activate(
          %Retex.Node.Type{class: "$" <> _variable = var} = neighbor,
          %Retex{graph: _graph} = rete,
          %Retex.Wme{identifier: identifier} = wme,
          bindings,
          tokens
        ) do
      rete
      |> Retex.create_activation(neighbor, wme)
      |> Retex.add_token(neighbor, wme, Map.merge(bindings, %{var => identifier}), tokens)
      |> Retex.continue_traversal(Map.merge(bindings, %{var => identifier}), neighbor, wme)
    end

    def activate(
          %Retex.Node.Type{class: identifier} = neighbor,
          %Retex{} = rete,
          %Retex.Wme{identifier: identifier} = wme,
          bindings,
          tokens
        ) do
      rete
      |> Retex.create_activation(neighbor, wme)
      |> Retex.add_token(neighbor, wme, bindings, tokens)
      |> Retex.continue_traversal(bindings, neighbor, wme)
    end

    def activate(
          %Retex.Node.Type{class: _class},
          %Retex{graph: _graph} = rete,
          %Retex.Wme{identifier: _identifier} = _wme,
          _bindings,
          _tokens
        ) do
      Retex.stop_traversal(rete, %{})
    end

    @spec active?(%{id: any}, Retex.t()) :: boolean()
    def active?(%{id: id}, %Retex{activations: activations}) do
      Enum.any?(Map.get(activations, id, []))
    end
  end
end
