/*
 * This file is part of the DOM implementation for KDE.
 *
 * Copyright (C) 2008, 2009 Maksim Orlovich (maksim@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; either
 * version 2 of the License, or (at your option) any later version.
 *
 * 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 "idstring.h"
#include <assert.h>

namespace khtml {

CaseNormalizeMode IDTableBase::MappingKey::caseNormalizationMode;

bool IDTableBase::MappingKey::operator==(const MappingKey& other) const
{
    if (IDTableBase::MappingKey::caseNormalizationMode == IDS_CaseSensitive)
        return !strcmp(str, other.str);
    else
        return !strcasecmp(str, other.str);
}

static inline unsigned int qHash(const IDTableBase::MappingKey& key) {
    if (!key.str) {
        return 82610334; //same as empty
    } else if (key.caseNormalizationMode == IDS_CaseSensitive) {
        return key.str->hash();
    } else if (key.caseNormalizationMode == IDS_NormalizeLower) {
        return key.str->lowerHash();
    } else { // caseNormalizationMode == IDS_NormalizeUpper
        return key.str->upperHash();
    }
}

void IDTableBase::releaseId(unsigned id)
{
    IDTableBase::MappingKey::caseNormalizationMode = IDS_CaseSensitive;

    m_mappingLookup.remove(m_mappings[id].name);
    m_mappings[id].name->deref();
    m_idFreeList.append(id);
}

unsigned short IDTableBase::grabId(DOMStringImpl* origName, CaseNormalizeMode cnm)
{
    unsigned short newId;

    // Check for existing one, ignoring case if needed
    IDTableBase::MappingKey::caseNormalizationMode = cnm;
    QHash<MappingKey, unsigned short>::const_iterator i = m_mappingLookup.constFind(origName);
    if (i != m_mappingLookup.constEnd()) {
        newId = *i;
        refId(newId);
        return newId;
    }

    // Nope. Allocate new ID. If there is normalization going on, we may now have to 
    // update our case so the canonical mapping is of the expected case. We
    // may also have to deep-copy 
    DOMStringImpl* name;
    switch (cnm) {
    case IDS_CaseSensitive:
        if (origName->m_shallowCopy) {
            // Create a new copy of the data since we may be extending its
            // lifetime indefinitely
            name = new DOMStringImpl(origName->s, origName->l);
            name->m_hash = origName->m_hash;
        } else {
            name = origName;
        }
        break;
    case IDS_NormalizeUpper:
        name = origName->upper();
        break;
    case IDS_NormalizeLower:
        name = origName->lower();
        break;
    }
    
    name->ref();

    if (!m_idFreeList.isEmpty()) {
        // Grab from freelist..
        newId = m_idFreeList.last();
        m_idFreeList.removeLast();
        m_mappings[newId].name = name;
    } else {
        // Make a new one --- if we can (we keep one spot for "last resort" mapping)
        if (m_mappings.size() < 0xFFFE) {
            m_mappings.append(Mapping(name));
            newId = m_mappings.size() - 1;
        } else {
            // We ran out of resources. Did we add a fallback mapping yet?
            // We use the same one for everything; and don't even keep track
            // of what it may go to, as we will have no way of freeing
            // the aliases. In particular, this means we no longer need the name..
            name->deref();
            
            if (m_mappings.size() == 0xFFFE) {
                // Need a new mapping..
                name = new DOMStringImpl("_khtml_fallback");
                m_mappings.append(Mapping(name));
                m_mappings[0xFFFF].refCount = 1; // pin it.
                name->ref();
            } else {
                name = m_mappings[0xFFFF].name; // No need to ref the name
                                                // here as the entry is eternal anyway
            }
            newId = 0xFFFF;
        }
    }

    m_mappingLookup[name] = newId;

    refId(newId);
    return newId;
}

void IDTableBase::addStaticMapping(unsigned id, const DOMString& name)
{
    DOMStringImpl* nameImpl = name.implementation();
    if (nameImpl) nameImpl->ref();
    
    IDTableBase::MappingKey::caseNormalizationMode = IDS_CaseSensitive;

    assert(id == m_mappings.size());
    assert(!m_mappingLookup.contains(nameImpl));
    m_mappings.append(Mapping(nameImpl));
    m_mappings[m_mappings.size() - 1].refCount = 1; // Pin it.
    m_mappingLookup[nameImpl] = id;
}

void IDTableBase::addHiddenMapping(unsigned id, const DOMString& name)
{
    DOMStringImpl* nameImpl = name.implementation();
    if (nameImpl) nameImpl->ref();

    assert(id == m_mappings.size());
    m_mappings.append(Mapping(nameImpl));
    m_mappings[m_mappings.size() - 1].refCount = 1; // Pin it.    
}

}

// kate: indent-width 4; replace-tabs on; tab-width 4; space-indent on;
