/* This file is part of the KOffice project
 * Copyright (C) 2005 Thomas Zander <zander@kde.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; version 2.

 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */
#include <algorithm>
#include "KWFrameList.h"
#include "KWFrame.h"
#include "KWFrameSet.h"
#include "KWTextFrameSet.h"
#include "KWDocument.h"
#include "KWViewMode.h"
#include "KWPageManager.h"

//#define DEBUG_SPEED

KWFrameList::KWFrameList(KWDocument *doc, KWFrame *theFrame) {
    m_doc = doc;
    m_frame = theFrame;

    update();
}

QValueList<KWFrame *> KWFrameList::framesBelow() const {
    QValueList<KWFrame *> frames;
//kdDebug() << "framesBelow " << endl;

    // Copy until we find m_frame
    for ( QValueVector<KWFrame*>::const_iterator it = m_frames.begin(), end = m_frames.end(); it != end && *it != m_frame; ++it) {
        frames.append( *it );
    }

    return  frames;
}

QValueList<KWFrame *> KWFrameList::framesOnTop() const {
//kdDebug() << "framesOnTop " << endl;
    QValueList<KWFrame *> frames;

    // Copy from m_frame to the end
    bool found = false;
    for ( QValueVector<KWFrame*>::const_iterator it = m_frames.begin(), end = m_frames.end(); it != end; ++it) {
        KWFrame* frame = *it;
        if ( found ) {
            Q_ASSERT( !frame->frameSet()->isFloating() );
            frames.append( frame );
        }
        else if ( frame == m_frame )
            found = true;
    }

    return frames;
}

void KWFrameList::setFrames(const QPtrList<KWFrame> &frames) {
    // kdDebug(31001) << "KWFrameList::setFrames for " << m_frame->frameSet()->name() << endl;
    m_frames.clear();
    if ( m_doc->layoutViewMode() && !m_doc->layoutViewMode()->hasFrames() )
        return;

    QPtrList<KWFrameSet> parentFramesets;
    KWFrameSet *fs = m_frame->frameSet();
    while(fs) {
        parentFramesets.append(fs);
        fs = (KWFrameSet*) fs->anchorFrameset();
    }

    // We now look at all other frames (in the same page)
    // to check for intersections. This is o(n^2), but with n small.
    QPtrListIterator<KWFrame> it( frames );
    for ( ; it.current() ; ++it )
    {
        KWFrame* daFrame = it.current();
        // kdDebug(32001) << "frame: " << daFrame->frameSet()->name() << endl;
        if ( m_frame == daFrame ) {
            m_frames.append( daFrame );
            continue;
        }
        // Skip 'daFrame' if it belongs to a table.
        // We trust that KWTableFrameSet will not make cells overlap ;)
        if ( m_frame->frameSet()->groupmanager() || daFrame->frameSet()->groupmanager() )
            continue;
        // Skip all frames from the parent frameset, if 'm_frame' is floating
        // ## might need a for loop for the case of inline-inside-inline,
        // or maybe calling isPaintedBy instead [depending on what should happen for tables]
        if ( daFrame->frameSet()->isFloating() &&
                (parentFramesets.contains(daFrame->frameSet()->anchorFrameset()) ||
                 daFrame->frameSet()->isPaintedBy(m_frame->frameSet())) )
            continue;
        // Floating frames are not "on top", they are "inside".
        // They are not "below" anything either - the parent frameset is.
        if ( m_frame->frameSet()->isFloating() )
            continue;
        KoRect intersect = m_frame->intersect( daFrame->outerKoRect() );
        if ( !intersect.isEmpty() )
            m_frames.append( daFrame );
    }
    std::sort( m_frames.begin(), m_frames.end(), KWFrame::compareFrameZOrder );
}

void KWFrameList::updateAfterMove(int oldPageNum) {
    int pageNumber = m_doc->pageManager()->pageNumber(m_frame);
    updateZOrderFor(m_doc->framesInPage( pageNumber, false ));

    if (pageNumber != oldPageNum)
        updateZOrderFor(m_doc->framesInPage( oldPageNum, false ));
}

void KWFrameList::update() {
    int pageNumber = m_doc->pageManager()->pageNumber(m_frame);
    if(pageNumber == -1)
        return;
    updateZOrderFor(m_doc->framesInPage( pageNumber, false ));
}

void KWFrameList::updateZOrderFor(const QPtrList<KWFrame> &frames) {
#ifdef DEBUG_SPEED
    kdDebug(32001) << "KWFrameList::updateZOrderFor " << frames.count() << " frames"<< endl;
    QTime dt;
    dt.start();
    int numberAdded = 0;
#endif

    QPtrListIterator<KWFrame> iter(frames);
    while( iter.current() ) {
        KWFrame *frame = iter.current();
        Q_ASSERT( frame->frameStack() );

        frame->frameStack()->setFrames(frames);
#ifdef DEBUG_SPEED
        numberAdded += frame->frameStack()->m_frames.count();
#endif
        ++iter;
    }

#ifdef DEBUG_SPEED
    kdDebug(32001) << "  updateZOrderFor took " << (float)(dt.elapsed()) / 1000 << " seconds, added " << numberAdded << " frames" << endl;
#endif
}

// ****** statics ******
KWFrameList *KWFrameList::getFirstFrameList(KWDocument *doc) {
    for (QPtrListIterator<KWFrameSet> fsit = doc->framesetsIterator(); fsit.current() ; ++fsit ) {
        KWFrame *frame = fsit.current()->frame(0);
        if (frame && frame->frameStack())
            return frame->frameStack();
    }
    return 0;
}

void KWFrameList::recalcFrames(KWDocument *doc, int pageFrom, int pageTo) {
    for(int i=pageTo; i >= pageFrom; i--) {
        QPtrList<KWFrame> framesOnPage = doc->framesInPage( i, false );
        KWFrame *f = framesOnPage.first();
        while(f) {
            Q_ASSERT(f->frameStack());
            f->frameStack()->setFrames(framesOnPage);
            f = framesOnPage.next();
        }
    }
}

void KWFrameList::recalcAllFrames(KWDocument *doc) {
    recalcFrames(doc, doc->startPage(), doc->lastPage());
}

void KWFrameList::createFrameList(KWFrame *f, KWDocument *doc) {
    Q_ASSERT(f);
    Q_ASSERT(doc);
    if(f->frameStack())
        return;
    f->setFrameStack(new KWFrameList(doc, f));
}

void KWFrameList::createFrameList(KWFrameSet *fs, KWDocument *doc, bool forceUpdate) {
    QPtrListIterator<KWFrame> iter( fs->frameIterator() );
    KWFrame *f = iter.current();
    while(f) {
        createFrameList(f, doc);
        if(forceUpdate)
            f->frameStack()->update();
        ++iter;
        f = iter.current();
    }
}