#ifdef WIN32
#pragma warning (disable : 4786)
#endif

#include "Information.h"

#include <list>
#include <string>

#include "Path.h"
#include "Project.h"
#include "Model.h"
#include "Model_kit.h"
#include "Atom.h"
#include "Molecule.h"
#include "mm.Core/Atom_groups/Molecular_sequence.h"
#include "Molecular_type.h"
#include "File_text_input.h"
#include "Mach_eps.h"

#include <math.h>

namespace
{
    std::list <std::string> & problem_list ()
    {
        static std::list <std::string> instance;
        return instance;
    }
}

namespace MM
{

Information & Information::
singleton ()
{
    static Information instance;
    return instance;
}

void Information::
warning (Text const& message)
{
    using namespace std;
    string str ("<p><font color=#707000>");
    
    /*if (Project::singleton().model_count() != 0)
    {
        str += Project::singleton().current_model().file_name().c_str();
        str += "<br/>";
    }//*/

    str += message.c_str();
    str += "</font></p>";
    problem_list ().push_back (str);
}

void Information::
error (Text const& message)
{
    using namespace std;
    string str ("<p><font color=#800000><u>");

    /*if (Project::singleton().model_count() != 0)
    {
        str += Project::singleton().current_model().file_name().c_str();
        str += "<br/>";
    }//*/
    
    str += message.c_str();
    str += "</u></p>";
    problem_list().push_back (str);
}

void Information::
session (Text &result)
{
    result += "<table border=1 cellpadding=3 bgcolor=#F8F8F2>";

    result += "<tr><td>Working Directory</td><td>";
    result += Path::working ();
    result += "</tr>";

    result += "<tr><td>bin</td><td>";
    result += Path::bin();
    result += "</tr>";

    result += "<tr><td>Residues Repository</td><td>";
    result += Path::repository();
    result += "</tr>";

    result += "<tr><td>Protocols</td><td>";
    result += Path::protocol();
    result += "</tr>";

    result += "<tr><td>Force Fields</td><td>";
    result += Path::force_field();
    result += "</tr>";

    result += "</table>";
}

void Information::
project (Text &result)
{
    result += "<table border=1 cellpadding=3 bgcolor=#F8F8F2>";
    for (int i=0;  i<Project::singleton().model_count();  ++i)
    {
        Model const & model = Project::singleton().model (i);

        if (&model == &Project::singleton().current_model())
            result += "<tr bgcolor=#C0E0FF><td>";
        else
            result += "<tr><td>";

        result += model.kit().file().file_name(); 
        result += "</td><td>";

        double mass = 0;
        for (int j=0;  j<model.atom_count();  ++j)
        {
            mass += model.atom(j).mass();
        }
        result += mass;
        result += " Dalton</td><td>";
        
        result += model.atom_count();
        result += " atoms</td><td>";
        result += model.bond_count();
        result += " bonds</td><td>";
        result += model.molecule_count();
        result += " molecules</td>";

        if (model.kit().has_force_field())
        {
            result += "<td>";
            result += model.kit().force_field().name();
            result += "</td>";
        }

        result += "</tr>";
    }
    result += "</table>";
}

//table: atoms, bonds, molecules, total mass, force field, title
void Information::
model (Text &result)
{
    if (Project::singleton().model_count() == 0)
        return;

    int i;
    Model     & model = Project::singleton().current_model();
    Model_kit & kit   = model.kit();

    result += "<table border=1 cellpadding=3 bgcolor=#F8F8F2>";
    result += "<tr><td>File</td><td>";
    result += model.kit().file().file_name();    
    result += "</td></tr>";

    result += "<tr><td>Boundary conditions</td><td>";
    result += kit.boundary_conditions().type_name();
    result += "</td></tr>";

    if (kit.has_crystal_cell ())
    {
        Box const& cell = kit.crystal_cell();

        result += "<tr><td>Crystal cell</td><td>";
        result +=  "a=";  result += cell.a_mod(); result+="&nbsp;&nbsp;&nbsp;";
        result += " b=";  result += cell.b_mod(); result+="&nbsp;&nbsp;&nbsp;";
        result += " c=";  result += cell.c_mod();

        result += "<br/>";
        //result += "  ";

        result +=" alpha="; result+=cell.alpha(); result+="&nbsp;&nbsp;&nbsp;";
        result +=" beta=";  result+=cell.beta();  result+="&nbsp;&nbsp;&nbsp;";
        result +=" gamma="; result+=cell.gamma();

        result += "</td></tr>";
    }

    if (kit.has_force_field())
    {
        result += "<tr><td>";
        result += "Force Field";
        result += "</td><td>";
        result += kit.force_field().name();
        result += "</td></tr>";
    }

    double mass = 0;
    for (i=0;  i<model.atom_count();  ++i)
    {
        mass += model.atom(i).mass();
    }
    result += "<tr><td>Mass</td><td>";
    result += mass;
    result += " Dalton</td></tr>";

    result += "</table>";
    
    result += "<p>";
    result += model.atom_count();
    result += " atoms &nbsp;&nbsp;&nbsp;";
    result += model.bond_count();
    result += " bonds &nbsp;&nbsp;&nbsp;";
    result += model.molecule_count();
    result += " molecules";
    result += "</p>";

    //result += "\nMolecules:\n";

    result += "<table border=1 cellpadding=3 bgcolor=#F8F8F2>";
    //Molecular_type_sequence const& type = kit.molecular_type_sequence ();
    Own <Molecular_sequence> seq(Molecular_sequence::create(model));
    //type().update();

    for (i=0;  i<seq().count();  ++i)
    {
        Molecular_type & type   = seq().type(i);
        int              count  = type.molecule_count();
        double           charge = 0.;

        for (int j=0;  j<count;  ++j)
            charge += type.molecule(j).charge();

        charge = fabs (charge) < f_mach_eps_2 ? 0 : charge;

        result += "<tr><td><h3>";
        result += type.molecule(0).molecular_formula (Molecule::HTML);
        result += "</h3></td><td>";
        result += type.molecule_count();
        result += " molecules</td><td>";
        result += type.molecule(0).mass();
        result += " Dalton</td><td>";
        result += charge / count;
        result += " electrons";
        result += "</td></tr>";
    }
    result += "</table>";
}

void Information::
//selected (Text &result)
selected (Text &)
{
}

void Information::
problems (Text &result)
{
    using namespace std;
    
    for (list <string>::iterator it=problem_list().begin();  
         it!=problem_list().end();  ++it)
    {
        result += it->c_str();
    }
}

void Information::
log (Text &result)
{
    File_text_input in (Path::report() + "_log.txt");

    char buffer[1000];

    while (in.next_line (buffer, 999))
        result += buffer;
}

void Information::
add_log  (Text const& log)
{
    log_ += log;
    next_log_.publish();
}

void Information::
on_log   (Command *adoptee, bool on)
{
    next_log_.subscribe (adoptee, on);
}

}//MM
