// This file is part of fityk program. Copyright (C) Marcin Wojdyr
// Licence: GNU General Public License version 2
// $Id: ui.cpp 322 2007-07-24 00:17:11Z wojdyr $

#include "common.h"
#include "ui.h"
#include "settings.h"
#include <string>
#include <iostream>
#include "logic.h"
#include "cmd.h"

using namespace std;

const char* config_dirname = ".fityk";
const char* startup_commands_filename = "init";

string Commands::Cmd::str() const 
{ 
    string s = cmd + " #>";
    if (status == status_ok)
        s += "OK";
    else if (status == status_execute_error) 
        s += "Runtime Error";
    else //status_syntax_error
        s += "Syntax Error"; 
    return s;
}

void Commands::put_command(string const &c, Commands::Status r)
{
    if (strip_string(c).empty())
        return;
    cmds.push_back(Cmd(c, r));
    ++command_counter;
    if (!log_filename.empty())  
        log << " " << c << endl; 
}

void Commands::put_output_message(string const& s) const
{
    if (!log_filename.empty() && log_with_output) {
        // insert "# " at the beginning of string and before every new line
        log << "# ";
        for (const char *p = s.c_str(); *p; p++) {
            log << *p;
            if (*p == '\n')
                log << "# ";
        }
        log << endl;
    }
}

vector<string> Commands::get_commands(int from, int to, bool with_status) const
{
    if (from < 0)
        from += cmds.size();
    if (to < 0)
        to += cmds.size();
    vector<string> r;
    if (!cmds.empty())
        for (int i = max(from, 0); i < min(to, size(cmds)); ++i) {
            string s;
            if (with_status)
                s = cmds[i].str();
            else
                s = cmds[i].cmd;
            r.push_back(s);
        }
    return r;
}

int Commands::count_commands_with_status(Status st) const
{
    int cnt = 0;
    for (vector<Cmd>::const_iterator i = cmds.begin(); i != cmds.end(); ++i)
        if (i->status == st)
            ++cnt;
    return cnt;
}

string Commands::get_info(bool extended) const
{
    string s = S(command_counter) + " commands since the start of the program,";
    if (command_counter == size(cmds))
        s += " of which:";
    else
        s += "\nin last " + S(cmds.size()) + " commands:";
    s += "\n  " + S(count_commands_with_status(status_ok)) 
              + " executed successfully"
        + "\n  " + S(count_commands_with_status(status_execute_error)) 
          + " finished with execute error"
        + "\n  " + S(count_commands_with_status(status_syntax_error))
          + " with syntax error";
    if (log_filename.empty())
        s += "\nCommands are not logged to any file.";
    else
        s += S("\nCommands (") + (log_with_output ? "with" : "without") 
            + " output) are logged to file: " + log_filename;
    if (extended) {
        // no extended info for now
    }
    return s;
}


void Commands::start_logging(string const& filename, bool with_output,
                             Ftk const* F)
{
    if (filename.empty())
       stop_logging(); 
    else if (filename == log_filename) {
        if (with_output != log_with_output) {
            log_with_output = with_output;
            log << "### AT "<< time_now() << "### CHANGED TO LOG "
                << (log_with_output ? "WITH" : "WITHOUT") << " OUTPUT\n";
        }
    }
    else {
        stop_logging();
        log.clear();
        log.open(filename.c_str(), ios::out | ios::app);
        if (!log) 
            throw ExecuteError("Can't open file for writing: " + filename);
        log << fityk_version_line << endl;
        log << "### AT "<< time_now() << "### START LOGGING ";
        log_with_output = false; //don't put info() below into log 
        if (with_output) {
            log << "INPUT AND OUTPUT";
            F->msg("Logging input and output to file: " + filename);
        }
        else {
            log << "INPUT";
            F->msg("Logging input to file: " + filename);
        }
        log << " TO THIS FILE (" << filename << ")\n";
        log_with_output = with_output;
        log_filename = filename;
    }
}

void Commands::stop_logging()
{
    log.close();
    log_filename = "";
}


//utility for storing lines from script file: line number and line as a string
struct NumberedLine
{
    int nr;  //1 - first line, etc.
    string txt;
    NumberedLine(int nr_, string txt_) : nr(nr_), txt(txt_) {}
};

Commands::Status UserInterface::exec_and_log(string const &c)
{
    Commands::Status r = this->exec_command(c); 
    commands.put_command(c, r); 
    return r;
}

void UserInterface::output_message (OutputStyle style, const string& s) const
{
    if (keep_quiet)
        return;
    show_message(style, s);
    commands.put_output_message(s);
    if (style == os_warn && F->get_settings()->get_b("exit-on-warning")) {
        show_message(os_normal, "Warning -> exiting program.");
        throw ExitRequestedException();
    }
}

/// items in selected_lines are ranges (first, after-last).
/// if selected_lines are empty - all lines from file are executed
void UserInterface::exec_script(const string& filename, 
                                const vector<pair<int,int> >& selected_lines)
{
    ifstream file(filename.c_str(), ios::in);
    if (!file) {
        F->warn("Can't open file: " + filename);
        return;
    }

    vector<NumberedLine> nls, //all lines from file
                         exec_nls; //lines to execute (in order of execution)

    //fill nls for easier manipulation of file lines
    string dir = get_directory(filename);
    string s;
    int line_index = 0;
    while (getline (file, s)) {
        replace_all(s, "_EXECUTED_SCRIPT_DIR_/", dir);
        nls.push_back(NumberedLine(++line_index, s));
    }

    if (selected_lines.empty())
        exec_nls = nls;
    else
        for (vector<pair<int,int> >::const_iterator i = selected_lines.begin(); 
                                            i != selected_lines.end(); i++) {
            int f = max(i->first, 0);  // f and t are 1-based (not 0-based)
            int t = min(i->second, size(nls));
            exec_nls.insert(exec_nls.end(), nls.begin()+f, nls.begin()+t);
        }

    for (vector<NumberedLine>::const_iterator i = exec_nls.begin(); 
                                                    i != exec_nls.end(); i++) {
        if (i->txt.length() == 0)
            continue;
        if (get_verbosity() >= 0)
            show_message (os_quot, S(i->nr) + "> " + i->txt); 
        // result of parse_and_execute here is neglected. Errors in script
        // don't change status of command, which executes script
        parse_and_execute(i->txt);
    }
}

void UserInterface::draw_plot (int pri, bool now)
{
    if (pri <= F->get_settings()->get_e("autoplot")) 
        do_draw_plot(now);
}


int UserInterface::get_verbosity() const
                           { return F->get_settings()->get_e("verbosity"); }


Commands::Status UserInterface::exec_command (std::string const &s)
{ 
    return m_exec_command ? (*m_exec_command)(s) : parse_and_execute(s); 
}

bool is_fityk_script(string filename)
{
    const char *magic = "# Fityk";

    ifstream f(filename.c_str(), ios::in | ios::binary);
    if (!f) 
        return false;

    int n = filename.size();
    if ((n > 4 && string(filename, n-4) == ".fit")
            || (n > 6 && string(filename, n-6) == ".fityk"))
        return true;

    const int magic_len = strlen(magic);
    char buffer[100];
    f.read(buffer, magic_len);
    return !strncmp(magic, buffer, magic_len);
}

void UserInterface::process_cmd_line_filename(string const& par)
{
    if (startswith(par, "=->"))
        exec_and_log(string(par, 3));
    else if (is_fityk_script(par))
        exec_script(par);
    else {
        exec_and_log("@+ <'" + par + "'");
    }
}




syntax highlighted by Code2HTML, v. 0.9.1