InsertAtFound.cs
//
// This code is part of Document Solutions for Word demos.
// Copyright (c) MESCIUS inc. All rights reserved.
//
using System;
using System.IO;
using System.Drawing;
using System.Linq;
using GrapeCity.Documents.Word;

namespace DsWordWeb.Demos
{
    // This sample shows how to insert the whole body of an existing DOCX into 
    // another document at a location where an arbitrary search string is found.
    // One interesting point in this sample is that the code ensures that the
    // insert position is valid, i.e. the source content (all paragraphs of
    // the source document) can be inserted at the found point. If necessary,
    // the target is split so that there is a content object of a valid type
    // at the point where the source is to be inserted.
    // In this sample, the target document is JsFrameworkExcerpt.
    // The source document inserted is SampleParagraphs, and it is inserted
    // on page 2 of the source document, immediately before the string
    // "software design principles".
    public class InsertAtFound
    {
        public GcWordDocument CreateDocx()
        {
            // The string to find. The source document will be inserted before this string:
            const string findPattern = "software design principles";

            // Target document where the source document will be inserted:
            var doc = new GcWordDocument();
            doc.Load(Path.Combine("Resources", "WordDocs", "JsFrameworkExcerpt.docx"));

            // Source document that will be inserted into the target:
            var sourceDoc = new GcWordDocument();
            sourceDoc.Load(Path.Combine("Resources", "WordDocs", "SampleParagraphs.docx"));
            // Note: the inserted document starts with a 'Heading 1', which adds a page break.
            // We change it to 'Heading 2' to avoid this so that the result is more clear:
            sourceDoc.Body.Paragraphs.First.Style = sourceDoc.Styles[BuiltInStyleId.Heading2];

            // Find the first occurrence of the search string:
            var findResult = doc.Body.Find(findPattern, new FindOptions(doc) { IgnoreCase = true }).FirstOrDefault();
            if (findResult == null)
                throw new Exception("Unexpected: search string not found.");

            // Find a valid insertion point near the found string:
            var insertObject = CreateInsertPoint(findResult);

            // Copy the source document to the target at insertion point:
            sourceDoc.Body.CopyTo(insertObject.GetRange(), InsertLocation.Before);

            // Done:
            return doc;
        }

        // Walk up the parent chain and determine where we are - inside body, cell or contentcontrol.
        // Return the original object if we are inside body or cell.
        // Return contentcontrol if we are inside contentcontrol.
        private static ContentObject GetAnchorObject(ContentObject testedObject)
        {
            var originalObject = testedObject;
            while (true)
            {
                if (testedObject.ParentContent == null)
                    return originalObject;
                else if (testedObject is Cell)
                    return originalObject;
                else if (testedObject is ContentControl)
                    return testedObject;
                else
                    testedObject = testedObject.ParentContent;
            }
        }

        // This method assumes that testObject is always an entity inside a paragraph.
        private static Paragraph GetParentParagraph(ContentObject testObject)
        {
            while (testObject != null)
            {
                if (testObject is Paragraph)
                    return (Paragraph)testObject;
                testObject = testObject.ParentContent;
            }
            throw new ArgumentException("testObject is not inside a paragraph.");
        }

        private static ContentObject CreateInsertPoint(FindResult fr)
        {
            var anchorObject = GetAnchorObject(fr.Range.First());
            var foundParagraph = GetParentParagraph(anchorObject);

            if (fr.StartIndex > 0 && anchorObject is Text)
                anchorObject = ((Text)anchorObject).Split(fr.StartIndex);
            if (foundParagraph == anchorObject)
                anchorObject = foundParagraph.Split(anchorObject, InsertLocation.End);
            else
                anchorObject = foundParagraph.Split(anchorObject, InsertLocation.Before);
            return anchorObject;
        }
    }
}