#include "Storable.h"

#include "Log.h"
#include "User.h"
#include "File_text_output.h"
#include "Path.h"

#include "Flaw.h"
#include "Model.h"//fix remove

#include "Model_kit.h"
#include "Atom.h"
#include "Atom_kit.h"
#include "Pull_on_builder.h"
#include "Default_builder.h"
#include "Bond_detector.h"
#include "Molecular_detector.h"
#include "Timer.h"
#include "MolMeccano_file.h"

#include <string>
//#include <iostream>


namespace MM
{
Storable::
Storable (Model & model)
:
    model_(model)
{
//    setup_current_file ("XYZ", "new.xyz");
	current_file_.adopt (new MolMeccano_file (model));
}

bool Storable::
was_defined () const
{
    REQUIRE ("", current_file_.is_valid());
    return current_file_().name () != "New.mlm";
}

Text Storable::
file_name () const
{
    REQUIRE ("", current_file_.is_valid());

    return current_file_().name ();
}

Text Storable::
extension () const
{
    REQUIRE ("", current_file_.is_valid());

    return current_file_().extension (0);
}

void Storable::
add (Text const & file_name, Text const & file_type, bool is_loading)
{
    Text type = file_type;
    if (file_type == "auto detect")
        type = detect_file_type (file_name);

    if (type == "unknown")
    {
        Text message;
        message += "Can not detect format of file '";
        message += file_name;
        message += "'.";
        to_user().error (message);
        return;
    }

    Timer time;
    Text  status (file_name);
    status += "     ";

    model_.clear_local_ids ();

    Own <Model_file> base_file;
    if (current_file_.is_valid())
        base_file.adopt (current_file_.orphan());

    setup_current_file (type);
    current_file_().adopt_builder (new Default_builder);

    try
    {
        current_file_().add (file_name);
    }
    catch(Text &e)
    {
        to_user().error (e);
    }

    status += Text (time.elapsed(), 2);
    status += " sec, ";
    time.restart();

//    if (current_file_->model().kit().has_view())             //fix move
//        current_file_->model().kit().view().fit_to_screen();

    if (!current_file_().support_bonds ())
    {
        Bond_detector bond_detector (model());
        bond_detector.execute();
    }
    status += "bonds ";
    status += Text (time.elapsed(), 2);
    status += " sec, ";
    time.restart();

    if (!current_file_().support_molecules ())
    {
        Molecular_detector molecular_detector (model());
        molecular_detector.execute();
    }
    status += "molecules ";
    status += Text (time.elapsed(), 2);
    status += " sec.\n";
    time.restart();

    //to_user().status (status);
    //log() << status;
    //std::cout << status.c_str() << std::endl;
    File_text_output out(Path::report() +"Storable.txt");   //fix
    out.line (status);

    #ifndef DEBUG
    int atom_count = current_file_().model().atom_count();
    for (int n=0;  n<atom_count;  ++n)
        current_file_().model().atom(n).kit().set_number(-1);
    #endif

    if (!is_loading)
    {
        if (base_file.is_valid())
            current_file_.adopt (base_file.orphan());
        else
            current_file_.adopt (0);
    }

    if (model().atom_count () > 0 && !model().kit().boundary_conditions().is_a_box())
        model().kit().crystal_builder().fit_box();

    current_file_().model().kit().visual().renew();
}

void Storable::
pull_on (Text const & file_name, Text const & file_type)
{
    Text type = file_type;
    if (file_type == "auto detect")
        type = detect_file_type (file_name);

    if (type == "unknown")
    {
        Text message;
        message += "Can not detect format of file '";
        message += file_name;
        message += "'.";
        to_user().error (message);
        return;
    }

    Own <Model_file> base_file;
    if (current_file_.is_valid())
        base_file.adopt (current_file_.orphan());

    setup_current_file (type);
    current_file_().adopt_builder (new Pull_on_builder);

    try
    {
        current_file_().add (file_name);
    }
    catch(Text &e)
    {
        to_user().error (e);
    }

    if (!current_file_().support_bonds ())
    {
        Bond_detector bond_detector (model());
        bond_detector.execute();
    }

    if (!current_file_().support_molecules ())
    {
        Molecular_detector molecular_detector (model());
        molecular_detector.execute();
    }

    #ifndef DEBUG
    int atom_count = current_file_().model().atom_count();
    for (int n=0;  n<atom_count;  ++n)
        current_file_().model().atom(n).kit().set_number(-1);
    #endif

    if (base_file.is_valid())
        current_file_.adopt (base_file.orphan());
    else
        current_file_.adopt (0);
}

/*void Storable::
clear() //fix
{
    current_file_().clear();
}//*/

void Storable::
load (Text const & file_name, Text const & file_type)
{
    /*if (current_file_.was_initialized())
    {
        clear ();
    }//*/
    model().clear();
    current_file_.adopt (0);
    //current_file_().model().kit().set_file_name (file_name);
    model().kit().set_file_name (file_name);
    add (file_name, file_type, true);
}

Text const & Storable::
detect_file_type (Text const & file_name, Text * extension)
{
//    log() << "detect_file_type (" << file_name << ")\n";

    Text tmp = file_name;
    tmp = tmp.to_lower ();
    std::string low_case_file_name = tmp.c_str();

//    log() << "to_lower() " << tmp << "\n";

    int k = Model_file::prototype_count();

    for (int i=0;  i<k;  ++i)
    {
        Model_file const & prototype = Model_file::prototype (i);
        int  n = prototype.extension_count();

        for (int j=0;  j<n;  ++j)
        {
            Text current_extension(".");
            current_extension += prototype.extension(j);
            
            if (low_case_file_name.rfind (current_extension.c_str()) 
                != std::string::npos )
            {
                if (extension != 0)
                {
                    //log() << "*extension " << (*extension) << "\n";
                    *extension = current_extension.c_str();
                }

                //log() << "prototype.type_name() " << prototype.type_name() << "\n";

                return prototype.type_name();
            }
        }
    }

    //log() << "END\n\n";

    static Text result("unknown");
    return result;
}

void Storable::
setup_current_file (Text const & file_type)
{
    current_file_.adopt (
        Model_file::prototype(file_type).clone(model()));
}

bool Storable::
save (bool to_comment)
{
    current_file_().save (to_comment);
    return true; //fix
}

bool Storable::
save_as (Text const & file_name, Text const & file_type,
         Text * name_saved, bool to_comment)
{
    using namespace std;
    Text type = file_type;
    Text name = file_name;
    Text extension;

    if (file_type == "auto detect")
    {
        type = detect_file_type (file_name);
    }
    else
    {
        type = detect_file_type (file_type, &extension);

        if (type == "unknown")
            type = detect_file_type (file_name);
        else
        {
            string new_name, old_name = file_name.c_str(); 
            
            string::size_type point = old_name.rfind ('.');
            if (point == string::npos)
                new_name = old_name + extension.c_str();
            else
            {
                string basename = old_name.substr (0, point);
                string extname  = old_name.substr (point + 1);
                new_name  = basename;
                new_name += extension.c_str();


                /*if (extname.empty())
                {
                    new_name = old_name + type.c_str();
                }
                else
                {
                    new_name = old_name;
                    new_name.replace (point + 1, string::npos, type.c_str());
                    name = new_name.c_str();
                }//*/
            }
            name = new_name.c_str();
        }
    }

    model().kit().set_file_name (name);

    //Text file_type = detect_file_type (file_name);
    //if (file_type == "unknown")
    if (type == "unknown")
    {
        Text message;
        message += "Can not detect type of file ";
        message += file_name;
        message += ".\n";
        message += "File does not saved";
        message += ".";
        to_user().error (message);
        return false;
    }
    
    try
    {
        string _type = type.c_str(); 
        setup_current_file (_type.c_str());
    }
    catch (Flaw & error)
    {
        Text message;
        message += "Can not save file ";
        message += file_name;
        message += ".\n";
        message += "File type - ";
        message += type;
        message += ".\n\n";
        message += "System: ";
        message += error.message();
        to_user().error (message);
        return false;
    }

    current_file_().save (to_comment);
    if (name_saved != 0)
        *name_saved = name;

    return true;
}
}//MM




