"""
Composite Lamina Module (:mod:`compmech.composite.lamina`)
==========================================================

.. currentmodule:: compmech.composite.lamina

"""
from __future__ import division, absolute_import

import numpy as np
from numpy import cos, sin

from compmech.constants import DOUBLE
from .matlamina import MatLamina


class Lamina(object):
    """
    =========  ===========================================================
    attribute  description
    =========  ===========================================================
    plyid      id of the composite lamina
    matobj     a pointer to a MatLamina object
    t          ply thickness
    theta      ply angle in degrees
    L          transformation matrix for displacements to laminate csys
    R          transformation matrix for stresses to laminate csys
    T          transformation matrix for stresses to lamina csys
    QL         constitutive matrix for plane-stress in laminate csys
    laminates  laminates that contain this lamina
    =========  ===========================================================

    References:
    -----------
    .. [1] Reddy, J. N., Mechanics of Laminated Composite Plates and
       Shells - Theory and Analysys. Second Edition. CRC PRESS, 2004.

    """
    def __init__(self):
        self.plyid    = None
        self.matobj   = None
        self.t        = None
        self.theta    = None
        self.L        = None
        self.R        = None
        self.T        = None
        self.QL       = None
        self.laminates = []

    def rebuild(self):
        thetarad = np.deg2rad(self.theta)
        cost = cos(thetarad)
        sint = sin(thetarad)
        sin2t = sin(2*thetarad)
        #
        cos2   = cost**2
        cos3   = cost**3
        cos4   = cost**4
        sin2   = sint**2
        sin3   = sint**3
        sin4   = sint**4
        sincos = sint*cost
        self.L = np.array([[ cost, sint, 0],
                           [-sint, cost, 0],
                           [   0,     0, 1]], dtype=DOUBLE)
        #STRESS
        #to lamina
        self.R = np.array(
            [[   cos2,   sin2, 0,   0,    0,     sin2t],
             [   sin2,   cos2, 0,   0,    0,    -sin2t],
             [      0,      0, 1,   0,    0,         0],
             [      0,      0, 0, cost, -sint,         0],
             [      0,      0, 0, sint,  cost,         0],
             [-sincos, sincos, 0,   0,    0, cos2-sin2]],dtype=DOUBLE)
        #to laminate
        self.T = np.array(
            [[  cos2,    sin2, 0,    0,   0,    -sin2t],
             [  sin2,    cos2, 0,    0,   0,     sin2t],
             [     0,       0, 1,    0,   0,         0],
             [     0,       0, 0,  cost, sint,         0],
             [     0,       0, 0, -sint, cost,         0],
             [sincos, -sincos, 0,    0,   0, cos2-sin2]],dtype=DOUBLE)
        # STRAINS
        # different from stress due to:
        #     2*e12 = e6    2*e13 = e5    2*e23 = e4
        # to laminate
        # self.Rstrain = np.transpose(self.Tstress)
        # to lamina
        # self.Tstrain = np.transpose(self.Rstress)
        if isinstance(self.matobj, MatLamina):
            e1   = self.matobj.e1
            e2   = self.matobj.e2
            nu12 = self.matobj.nu12
            nu21 = self.matobj.nu21
            g12  = self.matobj.g12
            g13  = self.matobj.g13
            g23  = self.matobj.g23
        else:
            e1   = self.matobj.e
            e2   = self.matobj.e
            nu12 = self.matobj.nu
            nu21 = self.matobj.nu
            g12  = self.matobj.g
            g  = self.matobj.g

        # plane stress
        q11  = e1/(1-nu12*nu21)
        q12  = nu12*e2/(1-nu12*nu21)
        q22  = e2/(1-nu12*nu21)
        q44  = g23
        q55  = g13
        q16 = 0
        q26 = 0
        q66  = g12

        q11L = q11*cos4 + 2*(q12 + 2*q66)*sin2*cos2 + q22*sin4
        q12L = (q11 + q22 - 4*q66)*sin2*cos2 + q12*(sin4 + cos4)
        q22L = q11*sin4 + 2*(q12 + 2*q66)*sin2*cos2 + q22*cos4
        q16L = (q11 - q12 - 2*q66)*sint*cos3 + (q12 - q22 + 2*q66)*sin3*cost
        q26L = (q11 - q12 - 2*q66)*sin3*cost + (q12 - q22 + 2*q66)*sint*cos3
        q66L = (q11 + q22 - 2*q12 - 2*q66)*sin2*cos2 + q66*(sin4 + cos4)
        q44L = q44*cos2 + q55*sin2
        q45L = (q55 - q44)*sincos
        q55L = q55*cos2 + q44*sin2

        self.QL = np.array([[q11L, q12L, q16L,    0,    0],
                            [q12L, q22L, q26L,    0,    0],
                            [q16L, q26L, q66L,    0,    0],
                            [   0,    0,    0, q44L, q45L],
                            [   0,    0,    0, q45L, q55L]], dtype=DOUBLE)

        #TODO add the thermal coeficient terms when calculating the
        #     stresses... to take into account eventual thermal expansions /
        #     contractions

