/****************************************************************************
**
** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (developer.feedback@nokia.com)
**
** This file is part of the HbCore module of the UI Extensions for Mobile.
**
** GNU Lesser General Public License Usage
** 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.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at developer.feedback@nokia.com.
**
****************************************************************************/

#include "hbthemeindex_p.h"
#include "hbhash_p.h"
#include <QDebug>
#ifndef HB_BOOTSTRAPPED
#include "hbthemeclient_p.h"
#include "hbinstance.h"
#endif // HB_BOOTSTRAPPED


HbThemeIndexResource::HbThemeIndexResource(const QString &resourceName) :
    resourceName(&resourceName),
    hashValue(0),
    data(0)
{
#ifndef HB_BOOTSTRAPPED
    getResourceData();
#endif // HB_BOOTSTRAPPED
}

HbThemeIndexResource::HbThemeIndexResource(quint32 hashValue) :
    resourceName(0),
    hashValue(hashValue),
    data(0)
{
#ifndef HB_BOOTSTRAPPED
    getResourceData();
#endif // HB_BOOTSTRAPPED
}

void HbThemeIndexResource::getResourceData()
{
#ifndef HB_BOOTSTRAPPED
#ifdef THEME_INDEX_TRACES
    if ( resourceName ) {
        qDebug() << "HbThemeIndexResource::getResourceData(), resourceName: " << *resourceName;
    } else {
        qDebug() << "HbThemeIndexResource::getResourceData(), hashValue: " << hashValue;
    }
#endif
    // Theme index tables are always valid in shared memory

    // Try to find themable item with following logic:
    // If item is locked in base theme
    //     Select item from base theme
    // Else if item is found from C-drive operator theme
    //     Select item from C-drive operator theme
    // Else if item is found from ROM operator theme
    //    Select item from ROM operator theme
    // Else if item is found from active theme
    //    Select item from active theme
    // Else
    //    Select item from base theme

    type = BaseTheme; // This is the default, even we couldn't find themable resource

    // First check base theme, which should be always valid
    HbThemeIndexInfo info = HbThemeUtils::getThemeIndexInfo(BaseTheme);
    if (!info.address) { // This shouldn't happen, as there must be valid base theme
        THEME_INDEX_DEBUG() << "HbThemeUtils::getThemeIndexInfo(BaseTheme) returned null address";
        return; // Data will be 0
    }
    
    HbThemeIndex baseIndex(info.address);
    const HbThemeIndexItemData *baseItemData = resourceName
        ? baseIndex.getItemData(*resourceName)
        : baseIndex.getItemData(hashValue);

    if (!baseItemData) { // If the item is not found from base theme, it can't be found elsewhere
#ifdef THEME_INDEX_TRACES
        if ( resourceName ) {
            qDebug("HbThemeIndex::getItemData(%s) returned null data", qPrintable(*resourceName));
        } else {
            qDebug("HbThemeIndex::getItemData(%u) returned null data", hashValue);
        }
#endif
        return; // Data will be 0
    }

    if (baseItemData->flags & HbThemeIndexItemData::Locked) {
        basePath = info.path;
        themeName = info.name;
        data = baseItemData;
        return;
    }

    // Base wasn't locked, next check operator theme in C-drive
    info = HbThemeUtils::getThemeIndexInfo(OperatorC);
    if (info.address) {
        HbThemeIndex operatorCIndex(info.address);
        const HbThemeIndexItemData *operatorCItemData = resourceName
            ? operatorCIndex.getItemData(*resourceName)
            : operatorCIndex.getItemData(hashValue);

        if (operatorCItemData) { // Found, use it
            type = OperatorC;
            basePath = info.path;
            themeName = info.name;
            data = operatorCItemData;
            return;
        }
    }

    // Not found from operator theme in C-drive, next check operator theme in ROM
    info = HbThemeUtils::getThemeIndexInfo(OperatorROM);
    if (info.address) {
        HbThemeIndex operatorZIndex(info.address);
        const HbThemeIndexItemData *operatorZItemData = resourceName
            ? operatorZIndex.getItemData(*resourceName)
            : operatorZIndex.getItemData(hashValue);

        if (operatorZItemData) { // Found, use it
            type = OperatorROM;
            basePath = info.path;
            themeName = info.name;
            data = operatorZItemData;
            return;
        }
    }

    // Not found from operator themes, try active theme
    info = HbThemeUtils::getThemeIndexInfo(ActiveTheme);
    if (info.address) {
        HbThemeIndex activeThemeIndex(info.address);
        const HbThemeIndexItemData *activeThemeItemData = resourceName
            ? activeThemeIndex.getItemData(*resourceName)
            : activeThemeIndex.getItemData(hashValue);

        if (activeThemeItemData) { // Found, use it
            type = ActiveTheme;
            basePath = info.path;
            themeName = info.name;
            data = activeThemeItemData;
            return;
        }
    }

    // Not found from active theme, use base
    info = HbThemeUtils::getThemeIndexInfo(BaseTheme);
    basePath = info.path;
    themeName = info.name;
    data = baseItemData;
    return;
#endif // HB_BOOTSTRAPPED
}


HbThemeIndexResource::~HbThemeIndexResource()
{
}

bool HbThemeIndexResource::isValid()
{
    if (data && data->itemType == HbThemeIndexItemData::ColorItem) {
        Q_ASSERT_X(!(data->flags & HbThemeIndexItemData::Reference),
            "HbThemeIndexResource::isValid()",
            "Reference to another color variable not supported");
    }

    if (data) {
        return true;
    }
    return false;
}

const QSize HbThemeIndexResource::defaultItemSize()
{
    if (data) {
        return QSize(data->defaultWidth, data->defaultHeight);
    }
    return QSize();
}

const QSize HbThemeIndexResource::mirroredItemSize()
{
    if (data) {
        return QSize(data->mirroredWidth, data->mirroredHeight);
    }
    return QSize();
}

bool HbThemeIndexResource::isAutomaticallyMirrored()
{
    if (data) {
        return (data->flags & HbThemeIndexItemData::Mirrorable);
    }
    return false;
}

bool HbThemeIndexResource::isLocked()
{
    if (data) {
        return (data->flags & HbThemeIndexItemData::Locked);
    }
    return false;
}

QString HbThemeIndexResource::fullFileName()
{
    if (!data || !resourceName) {
        return QString();
    }

    QString fullName = basePath;
    switch (data->itemType) {
        case HbThemeIndexItemData::SvgItem:
            {
            fullName = fullName + "/icons/" + themeName + "/scalable/" + *resourceName + ".svg";
            break;
            }
        case HbThemeIndexItemData::PngItem:
            {
            fullName = fullName + "/icons/" + themeName + "/pixmap/" + *resourceName + ".png";
            break;
            }
        case HbThemeIndexItemData::MngItem:
            {
            fullName = fullName + "/icons/" + themeName + "/pixmap/" + *resourceName + ".mng";
            break;
            }
        case HbThemeIndexItemData::GifItem:
            {
            fullName = fullName + "/icons/" + themeName + "/pixmap/" + *resourceName + ".gif";
            break;
            }
        case HbThemeIndexItemData::XpmItem:
            {
            fullName = fullName + "/icons/" + themeName + "/pixmap/" + *resourceName + ".xpm";
            break;
            }
        case HbThemeIndexItemData::JpgItem:
            {
            fullName = fullName + "/icons/" + themeName + "/pixmap/" + *resourceName + ".jpg";
            break;
            }
        case HbThemeIndexItemData::NvgItem:
            {
            fullName = fullName + "/icons/" + themeName + "/scalable/" + *resourceName + ".nvg";
            break;
            }
        case HbThemeIndexItemData::SvgzItem:
            {
            fullName = fullName + "/icons/" + themeName + "/scalable/" + *resourceName + ".svgz";
            break;
            }
        case HbThemeIndexItemData::QpicItem:
            {
            fullName = fullName + "/icons/" + themeName + "/pixmap/" + *resourceName + ".qpic";
            break;
            }
        case HbThemeIndexItemData::AxmlItem:
            {
            fullName = fullName + "/animations/" + themeName + '/' + *resourceName;
            break;
            }
        case HbThemeIndexItemData::FxmlItem:
            {
            fullName = fullName + "/effects/" + themeName + '/' + *resourceName;
            break;
            }
        default:
            {
            break;
            }
        }

    return fullName;
}

QString HbThemeIndexResource::fullMirroredFileName(bool &mirroredIconFound)
{
    mirroredIconFound = false;
    if (!data || !resourceName) {
        return QString();
    }

    QString fullName = basePath;
    switch (data->mirroredItemType) {
        case HbThemeIndexItemData::SvgItem:
            {
            fullName = fullName + "/icons/" + themeName + "/scalable/mirrored/"
                       + *resourceName + ".svg";
            break;
            }
        case HbThemeIndexItemData::PngItem:
            {
            fullName = fullName + "/icons/" + themeName + "/pixmap/mirrored/"
                       + *resourceName + ".png";
            break;
            }
        case HbThemeIndexItemData::MngItem:
            {
            fullName = fullName + "/icons/" + themeName + "/pixmap/mirrored/"
                       + *resourceName + ".mng";
            break;
            }
        case HbThemeIndexItemData::GifItem:
            {
            fullName = fullName + "/icons/" + themeName + "/pixmap/mirrored/"
                       + *resourceName + ".gif";
            break;
            }
        case HbThemeIndexItemData::XpmItem:
            {
            fullName = fullName + "/icons/" + themeName + "/pixmap/mirrored/"
                       + *resourceName + ".xpm";
            break;
            }
        case HbThemeIndexItemData::JpgItem:
            {
            fullName = fullName + "/icons/" + themeName + "/pixmap/mirrored/"
                       + *resourceName + ".jpg";
            break;
            }
        case HbThemeIndexItemData::NvgItem:
            {
            fullName = fullName + "/icons/" + themeName + "/scalable/mirrored/"
                       + *resourceName + ".nvg";
            break;
            }
        case HbThemeIndexItemData::SvgzItem:
            {
            fullName = fullName + "/icons/" + themeName + "/scalable/mirrored/"
                       + *resourceName + ".svgz";
            break;
            }
        case HbThemeIndexItemData::QpicItem:
            {
            fullName = fullName + "/icons/" + themeName + "/pixmap/mirrored/"
                       + *resourceName + ".qpic";
            break;
            }
        default:
            {
            return fullFileName(); // There was no mirrored icon, return normal icon
            }
        }

    mirroredIconFound = true;
    return fullName;
}

QColor HbThemeIndexResource::colorValue()
{
    if (!data || data->itemType != HbThemeIndexItemData::ColorItem) {
        THEME_INDEX_DEBUG("HbThemeIndexResource::colorValue(): cannot fetch color for 0x%p::%i",
                 data, data ? data->itemType : (uint) -1);
        return QColor();
    }
    THEME_INDEX_DEBUG() << "HbThemeIndexResource::colorValue(): constructing QColor with value:" << data->colorValue;
    return QColor(data->colorValue);
}


// Class HbThemeIndex has the logic of handling different versions of
// the theme index file formats.

HbThemeIndex::HbThemeIndex(const char *baseAddress) :
    mBaseAddress(baseAddress),
    mItemCount(0),
    mThemeItemDataArray(0),
    initialized(false)
{
}

HbThemeIndex::~HbThemeIndex()
{
}

void HbThemeIndex::init()
{
    //int version = *(reinterpret_cast<const int *>(mBaseAddress));
    // Assumes version 1 for now
    const HbThemeIndexHeaderV1 *header =
            reinterpret_cast<const HbThemeIndexHeaderV1 *>(mBaseAddress);
    mItemCount = header->itemCount;
    mThemeItemDataArray = reinterpret_cast<const HbThemeIndexItemData *>
                            (mBaseAddress + sizeof(HbThemeIndexHeaderV1));

    initialized = true;
}

const HbThemeIndexItemData *HbThemeIndex::getItemData(const QString &itemName)
{
    return getItemData(hbHash(itemName));
}

const HbThemeIndexItemData *HbThemeIndex::getItemData(quint32 hashValue)
{
    if (!initialized) {
        init();
    }

    int begin = 0;
    int end = mItemCount - 1;

    const HbThemeIndexItemData *retItem = 0;

    // binary search
    while (begin <= end) {
        int mid = begin + (end - begin) / 2;

        if (mThemeItemDataArray[mid].itemNameHash == hashValue) {
            retItem = &mThemeItemDataArray[mid];
            return retItem;
        } else if (hashValue < mThemeItemDataArray[mid].itemNameHash) {
            end = mid - 1;
        } else {
            begin = mid + 1;
        }
    }   

    // Did not find the target, return 0.
    return retItem;
}

int HbThemeIndex::itemCount()
{
    if (!initialized) {
        init();
    }

    return mItemCount;
}

bool HbThemeIndex::validateItems(qint64 byteSize)
{
    if (!initialized) {
        init();
    }

    bool indexOK = false;

    qint64 indexCalculatedSize = (qint64)(sizeof(HbThemeIndexHeaderV1) +
        (mItemCount * sizeof(HbThemeIndexItemData)));

    if (indexCalculatedSize == byteSize) {
        indexOK = true;
    }

    if (!indexOK) {
        THEME_INDEX_DEBUG() <<  "ThemeIndex: Index file corrupted (index size is wrong)!";
    }

    return indexOK;
}
