/**************************************************************
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_store.hxx"

#include "stortree.hxx"

#include "sal/types.h"
#include "osl/diagnose.h"

#include "store/types.h"

#include "storbase.hxx"
#include "storbios.hxx"

using namespace store;

/*========================================================================
 *
 * OStoreBTreeNodeData implementation.
 *
 *======================================================================*/
/*
 * OStoreBTreeNodeData.
 */
OStoreBTreeNodeData::OStoreBTreeNodeData (sal_uInt16 nPageSize)
	: OStorePageData (nPageSize)
{
	base::m_aGuard.m_nMagic = store::htonl(self::theTypeId);
	base::m_aDescr.m_nUsed  = store::htons(self::thePageSize); // usageCount(0)
	self::m_aGuard.m_nMagic = store::htonl(0); // depth(0)

	sal_uInt16 const n = capacityCount();
	T const          t;

	for (sal_uInt16 i = 1; i < n; i++)
		m_pData[i] = t;
}

/*
 * find.
 */
sal_uInt16 OStoreBTreeNodeData::find (const T& t) const
{
	sal_Int32 l = 0;
	sal_Int32 r = usageCount() - 1;

	while (l < r)
	{
		sal_Int32 const m = ((l + r) >> 1);

		if (t.m_aKey == m_pData[m].m_aKey)
			return ((sal_uInt16)(m));
		if (t.m_aKey < m_pData[m].m_aKey)
			r = m - 1;
		else
			l = m + 1;
	}

	sal_uInt16 const k = ((sal_uInt16)(r));
	if ((k < capacityCount()) && (t.m_aKey < m_pData[k].m_aKey))
		return(k - 1);
	else
		return(k);
}

/*
 * insert.
 */
void OStoreBTreeNodeData::insert (sal_uInt16 i, const T& t)
{
	sal_uInt16 const n = usageCount();
	sal_uInt16 const m = capacityCount();
	if ((n < m) && (i < m))
	{
		// shift right.
		memmove (&(m_pData[i + 1]), &(m_pData[i]), (n - i) * sizeof(T));

		// insert.
		m_pData[i] = t;
        usageCount (n + 1);
	}
}

/*
 * remove.
 */
void OStoreBTreeNodeData::remove (sal_uInt16 i)
{
	sal_uInt16 const n = usageCount();
	if (i < n)
	{
		// shift left.
		memmove (&(m_pData[i]), &(m_pData[i + 1]), (n - i - 1) * sizeof(T));

		// truncate.
		m_pData[n - 1] = T();
        usageCount (n - 1);
	}
}

#if 0  /* NYI */
/*
 * merge (with right page).
 */
void OStoreBTreeNodeData::merge (const self& rPageR)
{
    sal_uInt16 const n = usageCount();
    sal_uInt16 const m = rPageR.usageCount();
    if ((n + m) <= capacityCount())
	{
		memcpy (&(m_pData[n]), &(rPageR.m_pData[0]), m * sizeof(T));
		usageCount (n + m);
	}
}
#endif

/*
 * split (left half copied from right half of left page).
 */
void OStoreBTreeNodeData::split (const self& rPageL)
{
	sal_uInt16 const h = capacityCount() / 2;
	memcpy (&(m_pData[0]), &(rPageL.m_pData[h]), h * sizeof(T));
	truncate (h);
}

/*
 * truncate.
 */
void OStoreBTreeNodeData::truncate (sal_uInt16 n)
{
	sal_uInt16 const m = capacityCount();
	T const          t;

	for (sal_uInt16 i = n; i < m; i++)
		m_pData[i] = t;
	usageCount (n);
}

/*========================================================================
 *
 * OStoreBTreeNodeObject implementation.
 *
 *======================================================================*/
/*
 * guard.
 */
storeError OStoreBTreeNodeObject::guard (sal_uInt32 nAddr)
{
	return PageHolderObject< page >::guard (m_xPage, nAddr);
}

/*
 * verify.
 */
storeError OStoreBTreeNodeObject::verify (sal_uInt32 nAddr) const
{
	return PageHolderObject< page >::verify (m_xPage, nAddr);
}

/*
 * split.
 */
storeError OStoreBTreeNodeObject::split (
	sal_uInt16                 nIndexL,
	PageHolderObject< page > & rxPageL,
	OStorePageBIOS           & rBIOS)
{
    PageHolderObject< page > xPage (m_xPage);
    if (!xPage.is())
        return store_E_InvalidAccess;

	// Check left page usage.
    if (!rxPageL.is())
        return store_E_InvalidAccess;
	if (!rxPageL->querySplit())
		return store_E_None;

    // Construct right page.
    PageHolderObject< page > xPageR;
    if (!xPageR.construct (rBIOS.allocator()))
		return store_E_OutOfMemory;

	// Split right page off left page.
	xPageR->split (*rxPageL);
	xPageR->depth (rxPageL->depth());

	// Allocate right page.
    self aNodeR (xPageR.get());
	storeError eErrCode = rBIOS.allocate (aNodeR);
	if (eErrCode != store_E_None)
		return eErrCode;

	// Truncate left page.
	rxPageL->truncate (rxPageL->capacityCount() / 2);

	// Save left page.
	self aNodeL (rxPageL.get());
	eErrCode = rBIOS.saveObjectAt (aNodeL, aNodeL.location());
	if (eErrCode != store_E_None)
		return eErrCode;

	// Insert right page.
	OStorePageLink aLink (xPageR->location());
	xPage->insert (nIndexL + 1, T(xPageR->m_pData[0].m_aKey, aLink));

	// Save this page and leave.
	return rBIOS.saveObjectAt (*this, location());
}

/*
 * remove (down to leaf node, recursive).
 */
storeError OStoreBTreeNodeObject::remove (
	sal_uInt16         nIndexL,
	OStoreBTreeEntry & rEntryL,
	OStorePageBIOS &   rBIOS)
{
    PageHolderObject< page > xImpl (m_xPage);
    page & rPage = (*xImpl);

	// Check depth.
    storeError eErrCode = store_E_None;
	if (rPage.depth())
	{
		// Check link entry.
        T const aEntryL (rPage.m_pData[nIndexL]);
		if (!(rEntryL.compare (aEntryL) == T::COMPARE_EQUAL))
			return store_E_InvalidAccess;

		// Load link node.
		self aNodeL;
		eErrCode = rBIOS.loadObjectAt (aNodeL, aEntryL.m_aLink.location());
		if (eErrCode != store_E_None)
			return eErrCode;

		// Recurse: remove from link node.
		eErrCode = aNodeL.remove (0, rEntryL, rBIOS);
		if (eErrCode != store_E_None)
			return eErrCode;

		// Check resulting link node usage.
        PageHolderObject< page > xPageL (aNodeL.get());
		if (xPageL->usageCount() == 0)
		{
			// Free empty link node.
			eErrCode = rBIOS.free (xPageL->location());
			if (eErrCode != store_E_None)
				return eErrCode;

			// Remove index.
			rPage.remove (nIndexL);
			touch();
		}
		else
		{
#if 0   /* NYI */
			// Check for right sibling.
			sal_uInt16 const nIndexR = nIndexL + 1;
			if (nIndexR < rPage.usageCount())
			{
				// Load right link node.
				self aNodeR;
				eErrCode = rBIOS.loadObjectAt (aNodeR, rPage.m_pData[nIndexR].m_aLink.location());
				if (eErrCode == store_E_None)
				{
					if (rPageL.queryMerge (rPageR))
					{
						rPageL.merge (rPageR);

						eErrCode = rBIOS.free (rPageR.location());
					}
				}
			}
#endif  /* NYI */

			// Relink.
			rPage.m_pData[nIndexL].m_aKey = xPageL->m_pData[0].m_aKey;
			touch();
		}
	}
	else
	{
		// Check leaf entry.
		if (!(rEntryL.compare (rPage.m_pData[nIndexL]) == T::COMPARE_EQUAL))
			return store_E_NotExists;

		// Save leaf entry.
		rEntryL = rPage.m_pData[nIndexL];

		// Remove leaf index.
		rPage.remove (nIndexL);
		touch();
	}

	// Check for modified node.
	if (dirty())
	{
		// Save this page.
		eErrCode = rBIOS.saveObjectAt (*this, location());
	}

	// Done.
    return eErrCode;
}

/*========================================================================
 *
 * OStoreBTreeRootObject implementation.
 *
 *======================================================================*/
/*
 * testInvariant.
 * Precond: root node page loaded.
 */
bool OStoreBTreeRootObject::testInvariant (char const * message)
{
    OSL_PRECOND(m_xPage.get() != 0, "OStoreBTreeRootObject::testInvariant(): Null pointer");
    bool result = ((m_xPage->location() - m_xPage->size()) == 0);
    OSL_POSTCOND(result, message); (void) message;
    return result;
}

/*
 * loadOrCreate.
 */
storeError OStoreBTreeRootObject::loadOrCreate (
    sal_uInt32       nAddr,
    OStorePageBIOS & rBIOS)
{
    storeError eErrCode = rBIOS.loadObjectAt (*this, nAddr);
    if (eErrCode != store_E_NotExists)
        return eErrCode;

    eErrCode = construct<page>(rBIOS.allocator());
    if (eErrCode != store_E_None)
        return eErrCode;

    eErrCode = rBIOS.allocate (*this);
    if (eErrCode != store_E_None)
        return eErrCode;

    // Notify caller of the creation.
    (void) testInvariant("OStoreBTreeRootObject::loadOrCreate(): leave");
    return store_E_Pending;
}

/*
 * change.
 */
storeError OStoreBTreeRootObject::change (
	PageHolderObject< page > & rxPageL,
	OStorePageBIOS &           rBIOS)
{
    PageHolderObject< page > xPage (m_xPage);
    (void) testInvariant("OStoreBTreeRootObject::change(): enter");

    // Save root location.
    sal_uInt32 const nRootAddr = xPage->location();

    // Construct new root.
    if (!rxPageL.construct (rBIOS.allocator()))
		return store_E_OutOfMemory;

    // Save this as prev root.
    storeError eErrCode = rBIOS.allocate (*this);
    if (eErrCode != store_E_None)
		return store_E_OutOfMemory;

    // Setup new root.
    rxPageL->depth (xPage->depth() + 1);
    rxPageL->m_pData[0] = xPage->m_pData[0];
    rxPageL->m_pData[0].m_aLink = xPage->location();
    rxPageL->usageCount(1);

	// Change root.
    rxPageL.swap (xPage);
    {
        PageHolder tmp (xPage.get());
        tmp.swap (m_xPage);
    }

	// Save this as new root and finish.
	eErrCode = rBIOS.saveObjectAt (*this, nRootAddr);
    (void) testInvariant("OStoreBTreeRootObject::change(): leave");
    return eErrCode;
}

/*
 * find_lookup (w/o split()).
 * Precond: root node page loaded.
 */
storeError OStoreBTreeRootObject::find_lookup (
    OStoreBTreeNodeObject & rNode,  // [out]
    sal_uInt16 &            rIndex, // [out]
    OStorePageKey const &   rKey,
    OStorePageBIOS &        rBIOS)
{
    // Init node w/ root page.
    (void) testInvariant("OStoreBTreeRootObject::find_lookup(): enter");
    {
        PageHolder tmp (m_xPage);
        tmp.swap (rNode.get());
    }

    // Setup BTree entry.
    T const entry (rKey);

    // Check current page.
    PageHolderObject< page > xPage (rNode.get());
    for (; xPage->depth() > 0; xPage = rNode.makeHolder< page >())
    {
        // Find next page.
        page const & rPage = (*xPage);
        sal_uInt16 const i = rPage.find(entry);
        sal_uInt16 const n = rPage.usageCount();
        if (!(i < n))
        {
            // Path to entry not exists (Must not happen(?)).
            return store_E_NotExists;
        }

        // Check address.
        sal_uInt32 const nAddr = rPage.m_pData[i].m_aLink.location();
        if (nAddr == STORE_PAGE_NULL)
        {
            // Path to entry not exists (Must not happen(?)).
            return store_E_NotExists;
        }

        // Load next page.
        storeError eErrCode = rBIOS.loadObjectAt (rNode, nAddr);
        if (eErrCode != store_E_None)
            return eErrCode;
    }

    // Find index.
    page const & rPage = (*xPage);
    rIndex = rPage.find(entry);
    if (!(rIndex < rPage.usageCount()))
        return store_E_NotExists;

    // Compare entry.
    T::CompareResult eResult = entry.compare(rPage.m_pData[rIndex]);
    OSL_POSTCOND(eResult != T::COMPARE_LESS, "store::BTreeRoot::find_lookup(): sort error");
    if (eResult == T::COMPARE_LESS)
        return store_E_Unknown;

    // Greater or Equal.
    (void) testInvariant("OStoreBTreeRootObject::find_lookup(): leave");
    return store_E_None;
}

/*
 * find_insert (possibly with split()).
 * Precond: root node page loaded.
 */
storeError OStoreBTreeRootObject::find_insert (
    OStoreBTreeNodeObject & rNode,  // [out]
    sal_uInt16 &            rIndex, // [out]
    OStorePageKey const &   rKey,
    OStorePageBIOS &        rBIOS)
{
    (void) testInvariant("OStoreBTreeRootObject::find_insert(): enter");

    // Check for RootNode split.
    PageHolderObject< page > xRoot (m_xPage);
    if (xRoot->querySplit())
    {
        PageHolderObject< page > xPageL;

        // Change root.
        storeError eErrCode = change (xPageL, rBIOS);
        if (eErrCode != store_E_None)
            return eErrCode;

        // Split left page (prev root).
        eErrCode = split (0, xPageL, rBIOS);
        if (eErrCode != store_E_None)
            return eErrCode;
	}

    // Init node w/ root page.
    {
        PageHolder tmp (m_xPage);
        tmp.swap (rNode.get());
    }

    // Setup BTree entry.
    T const entry (rKey);

	// Check current Page.
    PageHolderObject< page > xPage (rNode.get());
    for (; xPage->depth() > 0; xPage = rNode.makeHolder< page >())
	{
		// Find next page.
        page const & rPage = (*xPage);
		sal_uInt16 const i = rPage.find (entry);
        sal_uInt16 const n = rPage.usageCount();
		if (!(i < n))
		{
			// Path to entry not exists (Must not happen(?)).
			return store_E_NotExists;
		}

		// Check address.
		sal_uInt32 const nAddr = rPage.m_pData[i].m_aLink.location();
		if (nAddr == STORE_PAGE_NULL)
		{
			// Path to entry not exists (Must not happen(?)).
			return store_E_NotExists;
		}

		// Load next page.
        OStoreBTreeNodeObject aNext;
		storeError eErrCode = rBIOS.loadObjectAt (aNext, nAddr);
		if (eErrCode != store_E_None)
			return eErrCode;

		// Check for next node split.
        PageHolderObject< page > xNext (aNext.get());
		if (xNext->querySplit())
		{
			// Split next node.
			eErrCode = rNode.split (i, xNext, rBIOS);
			if (eErrCode != store_E_None)
				return eErrCode;

			// Restart.
			continue;
		}

        // Let next page be current.
        PageHolder tmp (aNext.get());
        tmp.swap (rNode.get());
    }

	// Find index.
    page const & rPage = (*xPage);
	rIndex = rPage.find(entry);
	if (rIndex < rPage.usageCount())
	{
		// Compare entry.
		T::CompareResult result = entry.compare (rPage.m_pData[rIndex]);
		OSL_POSTCOND(result != T::COMPARE_LESS, "store::BTreeRoot::find_insert(): sort error");
		if (result == T::COMPARE_LESS)
			return store_E_Unknown;

		if (result == T::COMPARE_EQUAL)
			return store_E_AlreadyExists;
	}

    // Greater or not (yet) existing.
    (void) testInvariant("OStoreBTreeRootObject::find_insert(): leave");
    return store_E_None;
}
