#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <sstream>
#include <map>
#include <iterator>

typedef std::map<std::string, std::vector<std::string>> databazaSlov;

std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) 
{
    std::stringstream ss(s);
    std::string item;
    while(std::getline(ss, item, delim)) 
    {
        elems.push_back(item);
    }
    return elems;
}

std::vector<std::string> split(const std::string &s, char delim) 
{
    std::vector<std::string> elems;
    return split(s, delim, elems);
}

databazaSlov vytvorDatabazu(std::string slovnikFile)
{
    databazaSlov hotovaDatabaza;    
    std::ifstream inputStream;
    inputStream.open(slovnikFile);
    if(!inputStream.is_open())
    {
        return hotovaDatabaza;
    }
    std::string line;
    std::vector<std::string> slova;
    
    
    while(inputStream.good())
    {
        std::getline(inputStream,line);
        std::istringstream iss(line);
        std::copy(std::istream_iterator<std::string>(iss), std::istream_iterator<std::string>(), std::back_inserter<std::vector<std::string> >(slova));
        //v tejto chvili mame zo suboru natahane slova    
                

        for(int i = 0; i < slova.size(); ++i) //v tejto sekcii z natahanych slov spravime mapu slovo + slabikovy vector
        {
            std::vector<std::string> slabiky;
            std::string slovo = slova[i];
            slabiky = split(slovo, '-');
                        
            std::string opraveneSlovo;
            for(int j = 0; j < slovo.size(); ++j) //tento cyklus je na opravu slov - totiz po natahani je v map.first slovo aj s pomlckami, co je dobre kvoli parseru - po parsovani je nutne ich dat prec
            {
                char pismeno = slovo[j];
                if(pismeno == '-')
                {
                    continue;
                }
                else
                {
                    opraveneSlovo.push_back(pismeno); //ano, po pismenach... no a co ? :D ked teraz uvazujem, mohol so mto lepit aj po slabikach....
                }
            }
            slovo = opraveneSlovo;        

            hotovaDatabaza.insert(std::pair<std::string, std::vector<std::string>> (slovo, slabiky));
        }
              
    }
    
    inputStream.close();
    return hotovaDatabaza;
}

std::vector<std::string> nacitajVstup(std::string vstupFile) //v podstate zjednodusena verzia vyssej metody
{        
    std::ifstream inputStream;
    std::vector<std::string> slova;
    inputStream.open(vstupFile);
    if(!inputStream.is_open())
    {
        return slova;
    }
    std::string line;    

    while(inputStream.good())
    {
        std::getline(inputStream,line);
        std::istringstream iss(line);
        std::copy(std::istream_iterator<std::string>(iss), std::istream_iterator<std::string>(), std::back_inserter<std::vector<std::string> >(slova));
        //v tejto chvili mame zo suboru natahane slova a to je vlastne cela veda ^.^                     
    }

    inputStream.close();
    return slova;
}

bool jeSlovnikove(std::string slovo, databazaSlov slova) //toto nam matchuje aktualne slovo zo slovnikom - kolega Bednarek mi vynadal a vravel, ze ak pouzivam map, mam pouzivat aj find :D
{
    databazaSlov::iterator it;
    for(it = slova.begin(); it != slova.end(); ++it)
    {
        std::string match = it->first;
        if(match == slovo)
        {
            return true;
        }        
    }

    return false;
}

std::vector<std::string> kdeVSlovniku(std::string slovo, databazaSlov slova) //tu som si to teda potom opravil... :) predtym tu bol tiez forcyklus s iteratorom ;)
{
    databazaSlov::iterator it;
    int vysl;
    it = slova.find(slovo);

    return it->second;
}

void zalamuj(databazaSlov wordData, std::vector<std::string> vstupnyText, std::string outName, int rowSize) //jadro celeho programu a vlastne jedina naozaj zlozita cast kodu
{
    int spotrebovanaSirka = 0;    
    std::ofstream out;
    out.open(outName, std::ios::trunc | std::ios::out);

    for(int i = 0; i < vstupnyText.size(); ++i) //prebehneme cely zoznam slov zo vstupu
    {        
        std::string daneSlovo = vstupnyText[i];
        if(!jeSlovnikove(daneSlovo, wordData)) //tu sa zisti, ci aktualne slovo je v slovniku
        {
            if(daneSlovo.size() >= rowSize && spotrebovanaSirka == 0) //osetrenie pre neslovnikove slova, ktore su vacsie ako maximalna sirka riadku - tie rovno flusnem von
            {
                out << daneSlovo << "\n";
                spotrebovanaSirka == 0;
                continue;
            }

            spotrebovanaSirka += daneSlovo.size() + 1; //+1 pre medierku
            if(spotrebovanaSirka > rowSize) //dalsi riadok + znamienko - preliali sme sa cez hranicu riadku, takze hodime new line, spotrebu dame na nulu a dekrementujeme - to nam zaruci, ze sa k nevhodnemu slovu vratime v dalsom kole a nevynechame ho
            {                    
                out << "\n";    
                --i;                
                spotrebovanaSirka = 0;                
            }
            else
            {
                daneSlovo += " "; //pridame medzierku
                out << daneSlovo; //vypise aj s medzerou;
            }            
        }
        else//nie je slovnikove a mozeme lamat :3
        {
            std::vector<std::string> slabikySlova = kdeVSlovniku(daneSlovo, wordData); //toto povodne vracalo iterator, kde sa nachadza slovo v slovniku - boli s tym problemy, tak som si rozmyslel, ze mozem rovno vratit vector slabik :)           

            spotrebovanaSirka += daneSlovo.size() + 1; //+1 pre medierku
            if(spotrebovanaSirka >= rowSize - 1) //dalsi riadok + znamienko - znova, ak cele slovo nezmestim, musim pozriet ci zmestim nejaky substring slabik
            {
                int sirkaNecelehoSlova = 0;
                spotrebovanaSirka -= daneSlovo.size() + 1; //zhodme spotrebovanu sirku zase dole...
                for(int x = 0; x < slabikySlova.size(); ++x) //preiterujeme cez slabiky daneho slova
                {
                    std::string slabika = slabikySlova[x];
                    sirkaNecelehoSlova += slabika.size();
                    spotrebovanaSirka += sirkaNecelehoSlova;
                    if(spotrebovanaSirka > rowSize) //dalsi riadok + znamienko //ak to nejde s danou slabikou, flusneme von pomlcku a new line
                    {
                        if(x != 0) //toto je osetrenie, aby nam nevznikali pomlcky pred prvou slabikou 
                        {
                            out << "-\n";
                            --x; 
                        }
                        else
                        {
                            out << "\n";
                            --x; //a dekrementujeme vodiacu premennu pre slabiky - efekt ako vyssie pre cele slova.
                        }
                        spotrebovanaSirka = 0;//treba aj dovypisovat slovo
                        sirkaNecelehoSlova = 0;
                        //slovo mi ale dovypisuje v pohode ten cyklus vyssie - tuna prehodim spotrebu na nulu a on sa vrati a zisti, ze spotreba je nulova a bude pisat dalej...
                        //prejde do else vetvy a vypise zvysok slova ^^ ak sa da... ak nie pozalamuje a tak
                    }
                    else
                    {                        
                        out << slabikySlova[x]; //vypise aj s medzerou;    
                        if(x == slabikySlova.size() - 1) //ano, ak sme dopisali poslednu slabiku slova, prosime si za to aj medzeru... :)
                        {
                            out << " ";
                            ++spotrebovanaSirka; //spotreba narastla presne o 1 medzeru
                        }
                    }
                }                
            }
            else //neslovnikove slovo, ktore zmestim - flusnem von
            {
                daneSlovo += " "; //pridame medzierku
                out << daneSlovo; //vypise aj s medzerou;
            }    

        }
    }
    

    out.close();
    return;
}

int main()
{    
    //nacitat slovnik do databazy slov - asi map<string, vector>
    //nakrajane slova medzerami, v slove su '-', teda nakrajam to na slova a slova nakrajam na slabiky
    //nacitat vstup do vectoru
    //
    //int rowSize = 3;
    int rowSize = 11;	
    //testnite si to pre oba rowSize, a potom este pre 15 :)

    databazaSlov wordData;
    std::vector<std::string> vstupnyText;
    std::string vstup = "input.txt";
    std::string lang = "slovnik.txt";
    std::string vystup = "von.txt";
    //bolo nam dovolene zadratovat nazvy suborov natvrdo

    wordData = vytvorDatabazu(lang);
    //POZOR !!!!!!! v databaze su pomlcky, dat prec ! :) -- DONE !!!

    //teraz nacitanie vstupu
    vstupnyText = nacitajVstup(vstup);

    //vytvorenie vystupu
    zalamuj(wordData, vstupnyText, vystup, rowSize);

    std::cout << "lol"; //toto lol je tu kvoli krokovaniu :D

    return 0;
}