/*
         * private static void InvalidatePMap(PSTFile file)
         * {
         *  file.BaseStream.Seek(PMapPage.FirstPageOffset, SeekOrigin.Begin);
         *  PMapPage pmap = PMapPage.GetFilledPMapPage();
         *  byte[] buffer = pmap.GetBytes(PMapPage.FirstPageOffset);
         *  file.BaseStream.Write(buffer, 0, buffer.Length);
         * }
         *
         * private static void InvalidateDList(PSTFile file)
         * {
         *  file.BaseStream.Seek(DensityListPage.FirstPageOffset, SeekOrigin.Begin);
         *  byte[] buffer = new byte[Page.Length];
         *  file.BaseStream.Write(buffer, 0, buffer.Length);
         * }*/

        private static AllocationMapPage GrowPST(PSTFile file, int newPageIndex)
        {
            // The PST file MUST grow at integer multiples of the number of bytes mapped by an AMap
            ulong oldLength = (ulong)file.BaseStream.Length;
            ulong newLength = oldLength + AllocationMapPage.MapppedLength;

            file.BaseStream.Seek(0, SeekOrigin.End);
            AllocationMapPage amap = new AllocationMapPage();
            int freeSpaceInAMap    = AllocationMapPage.MapppedLength - 512;

            byte[] buffer = new byte[AllocationMapPage.MapppedLength];
            if (newPageIndex % 8 == 0)
            {
                // We need to write a PMap for backward compatibility
                PMapPage pmap       = PMapPage.GetFilledPMapPage();
                ulong    pmapOffset = PMapPage.FirstPageOffset + (ulong)(newPageIndex / 8) * PMapPage.MapppedLength;
                Array.Copy(pmap.GetBytes(pmapOffset), 0, buffer, 512, Page.Length);

                // Allocate the PMap out of the AMap
                amap.AllocateSpace(512, 512);
                freeSpaceInAMap -= 512;

                if (newPageIndex >= 128 && FreeMapPage.GetFreeMapEntryIndex(newPageIndex) == 0)
                {
                    // We need to write an FMap for backward compatibility
                    FreeMapPage fmap = new FreeMapPage();
                    fmap.rgbFMapBits[0] = 255;
                    int   fmapPageIndex = FreeMapPage.GetFreeMapPageIndex(newPageIndex);
                    ulong fmapOffset    = FreeMapPage.FirstPageOffset + (ulong)fmapPageIndex * FreeMapPage.MapppedLength;
                    Array.Copy(fmap.GetBytes(fmapOffset), 0, buffer, 1024, Page.Length);

                    // Allocate the FMap out of the AMap
                    amap.AllocateSpace(1024, 512);
                    freeSpaceInAMap -= 512;
                }

                // FPMap page will always follow an FMap page
                if (newPageIndex >= (128 * 64) && FPMapPage.GetFPMapEntryIndex(newPageIndex) == 0)
                {
                    // We need to write a FPMap for backward compatibility
                    FPMapPage fpmap          = new FPMapPage();
                    int       fpmapPageIndex = FPMapPage.GetFPMapPageIndex(newPageIndex);
                    ulong     fpmapOffset    = FPMapPage.FirstPageOffset + (ulong)fpmapPageIndex * FPMapPage.MapppedLength;
                    Array.Copy(fpmap.GetBytes(fpmapOffset), 0, buffer, 1536, Page.Length);

                    // Allocate the FPMap out of the AMap
                    amap.AllocateSpace(1536, 512);
                    freeSpaceInAMap -= 512;
                }
            }
            Array.Copy(amap.GetBytes(oldLength), 0, buffer, 0, AllocationMapPage.Length);
            file.BaseStream.Write(buffer, 0, buffer.Length);
            file.Header.root.ibFileEOF   = newLength;
            file.Header.root.ibAMapLast  = oldLength;
            file.Header.root.cbAMapFree += (ulong)freeSpaceInAMap;
            file.Header.WriteToStream(file.BaseStream, file.WriterCompatibilityMode);

            return(amap);
        }
        /// <returns>Offset of space allocated</returns>
        private static long AllocateSpace(PSTFile file, int allocationLength, bool pageAligned)
        {
            if (allocationLength > MaxAllocationLength)
            {
                throw new Exception("Invalid allocation length requested");
            }

            int numberOfPages = file.Header.root.NumberOfAllocationMapPages;

            AllocationMapPage targetPage = null;
            int targetPageIndex          = 0;

            for (int index = 0; index < numberOfPages; index++)
            {
                AllocationMapPage page = AllocationMapPage.ReadAllocationMapPage(file, index);
                int startOffset        = page.FindContiguousSpace(allocationLength, pageAligned);
                if (startOffset > 0) // 0 is allocated to the AMAP itself and is not a valid value
                {
                    targetPage      = page;
                    targetPageIndex = index;
                    break;
                }
            }

            if (targetPage == null)
            {
                // no space was found within existing pages
                targetPageIndex = numberOfPages;
                targetPage      = GrowPST(file, targetPageIndex);
            }

            int targetPageStartOffset = targetPage.FindContiguousSpace(allocationLength, pageAligned);

            targetPage.AllocateSpace(targetPageStartOffset, allocationLength);
            targetPage.WriteAllocationMapPage(file, targetPageIndex);

            UpdateFMap(file, targetPage, targetPageIndex);

            file.Header.root.cbAMapFree -= (uint)allocationLength;

            //InvalidatePMap(file);
            //InvalidateDList(file);
            return(AllocationMapPage.FirstPageOffset + (long)targetPageIndex * AllocationMapPage.MapppedLength + targetPageStartOffset);
        }