#include "catagorymgr.h"
#include <qfile.h>
#include <qdir.h>
#include <qstringlist.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>

CatagoryManager::CatagoryManager(QObject *parent, const char *name)
    : QObject(parent, name)
{
    int i;
    for(i=0; i < 256; ++i)
        catarray[i] = NULL;
    numItems = 0;
    readDb();
}

CatagoryManager::~CatagoryManager()
{
    clear();
}

bool CatagoryManager::readDb()
{
    unsigned char id;
    unsigned int n;
    int result;
    static char str[1024];
    qWarning("Loading category database");
    clear();

    QString fileStr(QDir::homeDirPath() + "/.pixiecategories");
    if(!QFile::exists(fileStr)){
        qWarning("No Pixie category database");
        return(true); // only return false on error
    }

    int fd = open(QFile::encodeName(fileStr), O_RDONLY);
    if(fd == -1){
        // TODO: error dialog
        qWarning("Unable to open category database.");
        return(false);
    }

    result = read(fd, &id, 1);
    while(result != 0 && result != -1){
        for(n=0; n < 1024; ++n){
            read(fd, str+n, 1);
            if(str[n] == '\0')
                break;
        }
        catarray[id] = new QString(str);
        nameList.append(*catarray[id]);
        ++numItems;
        result = read(fd, &id, 1);
    }
    close(fd);
    qWarning("Catagory database loaded");
    return(true);
}

bool CatagoryManager::writeDb()
{
    QString fileStr(QDir::homeDirPath() + "/.pixiecategories");
    int fd = open(QFile::encodeName(fileStr), O_WRONLY | O_CREAT | O_TRUNC,
                  S_IRUSR | S_IWUSR);
    if(fd == -1){
        // TODO: error dialog
        qWarning("Unable to write category database.");
        return(false);
    }

    unsigned int i;
    unsigned char id;
    for(i=1; i < 256; ++i){
        if(catarray[i] != NULL){
            qWarning("Writing %s", catarray[i]->latin1());
            id = i;
            write(fd, &id, 1);
            //fwrite((const char *)catarray[i]->latin1(),
            //       catarray[i]->length()-1, 1, fp); // don't use '\0'
            write(fd, (const char *)catarray[i]->latin1(),
                 catarray[i]->length());
            id = '\0';
            write(fd, &id, 1);
        }
    }
    close(fd);
    return(true);
}

void CatagoryManager::clear()
{
    if(!numItems)
        return;
    int i;
    for(i=1; i < 256; ++i){
        if(catarray[i])
            delete catarray[i];
        catarray[i] = NULL;
    }
    numItems = 0;
    nameList.clear();
}

void CatagoryManager::insertStringList(const QStringList &list)
{
    // this method doesn't really insert a new string list but checks
    // for new items and removed items in the list and handles it
    // appropriately, leaving all other items alone

    QStringList::ConstIterator it = list.begin();
    int i;
    bool hasItem;
    nameList.clear();
    while(it != list.end()){
        hasItem = false;
        for(i=1; i < 256 && !hasItem; ++i){
            if(catarray[i] != NULL){
                if((*it) == *catarray[i]){
                    hasItem = true;
                }
            }
        }
        if(!hasItem){
            qWarning("Adding new item: %s", (*it).latin1());
            for(i=1; i < 256 && catarray[i]; ++i)
                ;
            if(catarray[i] == NULL){
                catarray[i] = new QString;
                *catarray[i] = (*it);
                ++numItems;
            }
            else{
                // TODO Messagebox
                qWarning("Unable to add item %s!", (*it).latin1());
            }
        }
        ++it;
    }
    // check for removed items
    for(i=1; i < 256; ++i){
        if(catarray[i] != NULL){
            if(list.findIndex(*catarray[i]) == -1){
                qWarning("Removing item %s", catarray[i]->latin1());
                delete catarray[i];
                catarray[i] = NULL;
                --numItems;
            }
        }
    }
    nameList = list;
}

bool CatagoryManager::sync()
{
    if(writeDb()){
        emit changed();
        return(true);
    }
    else
        return(false);
}

int CatagoryManager::id(const QString &catagory)
{
    int i;
    for(i=1; i < 256; ++i){
        if(catarray[i] != NULL){
            if(*catarray[i] == catagory)
                return(i);
        }
    }
    return(0);
}

bool CatagoryManager::loadFolderCatagories(const QString &currentDir,
                                           QIntDict<CatInfo> *catDict)
{
    catDict->clear();
    if(catagoryList()->count() == 0){
        qWarning("No categories");
        return(false);
    }

    QString fileStr(currentDir + "/.category-");
    fileStr += getpwuid(getuid())->pw_name;

    bool useObseleteList = false;
    char str[1024];
    QValueList<int>obseleteList;

    if(!QFile::exists(fileStr)){
        qWarning("No Pixie categories in folder");
        return(true);
    }

    qWarning("Opening %s", fileStr.latin1());
    int fd = open(QFile::encodeName(fileStr), O_RDONLY);
    if(fd == -1){
        qWarning("Unable to open category database folder!");
        return(false);
    }

    // check for obselete items in category mapping
    QString **catagoryarray = array();
    QString tmpStr;
    unsigned char id;
    unsigned int i, n, tmp, items;
    int result;

    read(fd, &tmp, sizeof(unsigned int)); //reserved
    read(fd, &tmp, sizeof(unsigned int));
    read(fd, &tmp, sizeof(unsigned int));
    read(fd, &tmp, sizeof(unsigned int));

    read(fd, &items, sizeof(unsigned int));
    qWarning("%d category items", items);
    for(i=0; i < items; ++i){
        read(fd, &id, 1);
        for(n=0; n < 1024; ++n){
            read(fd, str+n, 1);
            if(str[n] == '\0')
                break;
        }
        tmp = id;// gcc don't like unsigned char indexes
        if(!catagoryarray[tmp] || *catagoryarray[tmp] != str){
            qWarning("Obselete category index found");
            useObseleteList = true;
            obseleteList.append(id);
        }
        qWarning("Read category mapping %s, id: %d", str, id);
    }

    // load in catagory database
    long inode;
    unsigned char numitems, catId;
    CatInfo *data;

    result = read(fd, &inode, sizeof(long));
    while(result != 0 && result != -1){
        read(fd, &numitems, 1);
        qWarning("Got %d items for inode", numitems);
        data = new CatInfo;
        for(i=0; i < 8; ++i)
            data->catagories[i] = 0;
        if(useObseleteList){
            for(i=0, id=0; i < numitems; ++i){
                read(fd, &catId, 1);
                qWarning("Category number %d: %d", i, catId);
                if(obseleteList.findIndex(catId) == -1){
                    data->catagories[id] = catId;
                    ++id;
                }
            }
            if(id == 0)
                delete data;
            else
                catDict->insert(inode, data);
        }
        else{
            for(i=0; i < numitems; ++i){
                read(fd, &data->catagories[i], 1);
                qWarning("Category number %d: %d", i, data->catagories[i]);
            }
            catDict->insert(inode, data);
        }
        result = read(fd, &inode, sizeof(long));
    }
    close(fd);
    qWarning("Catagory database load complete");
    return(true);
}

bool CatagoryManager::saveFolderCatagories(const QString &currentDir,
                                           QIntDict<CatInfo> *catDict)
{
    QString fileStr(currentDir + "/.category-");
    fileStr += getpwuid(getuid())->pw_name;
    qWarning("Saving category database for %s", currentDir.latin1());

    if(currentDir.isEmpty()){
        qWarning("Invalid path");
        return(true);
    }
    else if(!catDict->count()){ // no database, remove any files
        qWarning("No db needed");
        bool result = true;
        if(QFile::exists(fileStr)){
            if(unlink(QFile::encodeName(fileStr)) == -1){
                // TODO: Message Box
                qWarning("Unable to remove category file!");
                result = false;
            }
        }
        return(result);
    }

    bool usedcatagories[256];
    unsigned int i, items;
    unsigned char id, tmp, *data;
    QIntDictIterator<CatInfo> it(*catDict);

    for(i=0; i < 256; ++i)
        usedcatagories[i] = false;

    int fd = open(QFile::encodeName(fileStr), O_WRONLY | O_CREAT | O_TRUNC,
                 S_IRUSR | S_IWUSR);
    if(fd == -1){
        // TODO: error dialog
        qWarning("Unable to write local category database.");
        return(false);
    }

    i = 0;
    write(fd, &i, sizeof(unsigned int)); // reserved
    write(fd, &i, sizeof(unsigned int));
    write(fd, &i, sizeof(unsigned int));
    write(fd, &i, sizeof(unsigned int));

    // first find and write number of valid catagories
    items = 0;
    for(; it.current(); ++it){
        data = it.current()->catagories;
        if(data){
            for(i=0; i < 8 && data[i] != 0; ++i){
                id = data[i];
                if(!usedcatagories[id]){
                    usedcatagories[id] = true;
                    ++items;
                }
            }
        }
    }
    write(fd, &items, sizeof(unsigned int));

    // now write the catagory mappings
    QString **catagoryarray = array();
    for(i=0; i < 256; ++i){
        if(usedcatagories[i]){
            id = i;
            write(fd, &id, 1);
            write(fd, (const char *)catagoryarray[i]->latin1(),
                  catagoryarray[i]->length());
            id = '\0';
            write(fd, &id, 1);
        }
    }

    // and finally the items themselves
    long inode;
    it.toFirst();

    for(; it.current(); ++it){
        inode = (long)it.currentKey();
        write(fd, &inode, sizeof(long));
        data = it.current()->catagories;
        for(i=0; i < 8 && data[i] != 0; ++i)
            ;
        tmp = i;
        write(fd, &tmp, 1);
        qWarning("Catagories: %d", tmp);
        for(i=0; i < 8 && data[i] != 0; ++i)
            write(fd, &data[i], 1);
    }
    close(fd);
    return(true);
}


