//i690, Sümeghi Nándor, 12.B, Budapesti Fazekas Mihály Gyakorló Általános Iskola és Gimázium, GNU GCC Compiler
#include <iostream>
#include <vector>
#include <fstream>
#include <string>
#include <unordered_set>
#include <algorithm>
using namespace std;

struct Cmd
{
    string name;
    string desc;//leírás
};


// segédfüggvények
string lower(string s)
{
    for(char &c : s) c = tolower(c);
    return s;
}


void format(int num)
{
    cout<<"\033["<<num<<"m";
}
/*
1 - bold? or just white?
2 - grey
3 - italic
4 - underlined
5, 6 - light-dark flashing
7 - inverted color and background
8 - black everything
9 - crossed
21 - double underlined
30 - black
31-37 - red, green, yellow, blue, purple, cyan, grey
40-47 - same for background
53 - top line
91-97 & 101 - 107 lighter versions of the previous colours
*/
void deformat()
{
    cout<<" \033[0m";
}


vector<string> split_words(string text)
{
    vector<string> res;
    string jel;

    for(char c : text)
    {
        if(isalpha(c))
        {
            jel+=tolower(c);
        }
        else
        {
            if(!jel.empty())
            {
                res.push_back(jel);
                jel.clear();
            }
        }
    }

    if(!jel.empty()) res.push_back(jel);

    return res;
}


unordered_set<string> stopw(const string &filename)
{
    unordered_set<string> s;
    ifstream f(filename);

    string w;

    while(f>>w)
        s.insert(lower(w));

    return s;
}


vector<Cmd> betolt(string file)
{
    ifstream f(file);
    vector<Cmd> res;

    string line;

    getline(f,line); // header

    while(getline(f,line))
    {
        int p1=line.find(',');
        int p2=line.find(',',p1+1);

        Cmd c;
        c.name = lower(line.substr(p1+1,p2-p1-1));
        c.desc = lower(line.substr(p2+1));

        res.push_back(c);
    }

    return res;
}


// pontozás
int desc_score(const string &d1,const string &d2,
               const unordered_set<string> &stop)
{
    auto w1 = split_words(d1);
    auto w2 = split_words(d2);

    int score=0;

    for(auto &a:w1)
    {
        if(stop.count(a)) continue;

        for(auto &b:w2)
        {
            if(stop.count(b)) continue;

            if(a==b)
                score+=2;

            else if(a.size()>=5 && b.size()>=5 &&
                    a.substr(0,5)==b.substr(0,5))
                score+=2;
        }
    }

    return score;
}


int name_score(string a,string b)
{
    if(a==b) return 20;

    if(a.size()>=5 && b.find(a.substr(0,5))!=string::npos)
        return 10;

    if(b.size()>=5 && a.find(b.substr(0,5))!=string::npos)
        return 10;

    return 0;
}


int full_score(const Cmd &a,const Cmd &b,
               const unordered_set<string> &stop)
{
    int s=name_score(a.name,b.name);

    s+=desc_score(a.desc,b.desc,stop);

    return s;
}


// funkció 1.


void search_desc(const string &query,
                 const vector<Cmd> &linux,
                 const vector<Cmd> &win,
                 const vector<Cmd> &mac,
                 const unordered_set<string> &stop)
{
    vector<pair<string,int>> res;

    for(auto &c:linux)
    {
        int s=desc_score(query,c.desc,stop);
        if(s>0) res.push_back({"linux: "+c.name,s});
    }

    for(auto &c:win)
    {
        int s=desc_score(query,c.desc,stop);
        if(s>0) res.push_back({"windows: "+c.name,s});
    }

    for(auto &c:mac)
    {
        int s=desc_score(query,c.desc,stop);
        if(s>0) res.push_back({"macos: "+c.name,s});
    }

    sort(res.begin(),res.end(),
         [](auto &a,auto &b){return a.second>b.second;});

    for(auto &x:res)
        cout<<x.first<<"   ("<<x.second<<")\n";

    if(res.empty())
        cout<<"Nincs talalat.\n";
}


// funkció 2
void search_command(const string &name,
                    char sys,
                    const vector<Cmd> &linux,
                    const vector<Cmd> &win,
                    const vector<Cmd> &mac,
                    const unordered_set<string> &stop)
{
    const Cmd *base=nullptr;

    if(sys=='l')
        for(auto &c:linux) if(c.name==name) base=&c;

    if(sys=='w')
        for(auto &c:win) if(c.name==name) base=&c;

    if(sys=='m')
        for(auto &c:mac) if(c.name==name) base=&c;

    if(!base)
    {
        cout<<"Nincs ilyen parancs.\n";
        return;
    }

    vector<pair<string,int>> res;

    if(sys!='l')
        for(auto &c:linux)
        {
            int s=full_score(*base,c,stop);
            if(s>0) res.push_back({"linux: "+c.name,s});
        }

    if(sys!='w')
        for(auto &c:win)
        {
            int s=full_score(*base,c,stop);
            if(s>0) res.push_back({"windows: "+c.name,s});
        }

    if(sys!='m')
        for(auto &c:mac)
        {
            int s=full_score(*base,c,stop);
            if(s>0) res.push_back({"macos: "+c.name,s});
        }

    sort(res.begin(),res.end(),
         [](auto &a,auto &b){return a.second>b.second;});

    for(auto &x:res)
        cout<<x.first<<"   ("<<x.second<<")\n";

    if(res.empty())
        cout<<"Nincs talalat.\n";
}


// MAIN
int main()
{
    cout<<"Adatok betoltese...\n";

    auto stop = stopw("common_words.txt");

    auto win = betolt("cmd_commands.csv");
    auto linux = betolt("linux_commands.csv");
    auto mac = betolt("macos_commands.csv");


    while(true)
    {
        format(31);
        format(6);
        format(7);
        format(107);
        cout<<endl<<"1 - kereses leiras alapjan";
        deformat();
        cout<<endl;
        format(31);
        format(6);
        format(7);
        format(107);
        cout<<"2 - parancs megfeleltetes";
        deformat();
        cout<<endl;
        format(31);
        format(6);
        format(7);
        format(107);
        cout<<"0 - kilepes";
        deformat();
        cout<<endl;
        format(3);
        format(4);
        cout<<"Valasztas:";
        deformat();
        cout<<" ";
        string mode;
        getline(cin,mode);

        if(mode=="0") break;

        if(mode=="1")
        {
            while(true)
            {
                format(32);
                cout<<endl<<"Kereses (ures enter = vissza): ";
                deformat();
                string q;
                getline(cin,q);

                if(q.empty()) break;
                format(2);
                search_desc(q,linux,win,mac,stop);
                deformat();
            }
        }


        if(mode=="2")
        {
            while(true)
            {
                format(33);
                cout<<endl<<"Parancs neve (ures enter = vissza): ";
                deformat();
                string n;
                getline(cin,n);

                if(n.empty()) break;

                n=lower(n);
                format(33);
                cout<<"Rendszer (l/w/m): ";
                deformat();
                string r;
                getline(cin,r);

                if(r.empty()) break;
                format(2);
                search_command(n,r[0],linux,win,mac,stop);
                deformat();
            }
        }
    }
    format(1);
    format(21);
    format(91);
    cout<<"Program vege.";
    deformat();
    cout<<endl;
}
