# -*- coding: utf-8 -*-
"""
Fields for Discontinous Galerkin method
"""
import numpy as nm
import six
from numpy.lib.stride_tricks import as_strided
from six.moves import range

from sfepy.base.base import (output, assert_, Struct)
from sfepy.discrete import Integral, PolySpace
from sfepy.discrete.common.fields import parse_shape
from sfepy.discrete.fem.fields_base import FEField
from sfepy.discrete.fem.mappings import VolumeMapping


def get_unraveler(n_el_nod, n_cell):
    """Returns function for unraveling i.e. unpacking dof data from
    serialized array from shape (n_el_nod*n_cell, 1) to (n_cell, n_el_nod, 1).
    
    The unraveler returns non-writeable view into the input array.

    Parameters
    ----------
    n_el_nod : int
        expected dimensions of dofs array
    n_cell : int
        

    Returns
    -------
    unravel : callable

    """
    def unravel(u):
        """Returns non-writeable view into the input array reshaped  to
        (n*m, 1) to (m, n, 1) .

        Parameters
        ----------
        u : array_like
            solution in shape (n*m, 1)

        Returns
        -------
        u : ndarray
            unraveledsolution in shape (m, n, 1)

        """
        ustride1 = u.strides[0]
        ur = as_strided(u,
                        shape=(n_cell, n_el_nod, 1),
                        strides=(n_el_nod * ustride1, ustride1, ustride1),
                        writeable=False)
        return ur

    return unravel


def get_raveler(n_el_nod, n_cell):
    """Returns function for raveling i.e. packing dof data from
    two dimensional array of shape (n_cell, n_el_nod, 1) to (n_el_nod*n_cell, 1)
    
    The raveler returns view into the input array.

    Parameters
    ----------
    n_el_nod :
        param n_el_nod, n_cell: expected dimensions of dofs array
    n_cell : int
        

    Returns
    -------
    ravel : callable
    """
    def ravel(u):
        """Returns view into the input array reshaped from (m, n, 1) to (n*m, 1)
        to (m, n, 1) .

        Parameters
        ----------
        u : array_like

        Returns
        -------
        u : ndarray
        """

        # ustride1 = u.strides[0]
        # ur = as_strided(u, shape=(n_el_nod*n_cell, 1),
        #                     strides=(n_cell*ustride1, ustride1))
        ur = nm.ravel(u)[:, None]
        # possibly use according to
        #  https://docs.scipy.org/doc/numpy/reference/generated/numpy.ravel.html
        # ur = u.reshape(-1)
        return ur

    return ravel


# mapping between geometry element types
# and their facets types
# TODO move to sfepy/discrete/fem/geometry_element.py?
cell_facet_gel_name = {
    "1_2": "0_1",
    "2_3": "1_2",
    "2_4": "1_2",
    "3_4": "2_3",
    "3_8": "2_4"
}


def get_gel(region):
    """
    Parameters
    ----------
    region : sfepy.discrete.common.region.Region

    Returns
    -------
    gel :
        base geometry element of the region
    """
    cmesh = region.domain.cmesh
    for key, gel in six.iteritems(region.domain.geom_els):
        ct = cmesh.cell_types
        if (ct[region.cells] == cmesh.key_to_index[gel.name]).all():
            return gel
        else:
            raise ValueError('Region {} contains multiple'
                             ' reference geometries!'.format(region))


class DGField(FEField):
    """Class for usage with DG terms, provides functionality for Discontinous
    Galerkin method like neighbour look up, projection to discontinuous basis
    and correct DOF treatment.
    """
    family_name = 'volume_DG_legendre_discontinuous'
    is_surface = False

    def __init__(self, name, dtype, shape, region, space="H1",
                 poly_space_base="legendre", approx_order=1, integral=None):
        """
        Creates DGField, with Legendre polyspace and default integral
        corresponding to 2 * approx_order.

        Parameters
        ----------

        name : string

        dtype : type

        shape : string
        'vector', 'scalar' or something else

        region : sfepy.discrete.common.region.Region

        space : string
            default "H1"

        poly_space_base : PolySpace
            optionally force polyspace

        approx_order : 0 for FVM, default 1

        integral : Integral
            if None integral of order 2*approx_order is created
        """
        shape = 
parse_shape(shape, region.domain.shape.dim)