/***************************************************************************
                          stationlist.cpp  -  description
                             -------------------
    begin                : Sat March 29 2003
    copyright            : (C) 2003 by Klas Kalass, Ernst Martin Witte
    email                : klas@kde.org, witte@kawo1.rwth-aachen.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "include/radiostation.h"
#include "include/errorlog-interfaces.h"
#include "include/utils.h"
#include "include/stationlist.h"
#include "include/stationlistxmlhandler.h"
#include "include/tderadioversion.h"

#include <tqstring.h>
#include <tqfile.h>
#include <tqiodevice.h>
#include <tqmessagebox.h>
#include <tdeio/netaccess.h>
#include <tdetempfile.h>
#include <tdelocale.h>

//////////////////////////////////////////////////////////////////////////////

const KDE_EXPORT StationList emptyStationList;

//////////////////////////////////////////////////////////////////////////////

RawStationList::RawStationList ()
{
    setAutoDelete(true);
}


RawStationList::RawStationList (const RawStationList &sl)
    : TQPtrList<RadioStation>(sl)
{
    setAutoDelete(true);
}


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


TQPtrCollection::Item RawStationList::newItem (TQPtrCollection::Item s)
{
    if (s)
        return ((RadioStation*)s)->copy();
    else
        return NULL;
}


void  RawStationList::deleteItem (TQPtrCollection::Item s)
{
    if (autoDelete())
        delete (RadioStation*)s;
}


int RawStationList::compareItems(TQPtrCollection::Item a, TQPtrCollection::Item b)
{
    if (!a && !b)
        return 0;

    if (!a)
        return -1;

    if (!b)
        return 1;

    return ((RadioStation*)a)->compare(*(RadioStation*)b);
}


bool RawStationList::insert (uint index, const RadioStation * item )
{
    if (!item) return false;
    RadioStation *rs = &stationWithID(item->stationID());
    bool r = true;
    if (rs != item) {
        r = BaseClass::insert(index, item);
        removeRef(rs);
    }
    return r;
}


bool RawStationList::insert (const RadioStation * item )
{
    if (!item) return false;
    int idx = idxWithID(item->stationID());
    if (idx >= 0) {
        return replace(idx, item);
    } else {
        append(item);
        return true;
    }
}


void RawStationList::inSort ( const RadioStation * item )
{
    if (!item) return;
    RadioStation *rs = &stationWithID(item->stationID());
    if (rs != item) {
        removeRef(rs);
    }
    BaseClass::inSort(item);
}


void RawStationList::prepend ( const RadioStation * item )
{
    if (!item) return;
    RadioStation *rs = &stationWithID(item->stationID());
    if (rs != item) {
        removeRef(rs);
    }
    BaseClass::prepend(item);
}


void RawStationList::append ( const RadioStation * item )
{
    if (!item) return;
    RadioStation *rs = &stationWithID(item->stationID());
    if (rs != item) {
        removeRef(rs);
    }
    BaseClass::append(item);
}


bool RawStationList::replace ( uint index, const RadioStation * item )
{
    bool r = true;
    RadioStation *rs = &stationWithID(item->stationID());
    r = BaseClass::replace(index, item);
    if (rs != item) {
        BaseClass::removeRef(rs);
    }
    return r;
}


const RadioStation &RawStationList::stationWithID(const TQString &sid) const
{
    Iterator it(*this);
    for (; const RadioStation *s = it.current(); ++it) {
        if (s->stationID() == sid)
            return *s;
    }
    return (RadioStation &) undefinedRadioStation;
}


RadioStation &RawStationList::stationWithID(const TQString &sid)
{
    Iterator it(*this);
    for (; RadioStation *s = it.current(); ++it) {
        if (s->stationID() == sid)
            return *s;
    }
    return (RadioStation &) undefinedRadioStation;
}



int RawStationList::idxWithID(const TQString &sid) const
{
    int i = 0;
    Iterator it(*this);
    for (; const RadioStation *s = it.current(); ++it, ++i) {
        if (s->stationID() == sid)
            return i;
    }
    return -1;
}


bool RawStationList::operator == (const RawStationList &l) const
{
    TQPtrListIterator<RadioStation> it1(*this);
    TQPtrListIterator<RadioStation> it2(l);
    if (count() != l.count())
        return false;
    for (; it1.current() && it2.current(); ++it1, ++it2) {
        if (**it1 != **it2)
            return false;
    }
    return true;
}


//////////////////////////////////////////////////////////////////////////////

StationList::StationList()
{
    m_all.setAutoDelete(true);
}

StationList::StationList(const StationList &sl)
    : m_all      (sl.m_all),
      m_metaData (sl.m_metaData)
{
    m_all.setAutoDelete(true);
}


StationList::~StationList()
{
}


void StationList::merge(const StationList & other)
{
    // merge meta information: honor merge in comment

    StationListMetaData const & metaData = other.metaData();

    if (! m_metaData.comment.isEmpty())
        m_metaData.comment += "\n";

    m_metaData.lastChange = TQDateTime::currentDateTime();

    if (!metaData.maintainer.isEmpty())
        m_metaData.maintainer += (count() ? TQString(" / ") : TQString()) + metaData.maintainer;

    if (!metaData.country.isEmpty())
        m_metaData.country += (count() ? TQString(" / ") : TQString()) + metaData.country;

    if (!metaData.city.isEmpty())
        m_metaData.city = (count()     ? TQString(" / ") : TQString()) + metaData.city;

    if (!metaData.media.isEmpty())
        m_metaData.media += (count()   ? TQString(" / ") : TQString()) + metaData.media;

    if (!metaData.comment.isEmpty())
        m_metaData.comment += (count() ? TQString(" / ") : TQString()) + metaData.comment;
    if (count() && other.count())
        m_metaData.comment += " " + i18n("Contains merged Data");


    // merge stations

    TQPtrListIterator<RadioStation> it(other.all());
    for (RadioStation *s = it.current(); s; s = ++it) {
        m_all.insert(s);
    }
}



StationList &StationList::operator = (const StationList &other)
{
    m_metaData = other.metaData();
    m_all      = other.all();
    return *this;
}


const RadioStation &StationList::at(int idx) const
{
    RawStationList::Iterator it(m_all);
    it += idx;
    return it.current() ? *it.current() : (const RadioStation &) undefinedRadioStation;
}


RadioStation &StationList::at(int idx)
{
    RawStationList::Iterator it(m_all);
    it += idx;
    return it.current() ? *it.current() : (RadioStation &) undefinedRadioStation;
}


const RadioStation &StationList::stationWithID(const TQString &sid) const
{
    return m_all.stationWithID(sid);
}


RadioStation &StationList::stationWithID(const TQString &sid)
{
    return m_all.stationWithID(sid);
}


bool StationList::readXML (const TQString &dat, const IErrorLogClient &logger, bool enableMessageBox)
{
    // FIXME: TODO: error handling
    TQXmlInputSource source;
    source.setData(dat);
    TQXmlSimpleReader      reader;
    StationListXmlHandler handler(logger);
    reader.setContentHandler (&handler);
    if (reader.parse(source)) {
        if (handler.wasCompatMode() && enableMessageBox) {
            TQMessageBox::information(NULL, "TDERadio",
                                     i18n("Probably an old station preset file was read.\n"
                                     "You have to rebuild your station selections for "
                                     "the quickbar and the docking menu.")
                                     );
        }

        m_all      = handler.getStations();
        m_metaData = handler.getMetaData();
        return true;
    } else {
        logger.logError("StationList::readXML: " + i18n("parsing failed"));

        if (enableMessageBox) {
            TQMessageBox::warning(NULL, "TDERadio",
                                 i18n("Parsing the station preset file failed.\n"
                                      "See console output for more details."));
        }
        return false;
    }
}


bool StationList::readXML (const KURL &url, const IErrorLogClient &logger, bool enableMessageBox)
{
    TQString tmpfile;
    if (!TDEIO::NetAccess::download(url, tmpfile, NULL)) {
        if (enableMessageBox) {
            logger.logError("StationList::readXML: " +
                            i18n("error downloading preset file %1").arg(url.url()));
            TQMessageBox::warning(NULL, "TDERadio",
                                 i18n("Download of the station preset file at %1 failed.")
                                 .arg(url.url()));
        } else {
            logger.logWarning("StationList::readXML: " +
                              i18n("error downloading preset file %1").arg(url.url()));
        }
        return false;
    }

    logger.logDebug("StationList::readXML: " +
             i18n("temporary file: ") + tmpfile);

    TQFile presetFile (tmpfile);

    if (! presetFile.open(IO_ReadOnly)) {
        logger.logError("StationList::readXML: " +
                 i18n("error opening preset file %1").arg(tmpfile));
        if (enableMessageBox) {
            TQMessageBox::warning(NULL, "TDERadio",
                                 i18n("Opening of the station preset file at %1 failed.")
                                 .arg(tmpfile));
        }
        return false;
    }

    TQString xmlData;

    // make sure that qtextstream is gone when we close presetFile
    TQString tmp;
    {
        TQTextStream ins(&presetFile);
        tmp = ins.read();
    }

    presetFile.reset();

    // preset file written with tderadio <= 0.2.x
    if (tmp.find("<format>") < 0) {
        logger.logInfo(i18n("Old Preset File Format detected"));
        TQTextStream ins(&presetFile);
        ins.setEncoding(TQTextStream::Locale);
        xmlData = ins.read();
    }
    // preset file written with tderadio >= 0.3.0
    else {
        TQXmlInputSource tmp(TQT_TQIODEVICE(&presetFile));
        xmlData = tmp.data();
    }

    presetFile.close();

    TDEIO::NetAccess::removeTempFile(tmpfile);

    return readXML(xmlData, logger, enableMessageBox);
}


TQString StationList::writeXML (const IErrorLogClient &/*logger*/) const
{
    TQString data = TQString();

    // write station list

    TQString t   = "\t";
    TQString tt  = "\t\t";
    TQString ttt = "\t\t\t";

    data +=       xmlOpenTag(TDERadioConfigElement) +
            t   + xmlOpenTag(StationListElement) +
            tt  + xmlTag(StationListFormat, STATION_LIST_FORMAT) +
            tt  + xmlOpenTag(StationListInfo) +
            ttt + xmlTag(StationListInfoCreator,    "tderadio-" TDERADIO_VERSION) +
            ttt + xmlTag(StationListInfoMaintainer, m_metaData.maintainer) +
            ttt + xmlTag(StationListInfoChanged,    m_metaData.lastChange.toString(Qt::ISODate)) +
            ttt + xmlTag(StationListInfoCountry,    m_metaData.country) +
            ttt + xmlTag(StationListInfoCity,       m_metaData.city) +
            ttt + xmlTag(StationListInfoMedia,      m_metaData.media) +
            ttt + xmlTag(StationListInfoComments,   m_metaData.comment) +
            tt  + xmlCloseTag (StationListInfo);

    for (RawStationList::Iterator it(m_all); it.current(); ++it) {
        RadioStation *s = it.current();

        data += tt + xmlOpenTag (s->getClassName());

        TQStringList properties = s->getPropertyNames();
        TQStringList::iterator end = properties.end();
        for (TQStringList::iterator sit = properties.begin(); sit != end; ++sit) {
            data += ttt + xmlTag (*sit, s->getProperty(*sit));
        }
        data += tt + xmlCloseTag(s->getClassName());

    }

    data += t + xmlCloseTag(StationListElement) +
                xmlCloseTag(TDERadioConfigElement);

    return data;
}


bool StationList::writeXML (const KURL &url, const IErrorLogClient &logger, bool enableMessageBox) const
{
    KTempFile tmpFile;
    tmpFile.setAutoDelete(true);
    TQFile *outf = tmpFile.file();

    TQTextStream outs(outf);
    outs.setEncoding(TQTextStream::UnicodeUTF8);
    outs << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;

    TQString output = writeXML(logger);

    outs << output;
    if (outf->status() != IO_Ok) {
        logger.logError("StationList::writeXML: " +
                 i18n("error writing to tempfile %1").arg(tmpFile.name()));
        if (enableMessageBox) {
            TQMessageBox::warning(NULL, "TDERadio",
                                 i18n("Writing station preset file %1 failed.")
                                 .arg(tmpFile.name()));
        }
        return false;
    }

    // close hopefully flushes buffers ;)
    outf->close();

    if (count() <= 1) {
        logger.logWarning("StationList::writeXML: " +
                 i18n("uploading preset file %1: ").arg(url.url()));
        logger.logWarning("StationList::writeXML: " +
                 i18n("something strange happend, station list has only %1 entries. Writing station preset file skipped").arg(count()));
    } else {

        if (!TDEIO::NetAccess::upload(tmpFile.name(), url, NULL)) {
            logger.logError("StationList::writeXML: " +
                    i18n("error uploading preset file %1").arg(url.url()));

            if (enableMessageBox) {
                TQMessageBox::warning(NULL, "TDERadio",
                                    i18n("Upload of station preset file to %1 failed.")
                                    .arg(url.url()));
            }
            return false;
        }
    }

    return true;
}