protected function processParts($parts)
    {
        $num_parts = count($parts);
        $parts_etags = [];
        $parts_completed = 0;

        $max_retries = FilestackConfig::MAX_RETRIES;

        $current_part_index = 0;
        while($parts_completed < $num_parts) {
            $part = $parts[$current_part_index];
            $part['part_size'] = 0;
            $chunks = $part['chunks'];

            // process chunks of current part
            $promises = $this->processChunks($part, $chunks);

            // sends s3 chunks asyncronously
            $s3_results = $this->settlePromises($promises);
            $this->handleS3PromisesResult($s3_results);

            // handle fulfilled promises
            if ($this->intelligent) {
                // commit part
                $this->commitPart($part);
            }
            else {
                $part_num = array_key_exists('part_num', $part) ?
                    $part['part_num'] : 1;

                $this->multipartGetTags($part_num, $s3_results, $parts_etags);
            }

            unset($promises);
            unset($s3_results);

            $current_part_index++;
            $parts_completed++;
        }

        if (!$this->intelligent) {
            return implode(';', $parts_etags);
        }

        return $parts_completed;
    }