/**
 * This file is part of FT8Call.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 *
 * (C) 2018 Jordan Sherer  - All Rights Reserved
 *
 **/
#include "jsc.h"
#include "varicode.h"
#include 
Codeword JSC::codeword(quint32 index, bool separate, quint32 bytesize, quint32 s, quint32 c){
    QList out;
    quint32 v = ((index % s) << 1) + (quint32)separate;
    out.prepend(Varicode::intToBits(v, bytesize + 1));
    quint32 x = index / s;
    while(x > 0){
        x -= 1;
        out.prepend(Varicode::intToBits((x % c) + s, bytesize));
        x /= c;
    }
    Codeword word;
    foreach(auto w, out){
        word.append(w);
    }
    return word;
}
QList JSC::compress(QString text){
    QList out;
    const quint32 b = 4;
    const quint32 s = 7;
    const quint32 c = pow(2, 4) - s;
    foreach(QString w, text.split(" ", QString::SkipEmptyParts)){
        bool ok = false;
        auto index = lookup(w, &ok);
        if(ok){
            // cool, we found the word...
            out.append({ codeword(index, true, b, s, c), (quint32)w.length() + 1 /* for the space that follows */ });
        } else {
            // hmm. no dice. let's go for a prefix match
            while(!w.isEmpty()){
                bool hasPrefix = false;
                auto d = w.toLatin1().data();
                for(quint32 i = 0; i < JSC::size; i++){
                    quint32 len = JSC::list[i].size;
                    if(strncmp(d, JSC::list[i].str, len) == 0){
                        w = QString(w.mid(len));
                        auto index = JSC::list[i].index;
                        bool isLast = w.isEmpty();
                        out.append({ codeword(index, isLast, b, s, c), len + (isLast ? 1 : 0) /* for the space that follows */});
                        hasPrefix = true;
                        break;
                    }
                }
                if(!hasPrefix){
                    // no match...SOL
                    break;
                }
            }
        }
    }
    return out;
}
QString JSC::decompress(Codeword const& bitvec){
    const quint32 b = 4;
    const quint32 s = 7;
    const quint32 c = pow(2, b) - s;
    QStringList out;
    quint32 base[8];
    base[0] = 0;
    base[1] = s;
    base[2] = base[1] + s*c;
    base[3] = base[2] + s*c*c;
    base[4] = base[3] + s*c*c*c;
    base[5] = base[4] + s*c*c*c*c;
    base[6] = base[5] + s*c*c*c*c*c;
    base[7] = base[6] + s*c*c*c*c*c*c;
    QList bytes;
    QList separators;
    auto iter = bitvec.begin();
    while(iter != bitvec.end()){
        quint64 byte = Varicode::bitsToInt(iter, 4);
        iter += 4;
        bytes.append(byte);
        if(byte < s){
            if(*iter){
                separators.append(bytes.length()-1);
            }
            iter += 1;
        }
    }
    int start = 0;
    while(start < bytes.length()){
        int k = 0;
        int j = 0;
        while(start + k < bytes.length() && bytes[start + k] >= s){
            j = j*c + (bytes[start + k] - s);
            k++;
        }
        j = j*s + bytes[start + k] + base[k];
        out.append(QString(JSC::map[j].str));
        if(!separators.isEmpty() && separators.first() == start + k){
            out.append(" ");
            separators.removeFirst();
        }
        start = start + (k + 1);
    }
    return out.join("");
}
quint32 JSC::lookup(QString w, bool * ok){
    return lookup(w.toLatin1().data(), ok);
}
quint32 JSC::lookup(char const* b, bool *ok){
    for(quint32 i = 0; i < JSC::size; i++){
        if(strcmp(b, JSC::map[i].str) == 0){
            if(ok) *ok = true;
            return i;
        }
    }
    if(ok) *ok = false;
    return 0;
}