<?php
 namespace MongoDB\GridFS;
 use MongoDB\Collection;
 use MongoDB\Driver\Cursor;
 use MongoDB\Driver\Manager;
 use MongoDB\Driver\ReadPreference;
 use MongoDB\Exception\InvalidArgumentException;
 use MongoDB\UpdateResult;
 use stdClass;
 use function abs;
 use function sprintf;
 class CollectionWrapper {
 private $bucketName;
 private $chunksCollection;
 private $databaseName;
 private $checkedIndexes = false;
 private $filesCollection;
 public function __construct(Manager $manager, $databaseName, $bucketName, array $collectionOptions = []) {
 $this->databaseName = (string) $databaseName;
 $this->bucketName = (string) $bucketName;
 $this->filesCollection = new Collection($manager, $databaseName, sprintf('%s.files', $bucketName), $collectionOptions);
 $this->chunksCollection = new Collection($manager, $databaseName, sprintf('%s.chunks', $bucketName), $collectionOptions);
 }
 public function deleteChunksByFilesId($id) {
 $this->chunksCollection->deleteMany(['files_id' => $id]);
 }
 public function deleteFileAndChunksById($id) {
 $this->filesCollection->deleteOne(['_id' => $id]);
 $this->chunksCollection->deleteMany(['files_id' => $id]);
 }
 public function dropCollections() {
 $this->filesCollection->drop(['typeMap' => []]);
 $this->chunksCollection->drop(['typeMap' => []]);
 }
 public function findChunksByFileId($id, $fromChunk = 0) {
 return $this->chunksCollection->find( [ 'files_id' => $id, 'n' => ['$gte' => $fromChunk], ], [ 'sort' => ['n' => 1], 'typeMap' => ['root' => 'stdClass'], ] );
 }
 public function findFileByFilenameAndRevision($filename, $revision) {
 $filename = (string) $filename;
 $revision = (integer) $revision;
 if ($revision < 0) {
 $skip = abs($revision) - 1;
 $sortOrder = -1;
 }
 else {
 $skip = $revision;
 $sortOrder = 1;
 }
 return $this->filesCollection->findOne( ['filename' => $filename], [ 'skip' => $skip, 'sort' => ['uploadDate' => $sortOrder], 'typeMap' => ['root' => 'stdClass'], ] );
 }
 public function findFileById($id) {
 return $this->filesCollection->findOne( ['_id' => $id], ['typeMap' => ['root' => 'stdClass']] );
 }
 public function findFiles($filter, array $options = []) {
 return $this->filesCollection->find($filter, $options);
 }
 public function findOneFile($filter, array $options = []) {
 return $this->filesCollection->findOne($filter, $options);
 }
 public function getBucketName() {
 return $this->bucketName;
 }
 public function getChunksCollection() {
 return $this->chunksCollection;
 }
 public function getDatabaseName() {
 return $this->databaseName;
 }
 public function getFilesCollection() {
 return $this->filesCollection;
 }
 public function insertChunk($chunk) {
 if (! $this->checkedIndexes) {
 $this->ensureIndexes();
 }
 $this->chunksCollection->insertOne($chunk);
 }
 public function insertFile($file) {
 if (! $this->checkedIndexes) {
 $this->ensureIndexes();
 }
 $this->filesCollection->insertOne($file);
 }
 public function updateFilenameForId($id, $filename) {
 return $this->filesCollection->updateOne( ['_id' => $id], ['$set' => ['filename' => (string) $filename]] );
 }
 private function ensureChunksIndex() {
 foreach ($this->chunksCollection->listIndexes() as $index) {
 if ($index->isUnique() && $index->getKey() === ['files_id' => 1, 'n' => 1]) {
 return;
 }
 }
 $this->chunksCollection->createIndex(['files_id' => 1, 'n' => 1], ['unique' => true]);
 }
 private function ensureFilesIndex() {
 foreach ($this->filesCollection->listIndexes() as $index) {
 if ($index->getKey() === ['filename' => 1, 'uploadDate' => 1]) {
 return;
 }
 }
 $this->filesCollection->createIndex(['filename' => 1, 'uploadDate' => 1]);
 }
 private function ensureIndexes() {
 if ($this->checkedIndexes) {
 return;
 }
 $this->checkedIndexes = true;
 if (! $this->isFilesCollectionEmpty()) {
 return;
 }
 $this->ensureFilesIndex();
 $this->ensureChunksIndex();
 }
 private function isFilesCollectionEmpty() {
 return null === $this->filesCollection->findOne([], [ 'readPreference' => new ReadPreference(ReadPreference::RP_PRIMARY), 'projection' => ['_id' => 1], 'typeMap' => [], ]);
 }
 }
 