/**
  * @param int $left
  * @param int $right
  * @param int $depth
  * @param bool $forward
  * @param array|null $parent
  * @throws Exception
  */
 protected function insertNode($left, $right, $depth = 0, $forward = true, $parent = null)
 {
     if ($this->node->getIsNewRecord()) {
         throw new Exception('Can not create a node when the target node is new record.');
     }
     if ($depth === 0 && $this->getNodeBehavior()->isRoot()) {
         throw new Exception('Can not insert a node before/after root.');
     }
     $this->owner->setAttribute($this->depthAttribute, $this->node->getAttribute($this->depthAttribute) + $depth);
     if ($this->treeAttribute !== null) {
         $this->owner->setAttribute($this->treeAttribute, $this->node->getAttribute($this->treeAttribute));
     }
     if ($right - $left < 3) {
         for ($i = $right - $left; $i < 3; $i++) {
             $unallocated = $this->findUnallocatedAll($left, $right);
             if ($unallocated < $left) {
                 $this->shift($unallocated, $left, -1);
                 $left--;
             } else {
                 $this->shift($right, $unallocated, 1);
                 $right++;
             }
         }
         $this->owner->setAttribute($this->leftAttribute, $left + 1);
         $this->owner->setAttribute($this->rightAttribute, $right - 1);
     } else {
         $left++;
         $right--;
         $isPadding = false;
         if ($depth === 1 || $parent !== null) {
             // prepending/appending
             if (is_array($this->amountOptimize)) {
                 if (isset($this->amountOptimize[$depth - 1])) {
                     $amountOptimize = $this->amountOptimize[$depth - 1];
                 } else {
                     $amountOptimize = $this->amountOptimize[count($this->amountOptimize) - 1];
                 }
             } else {
                 $amountOptimize = $this->amountOptimize;
             }
             $pLeft = $parent !== null ? (int) $parent[$this->leftAttribute] : $this->node->getAttribute($this->leftAttribute);
             $pRight = $parent !== null ? (int) $parent[$this->rightAttribute] : $this->node->getAttribute($this->rightAttribute);
             $isCenter = !$this->noAppend && !$this->noPrepend;
             $isFirst = $left === $pLeft + 1 && $right === $pRight - 1;
             $isPadding = !$this->noInsert || $isFirst && ($forward ? !$this->noPrepend : !$this->noAppend);
             $step = $amountOptimize + $this->reserveFactor * ($this->noInsert ? $isCenter ? 2 : 1 : $amountOptimize + 1);
             $step = ($pRight - $pLeft - 1) / $step;
             $stepGap = $step * $this->reserveFactor;
             $padding = $isPadding ? $stepGap : 0;
             if ($forward) {
                 $pLeft = $left + (int) floor($padding);
                 $pRight = $left + (int) floor($padding + $step) - 1;
             } else {
                 $pLeft = $right - (int) floor($padding + $step) + 1;
                 $pRight = $right - (int) floor($padding);
             }
             if ($isFirst && $isCenter) {
                 $initPosition = (int) floor(($amountOptimize - 1) / 2) * (int) floor($step + ($this->noInsert ? 0 : $stepGap)) + ($this->noInsert ? 1 : 0);
                 $pLeft += $forward ? $initPosition : -$initPosition;
                 $pRight += $forward ? $initPosition : -$initPosition;
             }
             if ($pLeft < $pRight && $pRight <= $right && $left <= $pLeft && ($forward || $left < $pLeft) && (!$forward || $pRight < $right)) {
                 $this->owner->setAttribute($this->leftAttribute, $pLeft);
                 $this->owner->setAttribute($this->rightAttribute, $pRight);
                 return;
             }
         }
         $isPadding = $isPadding || !$this->noInsert;
         $step = (int) floor(($right - $left) / ($isPadding ? 3 : 2));
         $this->owner->setAttribute($this->leftAttribute, $left + ($forward && !$isPadding ? 0 : $step));
         $this->owner->setAttribute($this->rightAttribute, $right - (!$forward && !$isPadding ? 0 : $step));
     }
 }