/* Fesslix - Stochastic Analysis
 * Copyright (C) 2010-2016 Wolfgang Betz
 *
 * Fesslix is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Fesslix is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Fesslix.  If not, see <http://www.gnu.org/licenses/>. 
 */

#ifndef fesslix_flxfem_elements_H
#define fesslix_flxfem_elements_H

#include "flxfem_base.h"
#include "flxfem_legendrePolyH.h"

/**
* @brief class dealing with element information
*/
class FLXFEM_EXPORT FlxElementInfo {
  public:
    enum elInfo { Index, lx, ly, lz, gx, gy, gz, E, nu, rho, G, lambda, w, wx, wy, wz, wx_l, wy_l, wz_l, rx, ry, rz, A, Iy, Iz, It, t, NormalF, Moment_x, Moment_y, Moment_z, sigma_max, sigma_min };
  private:
  public:
    static const FlxElementInfo::elInfo get_elInfo(const std::string& strV, bool errSerious = true);
    static const std::string get_elInfo(const elInfo eI);
};

/**
* @brief base class for 1D-elements
*/
class FLXFEM_EXPORT flxElement_base : public FlxBoxBase2 {
  protected:
    static const tdouble *LX;
    static const tdouble *GX;
    static FlxRFBox *RFbox;
    
    flxMaterial* material;
    
    /**
    * @brief update global constants - using local coordinates (try to avoid this function ... use dimension-bound variant instead)
    */
    virtual void update_globalConst(const tdouble& lx, const tdouble& ly, const tdouble& lz) const = 0;
    /**
    * @brief returns the global displacement vector at the given local coordinates (try to avoid this function)
    * @param w pointer to an array of length 3
    */
    virtual void get_displacement_vector(tdouble *w, const tdouble& lx, const tdouble& ly, const tdouble& lz) const;
    /**
    * @brief returns the requested element-information (supports get_el_info) ... NOTE: call get_el_info instead !!!
    */
    virtual const tdouble get_el_info_help(const FlxElementInfo::elInfo eI) const;
    /**
    * @brief returns the dimension of the element
    */
    virtual const tuint get_dim() const = 0;
    /**
    * @brief activates the geometrical element of this element (needed for calculations with random fields)
    */
    virtual void activate_geoEl() const = 0;
    /**
    * @brief returns true, if the current point is within the element-domain
    */
    virtual const bool is_in_domain() const { return true;}
  public:
    flxElement_base(flxMaterial* material) : material(material) {}
     virtual ~flxElement_base() {}
    /**
    * @brief returns element information
    */
    const bool get_el_info(const tuint index, const tdouble& lx, const tdouble& ly, const tdouble& lz, const std::vector<FlxElementInfo::elInfo>& cVec, flxVec& rVec) const;
    /**
    * @brief function used by FlxObject 'elplot'
    */
    void plot(const tuint idx, std::ostream& os, const std::vector<FlxElementInfo::elInfo>& V, FlxMtxConstFun* ppdim, const int prec, const int fixW) const;
    
    static void set_Const();
    static void set_RFbox(FlxRFBox *RFboxV);
};

/**
* @brief base class for 1D-elements
*/
class FLXFEM_EXPORT flxElement_1D_base : public flxElement_base {
  protected:
    flxElement_1D_base* next;
    flxEdge& edge;
    flxCrossSec* crossSec;
    
    void update_globalConst(const tdouble& lx, const tdouble& ly, const tdouble& lz) const;
    void get_displacement_vector(tdouble *w, const tdouble& lx, const tdouble& ly, const tdouble& lz) const;
    virtual const tdouble get_el_info_help(const FlxElementInfo::elInfo eI) const;
    const tuint get_dim() const { return 1; }
    void activate_geoEl() const;
  public:
    flxElement_1D_base(flxMaterial* material, flxCrossSec* crossSec, flxEdge& edge);
    virtual ~flxElement_1D_base();
    
    virtual void assembleStf(const tdouble* meshInterval, const tuint meshInterval_size);
   
};


/**
* @brief base class for 2D-elements
*/
class FLXFEM_EXPORT flxElement_2D_base : public flxElement_base {
  protected:
    flxElement_2D_base* next;
    flxFace& face;
    FlxFunDeg* t;	// thickness
    
    void update_globalConst(const tdouble& lx, const tdouble& ly, const tdouble& lz) const;
    void get_displacement_vector(tdouble *w, const tdouble& lx, const tdouble& ly, const tdouble& lz) const;
    virtual const tdouble get_el_info_help(const FlxElementInfo::elInfo eI) const;
    virtual const bool is_in_domain() const;
    const tuint get_dim() const { return 2; }
    void activate_geoEl() const;
  public:
    flxElement_2D_base(flxMaterial* material, FlxFunDeg* t, flxFace& face);
    virtual ~flxElement_2D_base();
    
    virtual void assembleStf(const tuint meshIntLx, const tuint meshIntLy, tuint **mesh, tdouble **mesh_cc, const tdouble* meshXLx, const tdouble* meshYLy, flxStiffnessMtxBase& stfmtx);
    virtual void assembleStfHfem(flxStiffnessMtxBase& stfmtx);
};


// ------------------------------ Element - List  ----------------------------------------------------

/**
* @brief contains ALL defined elements 
* @note memory is not managed in this list -> contained elements are NOT deleted !!!
*/
class FLXFEM_EXPORT flxElementList : public FlxBoxBase {
  private:
    std::map<tuint, flxElement_base*> box;
  public:
    void insert (const tuint index, flxElement_base* element);
    flxElement_base* get(const tuint index, bool errSerious=true) const;
    /**
    * @brief output Element properties
    */
    void plot(std::ostream& sout, const std::vector<FlxElementInfo::elInfo>& V, FlxMtxConstFun* ppdim, FlxSMtx* elmtx, const int prec, const int fixW);
};


/**
* @brief controls the degree-detection of functions
*/
class FLXFEM_EXPORT flxFun_DegreeDetect : public FlxReaderBase2, public FlxBoxBase {
  private:
    bool do_autoDetect;
    FlxFunction* degmax;
    FlxFunction* testmax;
    FlxFunction* errmax;
  public:
    flxFun_DegreeDetect(const flxFun_DegreeDetect& ref);
    flxFun_DegreeDetect(bool errSerious=true);
    flxFun_DegreeDetect(bool do_autoDetect, FlxFunction* degmax, FlxFunction* testmax, FlxFunction* errmax);
    ~flxFun_DegreeDetect();
    flxFun_DegreeDetect& operator=(const flxFun_DegreeDetect& ref);
    /**
    * @brief preform the degree-detection (based on 'lx')
    * @param deg returns the required degree of integration
    * @param funI the function to integrate (of which to detect the degree)
    * @param coord_lx_start the lower bound of the integral
    * @param coord_lx_end the uppero bound of the integral
    * @return true: if an approximation below the specified error is possible; false: otherwise
    */
    const bool do_detection(tuint& deg, FunBase* funI, const tdouble& coord_lx_start, const tdouble& coord_lx_end);
    
    const bool is_auto_detect() { return do_autoDetect; }
    /**
    * @brief returns the detector stored as a string
    */
    std::string write();
};





#endif // fesslix_flxfem_elements_H
