public RevisionInternal PutRevision(string inDocId, RevisionID inPrevRevId, IDictionary<string, object> properties,
            bool deleting, bool allowConflict, Uri source, StoreValidation validationBlock)
        {
            if(_config.HasFlag(C4DatabaseFlags.ReadOnly)) {
                throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.Forbidden, TAG,
                    "Attempting to write to a readonly database (PutRevision)");
            }

            var json = default(string);
            if(properties != null) {
                json = Manager.GetObjectMapper().WriteValueAsString(Database.StripDocumentJSON(properties), true);
            } else {
                json = "{}";
            }

            if(inDocId == null) {
                inDocId = Misc.CreateGUID();
            }

            C4Document* doc = null;
            var putRev = default(RevisionInternal);
            var change = default(DocumentChange);
            var success = RunInTransaction(() =>
            {
                try {
                    var docId = inDocId;
                    var prevRevId = inPrevRevId;
                    C4DocPutRequest rq = new C4DocPutRequest {
                        body = json,
                        docID = docId,
                        deletion = deleting,
                        hasAttachments = properties?.Get("_attachments") != null,
                        existingRevision = false,
                        allowConflict = allowConflict,
                        history = prevRevId == null ? null : new[] { prevRevId.ToString() },
                        save = false
                    };

                    UIntPtr commonAncestorIndex = UIntPtr.Zero;
                    doc = (C4Document*)ForestDBBridge.Check(err =>
                    {
                        UIntPtr tmp;
                        var retVal = Native.c4doc_put(Forest, rq, &tmp, err);
                        commonAncestorIndex = tmp;
                        return retVal;
                    });

                    if(docId == null) {
                        docId = (string)doc->docID;
                    }

                    var newRevID = doc->selectedRev.revID.AsRevID();

                    Body body = null;
                    if(properties != null) {
                        properties.SetDocRevID(docId, newRevID);
                        body = new Body(properties);
                    }

                    putRev = new RevisionInternal(docId, newRevID, deleting, body);
                    if((uint)commonAncestorIndex == 0U) {
                        return true;
                    }

                    if(validationBlock != null) {
                        var prevRev = default(RevisionInternal);
                        if(Native.c4doc_selectParentRevision(doc)) {
                            prevRev = new ForestRevisionInternal(doc, false);
                        }

                        var status = validationBlock(putRev, prevRev, prevRev == null ? null : prevRev.RevID);
                        if(status.IsError) {
                            Log.To.Validation.I(TAG, "{0} ({1}) failed validation", new SecureLogString(docId, LogMessageSensitivity.PotentiallyInsecure), new SecureLogString(newRevID, LogMessageSensitivity.PotentiallyInsecure));
                            throw new CouchbaseLiteException("A document failed validation", status.Code);
                        }
                    }

                    var isWinner = SaveDocument(doc, newRevID, properties);
                    putRev.Sequence = (long)doc->sequence;
                    change = ChangeWithNewRevision(putRev, isWinner, doc, null);
                    return true;
                } finally {
                    Native.c4doc_free(doc);
                }
            });

            if(!success) {
                return null;
            }

            if(Delegate != null && change != null) {
                Delegate.DatabaseStorageChanged(change);
            }

            return putRev;
        }