#ifndef MODEL_KIT_H
#define MODEL_KIT_H

//#ifndef MODEL_H
//#include "model.h"
//#endif

#ifndef REPLICA_H
#include "Replica.h"
#endif

#ifndef DBC_H
#include "DbC.h"
#endif

#ifndef OWN_H
#include "Own.h"
#endif

#ifndef STORABLE_H
#include "Storable.h"
#endif

#ifndef SELECTOR_H
#include "Selector.h"
#endif

#ifndef MODEL_VIEW_COMPOSITE_H
#include "Model_view_composite.h"
#endif

//#ifndef M_DYNAMIX_MOLECULAR_TYPE_H
//#include "M_DynaMix_molecular_type.h"
//#endif

#ifndef ARRAY_OF_OBJ_H
#include "Array_of_obj.h"
#endif

#ifndef BOUNDARY_CONDITIONS_H
#include "Boundary_conditions.h"
#endif

#ifndef BOX_H
#include "Box.h"
#endif

//#ifndef PERIODIC_BOUNDARY_CONDITIONS_H
//#include "Periodic_boundary_conditions.h"
//#endif

#ifndef CRYSTAL_BUILDER_H
#include "Crystal_builder.h"
#endif

#ifndef SEQUENCE_EDITOR_H
#include "Sequence_editor.h"
#endif

#ifndef ADD_BUILDER_H
#include "Add_builder.h"
#endif

#ifndef MD_SIMULATOR_H
#include "MD_simulator.h"
#endif

#ifndef TITLE_H
#include "Program_title.h"
#endif

#ifdef M_DYNAMIX
    #ifndef M_DYNAMIX_SIMULATOR_H
    #include "M_DynaMix_simulator.h"
    #endif
#endif

#ifndef RIGID_BODY_H
#include "Rigid_body.h"
#endif

#ifndef COMPOSITE_INTERACTION_H
#include "Composite_interaction.h"
#endif

#ifndef ARBITRARY_ATOM_GROUP_H
#include "Arbitrary_atom_group.h"
#endif

//#ifndef FORCE_FIELD_H
//#include "Force_field.h"
//#endif

#ifndef MODEL_EVENT_H
#include "Model_event.h"
#endif

//#ifndef MOLECULAR_TYPE_SEQUENCE_H
//#include "Molecular_type_sequence.h"
//#endif

#include <map>

namespace MM
{
//class Model;
class MD_subject;
class Prototype;

class Model_kit : protected DbC
{
    Text                                    short_alias_;
    Text                                    alias_;
    //Text                                    model_name_;
    Text                                    comment_line_;
    Text                                    file_name_;

    Model &                                 model_;
    Replica                                 replica_;
    Model_event                             event_;
    Storable                                file_;
    Model_view_composite                    visual_;
    Own <Composite_interaction>             interaction_;
    Selector                                selector_;

    //Array_of_obj <M_DynaMix_molecular_type> m_DynaMix_molecular_type_;

    Own <Crystal_builder>                   crystal_builder_;
    Own <Sequence_editor>                   sequence_editor_;
    Own <Add_builder>                       add_builder_;

    Own <MD_simulator>                      MD_;
    Own <MD_simulator>                      MC_;
#ifdef M_DYNAMIX
    Own <M_DynaMix_simulator>               MDynaMix_;
#endif
    Simulator *                             current_simulator_;

    mutable Own <Box>                       crystal_cell_;
    Boundary_conditions                     boundary_conditions_;
    //Periodic_boundary_conditions            pbc_;
    Own <Rigid_body>                        rigid_body_;

    Own <Arbitrary_atom_group>              ordered_atoms_;
    //Own <Molecular_type_sequence>           molecular_type_sequence_;

    int                                     lock_count_;

public:

    std::map <Text, Text>                   MDynaMix_file_;

    class Lock
    {
        Model_kit &                         kit_;
    public:
        Lock (Model_kit &kit) : kit_(kit)   {kit_.lock_count_++;}
       ~Lock ()                             {kit_.lock_count_--;}
    };
    friend class MM::Model_kit::Lock;

    explicit                        Model_kit (Model &model);
    explicit                        Model_kit (Prototype &, Model &model);

    Model &                         model   () {return model_;}
    Replica &                       replica () {return replica_;}

    //bool                            has_model_name   () const                   {return !model_name_.is_empty();}
    bool                            has_short_alias  () const                   {return !short_alias_.is_empty();}
    bool                            has_alias        () const                   {return !alias_.is_empty();}
    bool                            has_comment_line () const                   {return !comment_line_.is_empty();}
    bool                            has_force_field  () const                   {return replica_.has_force_field();}

    //Text const &                    model_name   () const {return model_name_;}
    Text const &                    short_alias  () const {return short_alias_;}
    Text const &                    alias        () const {return alias_;}
    Text const &                    comment_line () const {return comment_line_;}
    Force_field &                   force_field  ()                             {REQUIRE ("", replica_.has_force_field  ()); return replica_.force_field();}
    Force_field const &             force_field  () const                       {REQUIRE ("", replica_.has_force_field  ()); return replica_.force_field();}

    Text const &                    file_name () const    {return file_name_;}
    void                            set_file_name (Text const &name)            {file_name_ = name;}

    //void                            set_model_name    (Text const &);
    void                            set_short_alias   (Text const &);
    void                            set_alias         (Text const &);
    void                            set_comment_line  (Text const &);
    void                            set_force_field   (Text const &);

    Storable &                      file ()             {return file_;}
    Storable const &                file () const       {return file_;}

    void                            strip (bool silent = false);

    Model_view_composite &          visual ();    //fix to visual
    Model_view_composite const &    visual () const;
    bool                            has_visual () const;
    void                            add_visual (Model_view * adoptee);
    //void                        remove_view();         //fix remove_all_views

    Selector &                      selector ()                                 {return selector_;}


    //bool                            has_interaction () const                  {return interaction_.is_valid();}
    Composite_interaction &         interaction ()                              {return interaction_();}
    Composite_interaction const &   interaction () const                        {return interaction_();}
    void                            adopt_interaction (Interaction * in)        {interaction_().adopt (in);}
    void                            clear_interactions ()                       {interaction_().clear ();}

    /*bool                            has_md_simulator () const            {return md_simulator_.is_valid();}
    MD_simulator &                  md_simulator ()                      {return md_simulator_();}
    MD_simulator const &            md_simulator () const                {return md_simulator_();}
    void                            adopt_md_simulator (MD_simulator * s){md_simulator_.adopt(s);}//*/

    //Array_of <M_DynaMix_molecular_type> &      m_DynaMix_molecular_type ();
    //const Array_of <M_DynaMix_molecular_type>& m_DynaMix_molecular_type ()const;

    Crystal_builder &               crystal_builder  ();
    Add_builder &                   add_builder      ();

    Simulator &                     simulator ()                                {return *current_simulator_;}
    void                            set_simulator (Simulator & sim)             {current_simulator_ = &sim;}
    MD_simulator &                  MD ()                                       {return MD_();}
    MD_simulator &                  Monte_Carlo ()                              {return MC_();}

#ifdef M_DYNAMIX
    M_DynaMix_simulator &           MDynaMix ()                                 {return MDynaMix_();}
#endif
    void                            set_MDynaMix_file (Text const& formula, 
                                                       Text const& path);
    Text                            MDynaMix_file     (Text const& formula);

    Sequence_editor &               sequence_editor  ();

    //Periodic_boundary_conditions &  pbc ( )           {return pbc_;}
    bool                            has_crystal_cell () const                   {return crystal_cell_.is_valid();}
    Box &                           crystal_cell     ();
    Box const&                      crystal_cell     () const;

    Boundary_conditions &           boundary_conditions ()                      {return boundary_conditions_;}
    Boundary_conditions const &     boundary_conditions () const                {return boundary_conditions_;}

    bool                            has_ordered_atoms () const                  {return ordered_atoms_.is_valid();}
    Arbitrary_atom_group &          ordered_atoms     ();

   // Molecular_type_sequence &       molecular_type_sequence ()                  {return molecular_type_sequence_();}
   // Molecular_type_sequence const & molecular_type_sequence () const            {return molecular_type_sequence_();}

    Rigid_body &                    rigid_body ()                               {return rigid_body_();}

    MD_subject *                    create_md_interface ();

    Model_event &                   event ()        {return event_;}
    Model_event const &             event () const  {return event_;}

    void                            erase (Model_view &adoptee);

    bool                            is_locked () const                          {REQUIRE("",lock_count_>=0); return lock_count_>0;}
    Lock                            lock () {return Lock (*this);}
};

inline Model_view_composite & Model_kit::
visual ()
{
    //REQUIRE ("", has_visual());
    return visual_;
}

inline Model_view_composite const & Model_kit::
visual () const
{
    //REQUIRE ("", has_visual());
    return visual_;
}

inline bool Model_kit:: 
has_visual () const
{
    return visual_.count() != 0;
}
//*/
/*inline Array_of <M_DynaMix_molecular_type> & Model_kit::
m_DynaMix_molecular_type ()
{
    return m_DynaMix_molecular_type_;
}

inline const Array_of <M_DynaMix_molecular_type> & Model_kit::
m_DynaMix_molecular_type () const
{
    return m_DynaMix_molecular_type_;
}//*/

}//MM

#endif //MODEL_KIT_H
