#include <cstdio>
#include <cstdarg>
#include <algorithm>

#include "settings.hh"
#include "symbols.hh"
#include "ctcset.hh"
#include "config.hh"
#include "space.hh"
#include "ctinsert.hh"
#include "conjugate.hh"
#include "o65.hh"

using namespace std;

void Conjugatemap::Load(const insertor &ins)
{
    const ConfParser::ElemVec& elems = GetConf("conjugator", "setup").Fields();
    if(elems.empty())
    {
        fprintf(stderr, "> Conjugator has nothing to do. Not using.\n");
        return;
    }
    /* Translate the "setup" commands in [conjugator] */
    for(std::size_t a=0; a<elems.size(); a += 3)
    {
        form tmp;
        const std::u32string &func  = elems[a];
        const std::u32string &data  = elems[a+1];
        const std::u32string &width = elems[a+2];

        tmp.func   = func;
        tmp.used   = false;
        tmp.prefix = 0;

        tmp.maxwidth = 0;
        for(size_t b=0; b<width.size(); ++b)
        {
            ctchar c = getctchar(width[b], cset_12pix);
            tmp.maxwidth += 1 + ins.GetFont12width(c);
        }

        for(size_t b=0; b<data.size(); ++b)
        {
            if(data[b] == ' ' || data[b] == '\n'
            || data[b] == '\r' || data[b] == '\t') continue;

            ctchar person = 0x13;
            switch(WcharToAsc(data[b]))
            {
                case '0': person=0x11; break;
                case 'c': person=0x13; break;
                case 'm': person=0x14; break;
                case 'l': person=0x15; break;
                case 'r': person=0x16; break;
                case 'f': person=0x17; break;
                case 'a': person=0x18; break;
                case 'u': person=0x19; break;
                case 'e': person=0x20; break;
                case '1': person=0x1B; break;
                case '2': person=0x1C; break;
                case '3': person=0x1D; break;
                default:
                    fprintf(stderr, "In configuration: Unknown person '%c'\n", data[b]);
            }
            size_t c = ++b;
            while(b < data.size() && WcharToAsc(data[b]) != ',') ++b;

            std::u32string s = data.substr(c, b-c);

            const std::u32string &name = Symbols.GetRev(16).find(person)->second;

            ctstring key;
            for(size_t a=0; a<s.size(); ++a)
                if(s.compare(a, name.size(), name) == 0)
                {
                    key += person;
                    a += name.size() - 1;
                }
                else
                    key += getctchar(s[a], cset_12pix);

            tmp.data[key] = person;
        }

        AddForm(tmp);
    }
}

void Conjugatemap::Work(ctstring &s)
{
    // Collect all the form identifiers, and sort them longest-first.
    std::vector<std::pair<formit, datamap_t::const_iterator>> forms_sorted;
    for(auto fit = forms.begin(); fit != forms.end(); ++fit)
    {
        form &form = *fit;
        for(auto dit = form.data.begin(); dit != form.data.end(); ++dit)
        {
            forms_sorted.emplace_back(fit, dit);
        }
    }
    std::sort(forms_sorted.begin(), forms_sorted.end(), [](auto& a, auto& b)
    {
        return std::get<1>(a)->first.size() > std::get<1>(b)->first.size();
    });

    for(std::size_t a = 0; a < s.size(); ++a)
    {
        for(auto& [fit, i]: forms_sorted)
        {
            auto& form   = *fit;
            auto& string = i->first;
            if(s.compare(a, string.size(), string) != 0)
                continue;

            if(!form.used)
            {
                unsigned byte = 0x2FF;
                for(;; --byte)
                {
                    /* If this byte is used... */
                    if(getchar32_t(byte, cset_12pix) != ilseq) continue;

                    bool used = false;

                    const ConfParser::ElemVec& elems = GetConf("font", "typeface").Fields();
                    for(size_t a=0; a<elems.size(); a += 6)
                    {
                        unsigned begin = elems[a+3];
                        unsigned end   = elems[a+4];
                        if(byte >= begin && byte < end)
                        {
                            used = true;
                            break;
                        }
                    }

                    if(used) continue;

                    for(formlist::const_iterator j=forms.begin(); j!=forms.end(); ++j)
                        if(j->used
                        && j->prefix == byte)
                        {
                            used = true;
                            break;
                        }
                    if(!used) break;
                }

                form.used   = true;
                form.prefix = byte;

#if 1
                fprintf(stderr, " \n  Conjugator: Assigned ControlCode 0x%04X for %s ",
                    form.prefix,
                    WstrToAsc(form.func).c_str());
#endif

                charmap[form.prefix] = fit;
            }

            ctstring tmp;
            tmp += form.prefix; // conjugater id
            tmp += i->second;   // character id

            // a = b + i->first.size();

            s.erase(a, string.size());
            s.insert(a, tmp);
            a += tmp.size() - 1;
            break;
        }
    }
}

bool Conjugatemap::IsConjChar(ctchar c) const
{
    return charmap.find(c) != charmap.end();
}

void Conjugatemap::RedefineConjChar(ctchar was, ctchar is)
{
    charmap_t::iterator i = charmap.find(was);
    if(i != charmap.end())
    {
        formit tmp = i->second;
#if 1
        fprintf(stderr, "   Conjugator: Redefined ControlCode %s as 0x%04X->0x%04X\n",
            WstrToAsc(tmp->func).c_str(),
            tmp->prefix,
            is);
#endif
        tmp->prefix = is;
        charmap.erase(i);
        charmap[is] = tmp;
    }
}

unsigned Conjugatemap::GetMaxWidth(ctchar c) const
{
    charmap_t::const_iterator i = charmap.find(c);
    if(i == charmap.end())return 0;
    return i->second->maxwidth;
}

Conjugatemap::Conjugatemap(const insertor &ins)
   : forms(), charmap()
{
    Load(ins);
/* - nobody cares
    fprintf(stderr, "Built conjugator-map\n");
*/
}

void insertor::WriteConjugator()
{
    const Conjugatemap::formlist &forms = Conjugater->GetForms();

    Conjugatemap::formlist::const_iterator i;
    for(i=forms.begin(); i!=forms.end(); ++i)
    {
        if(!i->used) continue;

        const std::u32string& funcname = i->func;

        string CodeName = "CODE_";
        CodeName += WstrToAsc(funcname);

        fprintf(stderr, "Defining conjugator code %s as $%X\n", CodeName.c_str(), i->prefix);
        objects.DefineSymbol(CodeName, i->prefix);
    }
}
