#include "M_DynaMix_Q4Process.h"

#include "Log.h"

#include "User.h"
#include "Path.h"
#include "Interface_for_M_DynaMix_process.h"
#include "Atom.h"
#include "Atom_group.h"
#include "Text_as_text_output.h"
#include "File_text_input.h"
#include "Collect_potential_from_mDynaMix.h"
#include "Collect_temperature_from_mDynaMix.h"
#include "Collect_pressure_from_mDynaMix.h"
#include "Collect_density_from_mDynaMix.h"
#include "Point_3D_impl.h"

#include <QApplication>//fix remove
#include <QTextStream>

#include <sstream>

namespace MM
{

M_DynaMix_Q4Process::
M_DynaMix_Q4Process (Interface_for_M_DynaMix_process & simulator)
:
    M_DynaMix_process   (simulator),
    state_              (none),
    process_            (new QProcess (this)),
    //janitor_            (process_),//fix ?
    ordered_atoms_      (0),//fix check validity
    output_             (Path::MDynaMix() + "MDynaMix.output" /*simulator().output_file_name()*/),
    //output_             (Path::report() + "MDynaMix.output" /*simulator().output_file_name()*/),
    current_atom_number_(0)
{
}

void M_DynaMix_Q4Process::
start (Atom_group * ordered_atoms)
{
    to_user(). status ("Launch MDynaMix.");
    to_user(). info ("\n___________________________________________");
    state_ = none;

    if (output_.isOpen ())
        output_.close ();

    if (!output_.open (QIODevice::WriteOnly/* | QIODevice::Text*/))
    {
        to_user().error ("Can not open 'output' file.");     
        return;
    }

    //Text filename;
    //filename += Path::MDynaMix();
    //filename += simulator().output_file_name();
    //output_.adopt (new File_text_output (filename));

    comment_ = Text();

    QString     program (Path::MDynaMix());
    //Text args (Path::MDynaMix());   //fix  make the same in pipe
    #ifdef MM_WIN32
         //args += "MDynaMix.exe";
        program += "MDynaMix.exe";
    #else
         //args += "md";
        program += "md";
    #endif

    //QStringList arguments;

    //process_->clearArguments ();
    //process_->addArgument (args.c_str());

    //connect (process_, SIGNAL (readyReadStdout  ()),    //fix move
    //         this,     SLOT   (read_from_stdout ()));

    //connect (process_, SIGNAL (processExited  ()),
    //         this,     SLOT   (read_from_stdout ()));

    process_->setReadChannel (QProcess::StandardOutput);
    process_->setWorkingDirectory (Path::MDynaMix());

    connect (process_,          SIGNAL(started()),
             this,              SLOT  (started()));

    connect (process_,          SIGNAL(finished (int, QProcess::ExitStatus)),
             this,              SLOT  (finished (int, QProcess::ExitStatus)));

    connect (process_,          SIGNAL(readyRead()),
             this,              SLOT  (read()));


    ordered_atoms_ = ordered_atoms;

    //Text_as_text_output input;
    //File_text_input     file ("MDynaMix.input"); //fix in all places
    //const int           bufsize = 255;
    //char                buf [bufsize];

    //while (file.next_line (buf, bufsize))
    //    input.text(buf);

    process_->setStandardInputFile (Path::MDynaMix() + "MDynaMix.input");

    //bool success = process_->launch (QString(input.c_str()));
    //if (!success)
    //    to_user().error ("Could not start M.DynaMix.");


    process_->start (program);

    bool started = process_->waitForStarted (3000);
    if (!started)
        to_user().fatal_error ("Could not start MDynaMix.");

    //while (process_->isRunning () || process_->canReadLineStdout ())
    //{
    //    qApp->processEvents (QEventLoop::AllEvents, 1);
    //}
    //for (int i=0;  i<10000;  ++i)
    //    qApp->processEvents (QEventLoop::AllEvents, 10);

    
    process_->waitForFinished();
}

//void M_DynaMix_QProcess::
//stop ()
//{
//    process_->kill();
//}

void M_DynaMix_Q4Process::
started ()
{
    to_user().status ("MDynaMix started");
    state_ = none;
}

void M_DynaMix_Q4Process::
finished (int, QProcess::ExitStatus exitStatus)
{
    if (exitStatus == QProcess::CrashExit/* && !user_stop_flag_*/)//fix
    {
        Text message ("MDynaMix crashed. ");
        to_user ().status (message);

        switch (process_->error ())
        {
        case QProcess::FailedToStart:
            to_user ().error (message +
                "The process failed to start. "
                "Either the invoked program is missing, or you may have "
                "insufficient permissions to invoke the program.");
            break;

        case QProcess::Crashed:
            to_user ().error (message +
                "The process crashed some time after "
                "starting successfully.");
            break;

        case QProcess::Timedout:
            to_user ().error (message + "Timed out");
            break;

        case QProcess::WriteError:
            to_user ().error (message +
                "An error occurred when attempting to write to the process.");
            break;

        case QProcess::ReadError:
            to_user ().error (message +
                "An error occurred when attempting to read from "
                "the process. For example, the process may not be running.");
            break;

        case QProcess::UnknownError:
            to_user ().error (message + "An unknown error occurred.");
            break;
        }
    }
    else
    {
        //read ();
        output_.flush ();

        Text message ("MDynaMix finished ");
        //if (success_)                         //fix
            message += "NORMALLY";
        //else
        //    to_user ().warning ("Execution of MDynaMix terminated ABNORMALLY.");

        to_user ().status (message);
    }

    read ();
    state_ = none;
}

void M_DynaMix_Q4Process::
read ()
{
    REQUIRE ("", ordered_atoms_ != 0);

    char    buf[1024];

    static bool reinter_flag = false;
    if (reinter_flag)
    {
        log() << "# reinter #";
        return;
    }
    reinter_flag = true;

    //QString result;

    //while (process_->canReadLineStdout ())
    while (process_->canReadLine ())
    {
        //result = process_->readLineStdout ();
        qint64 bytes = process_->readLine (buf, sizeof(buf));

        //if (!(buf[0] == '@' && buf[1] == 'm' && buf[2] == 'm'))
        //    output_.write (buf, bytes);

#ifdef MM_WIN32
        for (int i=0;  i<1024;  ++i)
        {
            if (buf[i] == '\0')
            {
                buf[i-2] = 0x0A;
                buf[i-2] = '\0';
            }
        }
#endif


        QString result (buf);

        //=====================================================================
        if (state_ != read_coords)
            qApp->processEvents (QEventLoop::ExcludeSocketNotifiers);

        if (result.isNull())
        {
            to_user() .error ("result.isNull()");
            continue;
        }

        if (result[4] == 'c' && result.indexOf ("coord.end") != -1)
        {
            state_ = none;
            continue;
        }

        if (state_ == read_coords)
        {
            double x, y, z;
            //std::string first_word, str = result.toStdString();
            //std::istringstream in (str);
            QString first_word;
            QTextStream in (&result);

            in >> first_word >> x >> y >> z;
            
#ifndef NDEBUG
            if (first_word != "@mm")
            {
                to_user (). error (Text ("Can not read coords from '")
                     + Text (result) + "'.");
                simulator().stop();
                break;
            }
#endif
        
            //if (model().atom_count() !=0)
            CHECK ("", ordered_atoms_->atom_count() != 0);
            //if (in.good())
            //if (!in.fail())
            if (in.status() == QTextStream::Ok)
            {
                Atom &current_atom = ordered_atoms_->atom (current_atom_number_);
                //current_atom.set_x (x);
                //current_atom.set_y (y);
                //current_atom.set_z (z);
                current_atom.set_position (Point_3D_impl (x, y, z));
                //fix to model().set_coords;
            }
        
            ++current_atom_number_;
            continue;
        }

        if (result.indexOf ("@mm") == -1)
        {
            if (result.indexOf ("!!!") != -1)
            {
                if (result.indexOf ("!!! error in input file") != -1)
                {
                    //output_().line (result.latin1());
                    output_.write (buf, bytes);
                    comment_ += result;
                    comment_ += " : ";
            
                    //result = process_->readLineStdout ();
                    bytes = process_->readLine (buf, sizeof(buf));
                    ////output_ << result.latin1() << "\n";
                    //output_().line (result.latin1());
                    output_.write (buf, bytes);
                    comment_ += result;
                    comment_ += " : ";
            
                    //result = process_->readLineStdout ();
                    bytes = process_->readLine (buf, sizeof(buf));
                    ////output_ << result.latin1() << "\n";
                    //output_().line (result.latin1());
                    output_.write (buf, bytes);
                    comment_ += result;
                    comment_ += " : ";
            
                    //result = process_->readLineStdout ();
                    bytes = process_->readLine (buf, sizeof(buf));
                    comment_ += result;
                }
                else
                {
                    comment_  = "Warning: ";
                    comment_ += result;
                }
            }
            
            //output_().line (result.latin1());
            //output_().flush();
            output_.write (buf, bytes);
            continue;
        }

        // @mm
        if (result.indexOf ("coord.start") != -1)
        {
            state_ = read_coords;
            current_atom_number_ = 0;
            continue;
        }

        //std::string first_word, second_word, str = result.toStdString();
        //std::istringstream line (str);

        QString first_word, second_word;
        QTextStream line (&result);

        line >> first_word >> second_word;
        
        if (first_word != "@mm")
        {
            to_user (). error (Text () + 
                "No '@mm' prefix in string '" + Text (result) + "'.");
            simulator().stop();
            break;
        }

        if (second_word == "E")
        {
            Text text;
            double value;

            int number;
            line >> number;   text += "step=";            text += number;
            line >> value;    text += "   E.pot=";        text += value;
            simulator().potential_monitor().set_current_value (value);
            line >> value;    text += "   E.bonded=";     text += value;
            line >> value;    text += "   E.el.st=";      text += value;
            line >> value;    text += "   E.full=";       text += value;
            line >> value;    text += "   T=";            text += value;
            simulator().temperature_monitor().set_current_value (value);
            line >> value;    text += "   P=";            text += value;
            simulator().pressure_monitor().set_current_value (value);
            line >> value;    text += "   d=";            text += value;
            simulator().density_monitor().set_current_value (value);

            /*if (line.fail())
            {
                to_user (). error (Text () + 
                    "Can not E from '" + result.latin1() + "'.");
                simulator().stop();
                break;
            }//*/
            //if (line.fail())
            if (line.status() != QTextStream::Ok)
            {
                text = result;
            }

            to_user().status (text);
            simulator().publish_step_finish ();
            //qApp->processEvents (QEventLoop::ExcludeSocketNotifiers);

            comment_ = text;
        }
    }

    reinter_flag = false;
}

}//MM

