/***************************************************************************
 *   Copyright (C) 2004 by Juanjo Álvarez                                  *
 *   juanjux@yahoo.es                                                      *
 *                                                                         *
 *   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 2 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, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Steet, Fifth Floor, Boston, MA  02111-1307, USA.          *
 ***************************************************************************/

#include "altparser.h"
#include <tqfileinfo.h>
#include <tqfile.h>
#include <tqdir.h>
#include <tqstringlist.h>

#include <unistd.h>
#include <string.h>
#include <errno.h>

Alternative::Alternative(Item *parentarg) : m_parent(parentarg)
{
    m_priority = 1;
    m_altSlaves = new TQStringList;
}

// Copy constructor
Alternative::Alternative(const Alternative &alt) :
    m_altPath(alt.getPath()),
    m_priority(alt.getPriority()),
    m_parent(alt.getParent())
{
    m_altSlaves = new TQStringList( *(alt.m_altSlaves) );
}

Alternative::~Alternative()
{
    if(m_altSlaves)delete m_altSlaves;
}

Alternative& Alternative::operator=(const Alternative &alt)
{
    if(this != &alt)
    {
        if(m_altSlaves)delete m_altSlaves;
        m_altPath = alt.getPath();
        m_priority = alt.getPriority();
        m_parent = alt.getParent();
        m_altSlaves = new TQStringList( *(alt.m_altSlaves) );
    }
    return (*this);
}

void Alternative::setSlaves(TQStringList *slaves)
{
    if(m_altSlaves) delete m_altSlaves;
    m_altSlaves = slaves;
}

bool Alternative::isSelected() const
{
    if(m_parent->isBroken()) return false;
    TQFileInfo file("/etc/alternatives/"+m_parent->getName());
    if(!file.isSymLink()) return false;
    if(file.readLink() == m_altPath) return 1;
    return 0;
}


bool Alternative::isBroken() const
{
    return !TQFile::exists(m_altPath);
}

bool Alternative::select()
{
    if(isSelected()) return true;
    if(isBroken())
    {
        m_selectError = TQString("Broken alternative: Unexisting path %1").arg(m_altPath);
        return false;
    }

    // Remove the current link:
    TQString parentPath = TQString("/etc/alternatives/%1").arg(m_parent->getName());
    TQFile origlink(parentPath);
    if(!origlink.remove())
    {
        m_selectError = TQString("Could not delete alternative link %1: %2").arg(parentPath).arg(origlink.errorString());
        return false;
    }

    // Then we do the main link:
    if(symlink(m_altPath.ascii(), parentPath.ascii()) == -1)
    {
        m_selectError = TQString(strerror(errno));
        return false;
    }

    // And finally the slaves
    SlaveList *parslaves = m_parent->getSlaves();
    parslaves->setAutoDelete(1);
    if(parslaves->count() == 0 || m_altSlaves->count() == 0) return true;
    int count = 0;
    TQStringList::iterator sl;
    Slave *parsl;
    for( sl = m_altSlaves->begin(); sl != m_altSlaves->end(); ++sl)
    {
        parsl = parslaves->at(count);
        TQString parstr = TQString("/etc/alternatives/%1").arg(parsl->slname);
        TQFile parlink(parstr);
        if(!parlink.remove())
        {
            m_selectError = TQString("Could not delete slave alternative link %1: %2").arg(parstr).arg(parlink.errorString());
            return false;
        }
        if(symlink( (*sl).ascii(), parstr.ascii()) == -1)
        {
            m_selectError = TQString(strerror(errno));
            return false;
        }
        ++count;
    }
    return true;
}


//*************************************** Item

Item::Item()
{
    m_mode = "auto";
    m_itemSlaves = new SlaveList;
    m_itemAlts = new AltsPtrList;
    m_itemSlaves->setAutoDelete(1);
    m_itemAlts->setAutoDelete(1);
}

// Deep copy
Item::Item(const Item &item) :
    m_name(item.m_name),
    m_mode(item.m_mode),
    m_path(item.m_path)
{
    m_itemSlaves = new SlaveList;
    m_itemAlts = new AltsPtrList;
    m_itemSlaves->setAutoDelete(1);
    m_itemAlts->setAutoDelete(1);
    Slave *slave;
    Slave *slavecopy;
    for(slave = item.m_itemSlaves->first(); slave; slave = item.m_itemSlaves->next())
    {
        slavecopy = new Slave;
        slavecopy->slname = slave->slname;
        slavecopy->slpath = slave->slpath;
        m_itemSlaves->append(slavecopy);
    }

    Alternative *alt;
    Alternative *altcopy;
    for(alt = item.m_itemAlts->first(); alt; alt = item.m_itemAlts->next())
    {
        // The Alternative class already has a deep copy constructor:
        altcopy = new Alternative( (*alt) );
        m_itemAlts->append(altcopy);
    }
}

Item& Item::operator=(const Item &item)
{
    if(this != &item)
    {
        if(m_itemSlaves)delete m_itemSlaves;
        if(m_itemAlts)delete m_itemAlts;
        m_name = item.m_name;
        m_mode = item.m_mode;
        m_path = item.m_path;
        m_itemSlaves = new SlaveList;
        m_itemAlts = new AltsPtrList;
        m_itemSlaves->setAutoDelete(1);
        m_itemAlts->setAutoDelete(1);
        Slave *slave;
        Slave *slavecopy;
        for(slave = item.m_itemSlaves->first(); slave; slave = item.m_itemSlaves->next())
        {
            slavecopy = new Slave;
            slavecopy->slname = slave->slname;
            slavecopy->slpath = slave->slpath;
            m_itemSlaves->append(slavecopy);
        }

        Alternative *alt;
        Alternative *altcopy;
        for(alt = item.m_itemAlts->first(); alt; alt = item.m_itemAlts->next())
        {
            altcopy = new Alternative( (*alt) );
            m_itemAlts->append(altcopy);
        }
    }
    return (*this);
}



Item::~Item()
{
    if(m_itemSlaves)delete m_itemSlaves;
    if(m_itemAlts)delete m_itemAlts;
}

Alternative *Item::getSelected() const
{
    Alternative *a;
    for(a = m_itemAlts->first(); a; a = m_itemAlts->next())
    {
        if(a->isSelected())
        {
            return a;
            break;
        }
    }
    return NULL;
}

void Item::setSlaves(SlaveList *slaves)
{
    if(this->m_itemSlaves)delete this->m_itemSlaves;
    this->m_itemSlaves = slaves;
}

void Item::addSlave(const TQString &namearg, const TQString &patharg)
{
    Slave *s = new Slave;
    s->slname = namearg;
    s->slpath = patharg;
    m_itemSlaves->append(s);
}

void Item::delSlave(const TQString &namearg)
{
    TQPtrListIterator<Slave> it(*m_itemSlaves);

    Slave *s;
    while( (s = it.current()) != 0)
    {
        ++it;
        if(s->slname == namearg)
        {
            m_itemSlaves->remove(s);
            break;
        }
    }
}
void Item::delSlaveByPath(const TQString &patharg)
{
    TQPtrListIterator<Slave> it(*m_itemSlaves);

    Slave *s;
    while( (s = it.current()) != 0)
    {
        ++it;
        if(s->slpath == patharg)
        {
            m_itemSlaves->remove(s);
            break;
        }
    }
}

Alternative *Item::getAlternative(const TQString &altpath)
{
    Alternative *a;
    for(a = m_itemAlts->first(); a; a = m_itemAlts->next())
    {
        if(a->getPath() == altpath)
        {
            return a;
            break;
        }
    }
    return NULL;
}

void Item::setAlternatives(AltsPtrList &alts)
{
    if(this->m_itemAlts)delete this->m_itemAlts;
    this->m_itemAlts = &alts;
}

void Item::delAlternativeByPath(const TQString &patharg)
{
    TQPtrListIterator<Alternative> it(*m_itemAlts);

    Alternative *a;
    while( (a = it.current()) != 0)
    {
        ++it;
        if(a->getPath() == patharg)
        {
            m_itemAlts->remove(a);
            break;
        }
    }
}

void Item::delAlternativeByPriority(int priorityarg)
{
    TQPtrListIterator<Alternative> it(*m_itemAlts);

    Alternative *a;
    while( (a = it.current()) != 0)
    {
        ++it;
        if(a->getPriority() == priorityarg)
        {
            m_itemAlts->remove(a);
            break;
        }
    }
}

bool Item::isBroken() const
{
    return !TQFile::exists(m_path);
}

/********************** AltFIlesManager ************/

AltFilesManager::AltFilesManager(const TQString &altdirarg) :
    m_altdir(altdirarg)
{
    m_itemlist = new ItemPtrList;
    m_itemlist->setAutoDelete(1);
    m_parseOk = true;
    m_errorMsg = "";
    if(!parseAltFiles(m_errorMsg))
    {
        m_parseOk = false;
    }
    //debugPrintAlts();
}

AltFilesManager::~AltFilesManager()
{
    //delete m_itemlist;
    /*
    Item *item;
    for(item = m_itemlist->first(); item; item = m_itemlist->next())
    {
        delete item;
    }

    delete m_itemlist;
    */
}

Item* AltFilesManager::getItem(const TQString &name) const
{
    TQPtrListIterator<Item> it(*m_itemlist);
    Item *i;
    while( (i = it.current()) !=  0)
    {
        ++it;
        if(i->getName() == name)
        {
            return i;
            break;
        }
    }
    return NULL;
}

bool AltFilesManager::parseAltFiles(TQString &errorstr)
{
    TQDir d(m_altdir);
    TQStringList fileList = d.entryList();
    TQStringList lines;
    TQFile altFile;
    TQString line, tmp;
    int nslaves;
    unsigned int index, slavesend;

    for( TQStringList::Iterator it = fileList.begin(); it != fileList.end(); ++it)
    {
        Item *item = new Item;
        if(*it == "." || *it == "..") {
            delete item;
            continue;
        }

        item->setName(*it);
        altFile.setName(m_altdir+"/"+*it);

        if(!altFile.open( IO_ReadOnly ))
        {
            errorstr = altFile.errorString();
            delete item;
            return false;
        }

        lines.clear();
        while ( !altFile.atEnd() )
        {
            if(!altFile.readLine(line, 9999))
            {
                errorstr = altFile.errorString();
                delete item;
                return false;
            }
            lines.append(line);
        }

        line = lines[0];
        tmp = line.left(line.length()-1);
        item->setMode(tmp);

        line = lines[1];
        tmp = line.left(line.length()-1);
        item->setPath(tmp);

        index = 2;
        line = lines[index];
        nslaves = 0;
        SlaveList *slaves = new SlaveList;
        slaves->setAutoDelete(1);

        while(line != "\n")
        {
            tmp = line.left(line.length()-1);
            Slave *slave = new Slave;
            nslaves++;
            slave->slname = tmp;

            line = lines[++index];
            tmp = line.left(line.length()-1);
            slave->slpath = tmp;

            slaves->append(slave);
            line = lines[++index];
        }

        item->setSlaves(slaves);

        ++index;
        while(index < lines.count()-1)
        {
            line = lines[index];
            Alternative *a = new Alternative(item);
            tmp = line.left(line.length()-1);
            a->setPath(tmp);

            if(line=="\n") {
                //File end (with a \n)
                delete a;
                break;
            }

            if(++index == lines.count())
            {
                item->addAlternative(a);
                break;
            }


            line = lines[index];
            tmp = line.left(line.length()-1);
            a->setPriority(tmp.toInt());

            if(++index == lines.count())
            {
                item->addAlternative(a);
                break;
            }

            line = lines[index];
            if(line != "\n" and nslaves > 0)
            {
                slavesend = index+nslaves;
                while(index < slavesend)
                {
                    line = lines[index];
                    tmp = line.left(line.length()-1);
                    a->addSlave(tmp);
                    ++index;
                }
            }
			else
			{
				if (nslaves >0)
				{
					++index;
				}
			}
            item->addAlternative(a);
        }
        m_itemlist->append(item);
        altFile.close();
    }

    return true;
}

//FIXME: This must be in a son of qptrlist!
/*
int AltFilesManager::compareItems(Item i1, Item i2)
{
    return i1.getPath().compare(i2.getPath());
}
*/

/*
void AltFilesManager::debugPrintAlts() const
{
    printf("----------------------------------\n");
    Item *item;
    for(item = m_itemlist->first(); item; item = m_itemlist->next())
    {
        printf("\nItem: %s\n", item->getName().ascii());
        printf("\tMode: %s\n", item->getMode().ascii());
        printf("\tPath: %s\n", item->getPath().ascii());
        if(item->getSlaves()->count() == 0)
            printf("\tNo slaves\n");
        else
        {
            Slave *slave;
            SlaveList *slaves = item->getSlaves();
            for(slave = slaves->first(); slave; slave = slaves->next())
            {
                printf("\tSlave name: %s\n", slave->name.ascii());
                printf("\tSlave path: %s\n", slave->path.ascii());
            }
        }
        printf("\tAlternatives:\n");
        if(item->getAlternatives()->count() == 0)
            printf("\t\tNO ALTERNATIVES!");
        else
        {
            Alternative *a;
            AltsPtrList *alts = item->getAlternatives();
            for(a = alts->first(); a; a = alts->next())
            {
                printf("\t\tPath: %s\n", a->getPath().ascii());
                printf("\t\tPriority: %d\n", a->getPriority());
                printf("\t\tSlaves:\n");
                if(a->getSlaves()->count() == 0)
                    printf("\t\t\tNo slaves\n");
                else
                {
                    TQStringList altslaves = *(a->getSlaves());
                    TQStringList::iterator sl;
                    for( sl = altslaves.begin(); sl != altslaves.end(); ++sl)
                    {
                        printf("\t\t\t%s\n", (*sl).ascii());
                    }
                }
            }
        }
    }
}
*/
/*
// ************************************** Test
int main(int argc, char **argv)
{
    AltFilesManager a("/var/lib/rpm/alternatives");
    if(!a.parsingOk())
        printf("ERROR PARSING ALT FILES: %s\n", a.getErrorMsg().ascii());
    else
        printf("\nOK, Finished parsing\n");

    Item *item= a.getItem("vi");
    if(item == NULL) return 0;
    printf("Nombre item: %s\n", item->getName().ascii());
    printf("Path item: %s\n", item->getPath().ascii());
    Alternative *alt = item->getSelected();
    if(alt == NULL) return 0;
    printf("Selected alt: %s\n", alt->getPath().ascii());

    Alternative *vimminimal = item->getAlternative("/bin/vim-minimal");
    if(vimminimal == NULL) { printf("NULL!\n"); return 0; }
    printf("Not selected alt: %s\n", vimminimal->getPath().ascii());

    printf("Selecting vim-minimal instead of vim-enhanced as vi\n");
    if(!vimminimal->select())
    {
        printf("ERROR: %s\n", vimminimal->getSelectError().ascii());
    }

    printf("Now selecting vim-enhanced...\n");
    Alternative *vimen = item->getAlternative("/usr/bin/vim-enhanced");
    if(vimen == NULL) { printf("NULL!\n"); return 0; }
    if(!vimen->select())
    {
        printf("ERROR: %s\n", alt->getSelectError().ascii());
    }
    return 0;
  }
*/
