"""
Classes of variables for equations/terms.
"""
from __future__ import print_function
from __future__ import absolute_import
from collections import deque

import numpy as nm

from sfepy.base.base import (real_types, complex_types, assert_, get_default,
                             output, OneTypeList, Container, Struct, basestr,
                             iter_dict_of_lists)
from sfepy.base.timing import Timer
import sfepy.linalg as la
from sfepy.discrete.functions import Function
from sfepy.discrete.conditions import get_condition_value
from sfepy.discrete.integrals import Integral
from sfepy.discrete.common.dof_info import (DofInfo, EquationMap,
                                            expand_nodes_to_equations,
                                            is_active_bc)
from sfepy.discrete.fem.lcbc_operators import LCBCOperators
from sfepy.discrete.common.mappings import get_physical_qps
from sfepy.discrete.evaluate_variable import eval_real, eval_complex
import six
from six.moves import range

is_state = 0
is_virtual = 1
is_parameter = 2
is_field = 10

def create_adof_conns(conn_info, var_indx=None, active_only=True, verbose=True):
    """
    Create active DOF connectivities for all variables referenced in
    `conn_info`.

    If a variable has not the equation mapping, a trivial mapping is assumed
    and connectivity with all DOFs active is created.

    DOF connectivity key is a tuple ``(primary variable name, region name,
    type, is_trace flag)``.

    Notes
    -----
    If `active_only` is False, the DOF connectivities contain all DOFs, with
    the E(P)BC-constrained ones stored as `-1 - <DOF number>`, so that the full
    connectivities can be reconstructed for the matrix graph creation.
    """
    var_indx = get_default(var_indx, {})

    def _create(var, econn):
        offset = var_indx.get(var.name, slice(0, 0)).start
        if var.eq_map is None:
            eq = nm.arange(var.n_dof, dtype=nm.int32)

        else:
            if isinstance(var, DGFieldVariable):
                eq = nm.arange(var.n_dof, dtype=nm.int32)
            else:
                if active_only:
                    eq = var.eq_map.eq
                else:
                    eq = nm.arange(var.n_dof, dtype=nm.int32)
                    eq[var.eq_map.eq_ebc] = -1 - (var.eq_map.eq_ebc + offset)
                    eq[var.eq_map.master] = eq[var.eq_map.slave]

        adc = create_adof_conn(eq, econn, var.n_components, offset)

        return adc

    def _assign(adof_conns, info, region, var, field, is_trace):
        key = (var.name, region.name, info.dc_type.type, is_trace)
        if not key in adof_conns:
            econn = field.get_econn(info.dc_type, region, is_trace=is_trace)
            if econn is None: return

            adof_conns[key] = _create(var, econn)

        if info.is_trace:
            key = (var.name, region.name, info.dc_type.type, False)
            if not key in adof_conns:
                econn = field.get_econn(info.dc_type, region, is_trace=False)

                adof_conns[key] = _create(var, econn)

    if verbose:
        output('setting up dof connectivities...')
        timer = Timer(start=True)

    adof_conns = {}

    for key, ii, info in 
iter_dict_of_lists(conn_info, return_keys=True)