/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://www.qtsoftware.com/contact.
**
**************************************************************************/

#include "cdbstackframecontext.h"
#include "cdbsymbolgroupcontext.h"
#include "cdbdumperhelper.h"
#include "debuggeractions.h"
#include "watchhandler.h"

#include <QtCore/QDebug>

enum { debug = 0 };

namespace Debugger {
namespace Internal {

enum { OwnerNewItem, OwnerSymbolGroup, OwnerDumper };

typedef QSharedPointer<CdbDumperHelper> SharedPointerCdbDumperHelper;
typedef QList<WatchData> WatchDataList;

// Put a sequence of  WatchData into the model
class WatchHandlerModelInserter {
public:
    explicit WatchHandlerModelInserter(WatchHandler *wh) : m_wh(wh) {}

    inline WatchHandlerModelInserter & operator*() { return *this; }
    inline WatchHandlerModelInserter &operator=(const WatchData &wd)  {
        m_wh->insertData(wd);
        return *this;
    }
    inline WatchHandlerModelInserter &operator++() { return *this; }

private:
    WatchHandler *m_wh;
};

// Helper to sort apart a sequence of  WatchData using the Dumpers.
// Puts the stuff for which there is no dumper in the model
// as is and sets ownership to symbol group. The rest goes
// to the dumpers.
class WatchHandlerSorterInserter {
public:
    explicit WatchHandlerSorterInserter(WatchHandler *wh,
                                        const SharedPointerCdbDumperHelper &dumper);

    inline WatchHandlerSorterInserter & operator*() { return *this; }
    inline WatchHandlerSorterInserter &operator=(WatchData wd);
    inline WatchHandlerSorterInserter &operator++() { return *this; }

private:
    WatchHandler *m_wh;
    const SharedPointerCdbDumperHelper m_dumper;
    QList<WatchData> m_dumperResult;
    QStringList m_dumperINames;
};

WatchHandlerSorterInserter::WatchHandlerSorterInserter(WatchHandler *wh,
                                                       const SharedPointerCdbDumperHelper &dumper) :
    m_wh(wh),
    m_dumper(dumper)
{
}

WatchHandlerSorterInserter &WatchHandlerSorterInserter::operator=(WatchData wd)
{
    // Is this a child belonging to some item handled by dumpers,
    // such as d-elements of QStrings -> just ignore it.
    if (const int dumperINamesCount = m_dumperINames.size()) {
        for (int i = 0; i < dumperINamesCount; i++)
            if (wd.iname.startsWith(m_dumperINames.at(i)))
                return *this;
    }
    QString errorMessage;
    switch (m_dumper->dumpType(wd, true, OwnerDumper, &m_dumperResult, &errorMessage)) {
    case CdbDumperHelper::DumpOk:
        // Discard the original item and insert the dumper results
        m_dumperINames.push_back(wd.iname + QLatin1Char('.'));
        foreach(const WatchData &dwd, m_dumperResult)
            m_wh->insertData(dwd);
        break;
    case CdbDumperHelper::DumpNotHandled:
    case CdbDumperHelper::DumpError:
        wd.source = OwnerSymbolGroup;
        m_wh->insertData(wd);
        break;
    }
    return *this;
}

// -----------CdbStackFrameContext
CdbStackFrameContext::CdbStackFrameContext(const QSharedPointer<CdbDumperHelper> &dumper,
                                           CdbSymbolGroupContext *symbolContext) :
        m_useDumpers(dumper->isEnabled() && theDebuggerBoolSetting(UseDebuggingHelpers)),
        m_dumper(dumper),
        m_symbolContext(symbolContext)
{
}

bool CdbStackFrameContext::assignValue(const QString &iname, const QString &value,
                                       QString *newValue /* = 0 */, QString *errorMessage)
{
    return m_symbolContext->assignValue(iname, value, newValue, errorMessage);
}

bool CdbStackFrameContext::populateModelInitially(WatchHandler *wh, QString *errorMessage)
{
    if (debug)
        qDebug() << "populateModelInitially";
    const bool rc = m_useDumpers ?
        CdbSymbolGroupContext::populateModelInitially(m_symbolContext,
                                                      wh->expandedINames(),
                                                      WatchHandlerSorterInserter(wh, m_dumper),                                                      
                                                      errorMessage) :
        CdbSymbolGroupContext::populateModelInitially(m_symbolContext,
                                                      wh->expandedINames(),
                                                      WatchHandlerModelInserter(wh),
                                                      errorMessage);
    return rc;
}

bool CdbStackFrameContext::completeModel(const QList<WatchData> &incompleteLocals,
                                         WatchHandler *wh,
                                         QString *errorMessage)
{
    if (debug) {
        QDebug nsp = qDebug().nospace();
        nsp << ">completeModel ";
        foreach (const WatchData &wd, incompleteLocals)
            nsp << wd.iname << ' ';
        nsp << '\n';
    }

    if (!m_useDumpers) {
        return CdbSymbolGroupContext::completeModel(m_symbolContext, incompleteLocals,
                                                    WatchHandlerModelInserter(wh),
                                                    errorMessage);
    }

    // Expand dumper items
    int handledDumpers = 0;
    foreach (const WatchData &cwd, incompleteLocals) {
        if (cwd.source == OwnerDumper) { // You already know that.
            WatchData wd = cwd;
            wd.setAllUnneeded();
            wh->insertData(wd);
            handledDumpers++;
        }
    }
    if (handledDumpers == incompleteLocals.size())
        return true;
    // Expand symbol group items
    return CdbSymbolGroupContext::completeModel(m_symbolContext, incompleteLocals,
                                                WatchHandlerSorterInserter(wh, m_dumper),
                                                errorMessage);
}

CdbStackFrameContext::~CdbStackFrameContext()
{
    delete m_symbolContext;
}

bool CdbStackFrameContext::editorToolTip(const QString &iname,
                                         QString *value,
                                         QString *errorMessage)
{
    value->clear();
    unsigned long index;
    if (!m_symbolContext->lookupPrefix(iname, &index)) {
        *errorMessage = QString::fromLatin1("%1 not found.").arg(iname);
        return false;
    }
    const WatchData wd = m_symbolContext->symbolAt(index);
    // Check dumpers. Should actually be just one item.
    if (m_useDumpers && m_dumper->state() != CdbDumperHelper::Disabled) {
        QList<WatchData> result;
        if (CdbDumperHelper::DumpOk == m_dumper->dumpType(wd, false, OwnerDumper, &result, errorMessage))  {
            foreach (const WatchData &dwd, result) {
                if (!value->isEmpty())
                    value->append(QLatin1Char('\n'));
                value->append(dwd.toToolTip());
            }
            return true;
        } // Dumped ok
    }     // has Dumpers
    *value = wd.toToolTip();
    return true;
}

} // namespace Internal
} // namespace Debugger
