/***********************************************************************
* Copyright 2003-2004  Max Howell <max.howell@methylblue.com>
* Copyright 2008-2009  Martin Sandsmark <sandsmark@samfundet.no>
*
* 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) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* 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 <http://www.gnu.org/licenses/>.
***********************************************************************/

#ifndef FILETREE_H
#define FILETREE_H

#include <QtCore/QByteArray> //qstrdup
#include <QtCore/QFile> //decodeName()

#include <stdlib.h>

//TODO these are pointlessly general purpose now, make them incredibly specific

typedef quint32 FileSize;
typedef quint32 Dirsize;  //**** currently unused

template <class T> class Iterator;
template <class T> class ConstIterator;
template <class T> class Chain;

template <class T>
class Link
{
public:
    Link(T* const t) : prev(this), next(this), data(t) {}
    Link() : prev(this), next(this), data(0) {}

//TODO unlinking is slow and you don't use it very much in this context.
//  ** Perhaps you can make a faster deletion system that doesn't bother tidying up first
//  ** and then you MUST call some kind of detach() function when you remove elements otherwise
    ~Link() {
        delete data;
        unlink();
    }

    friend class Iterator<T>;
    friend class ConstIterator<T>;
    friend class Chain<T>;

private:
    void unlink() {
        prev->next = next;
        next->prev = prev;
        prev = next = this;
    }

    Link<T>* prev;
    Link<T>* next;

    T* data; //ensure only iterators have access to this
};


template <class T>
class Iterator
{
public:
    Iterator() : link(0) { } //**** remove this, remove this REMOVE THIS!!! dangerous as your implementation doesn't test for null links, always assumes they can be derefenced
    Iterator(Link<T> *p) : link(p) { }

    bool operator==(const Iterator<T>& it) const {
        return link == it.link;
    }
    bool operator!=(const Iterator<T>& it) const {
        return link != it.link;
    }
    bool operator!=(const Link<T> *p) const {
        return p != link;
    }

    //here we have a choice, really I should make two classes one const the other not
    const T* operator*() const {
        return link->data;
    }
    T* operator*() {
        return link->data;
    }

    Iterator<T>& operator++() {
        link = link->next;    //**** does it waste time returning in places where we don't use the retval?
        return *this;
    }

    bool isNull() const {
        return (link == 0);    //REMOVE WITH ABOVE REMOVAL you don't want null iterators to be possible
    }

    void transferTo(Chain<T> &chain)
    {
        chain.append(remove());
    }

    T* remove() //remove from list, delete Link, data is returned NOT deleted

    {
        T* const d = link->data;
        Link<T>* const p = link->prev;

        link->data = 0;
        delete link;
        link = p; //make iterator point to previous element, YOU must check this points to an element

        return d;
    }

private:
    Link<T> *link;
};


template <class T>
class ConstIterator
{
public:
    ConstIterator(Link<T> *p) : link(p) { }

    bool operator==(const Iterator<T>& it) const {
        return link == it.link;
    }
    bool operator!=(const Iterator<T>& it) const {
        return link != it.link;
    }
    bool operator!=(const Link<T> *p) const {
        return p != link;
    }

    const T* operator*() const {
        return link->data;
    }

    ConstIterator<T>& operator++() {
        link = link->next;
        return *this;
    }

private:
    const Link<T> *link;
};


template <class T>
class Chain
{
public:
    virtual ~Chain() {
        empty();
    }

    void append(T* const data)
    {
        Link<T>* const link = new Link<T>(data);

        link->prev = head.prev;
        link->next = &head;

        head.prev->next = link;
        head.prev = link;
    }

    void transferTo(Chain &c)
    {
        if (isEmpty()) return;

        Link<T>* const first = head.next;
        Link<T>* const last  = head.prev;

        head.unlink();

        first->prev = c.head.prev;
        c.head.prev->next = first;

        last->next = &c.head;
        c.head.prev = last;
    }

    void empty() {
        while (head.next != &head) {
            delete head.next;
        }
    }

    Iterator<T>      iterator()      const {
        return Iterator<T>(head.next);
    }
    ConstIterator<T> constIterator() const {
        return ConstIterator<T>(head.next);
    }
    const Link<T>   *end()           const {
        return &head;
    }
    bool             isEmpty()       const {
        return head.next == &head;
    }

private:
    Link<T> head;
    void operator=(const Chain&);
};


class Folder;
class QString;

class File
{
public:
    friend class Folder;

    enum UnitPrefix { kilo, mega, giga, tera };

    static const uint DENOMINATOR[4];

public:
    File(const char *name, FileSize size) : m_parent(0), m_name(qstrdup(name)), m_size(size) {}
    virtual ~File() {
        delete [] m_name;
    }

    const Folder *parent() const {
        return m_parent;
    }
    const char *name8Bit() const {
        return m_name;
    }
    FileSize size() const {
        return m_size;
    }
    QString name() const {
        return QFile::decodeName(m_name);
    }

    virtual bool isFolder() const {
        return false;
    }

    QString fullPath(const Folder* = 0) const;
    QString humanReadableSize(UnitPrefix key = mega) const;

public:
    static QString humanReadableSize(uint size, UnitPrefix Key = mega);

protected:
    File(const char *name, FileSize size, Folder *parent) : m_parent(parent), m_name(qstrdup(name)), m_size(size) {}

    Folder *m_parent; //0 if this is treeRoot
    char      *m_name;
    FileSize   m_size;   //in units of KiB

private:
    File(const File&);
    void operator=(const File&);
};


class Folder : public Chain<File>, public File
{
public:
    Folder(const char *name) : File(name, 0), m_children(0) {} //DON'T pass the full path!

    uint children() const {
        return m_children;
    }
    virtual bool isFolder() const {
        return true;
    }

    ///appends a Folder
    void append(Folder *d, const char *name=0)
    {
        if (name) {
            delete [] d->m_name;
            d->m_name = qstrdup(name);
        } //directories that had a fullpath copy just their names this way

        m_children += d->children(); //doesn't include the dir itself
        d->m_parent = this;
        append((File*)d); //will add 1 to filecount for the dir itself
    }

    ///appends a File
    void append(const char *name, FileSize size)
    {
        append(new File(name, size, this));
    }

private:
    void append(File *p)
    {
        m_children++;
        m_size += p->size();
        Chain<File>::append(p);
    }

    uint m_children;

private:
    Folder(const Folder&); //undefined
    void operator=(const Folder&); //undefined
};

#endif
