<?php

namespace WpieFw\Files;

use WpieFw\Files\WpieFile;
use WpieFw\Files\WpieFilesystemIteratorFactory;

if( ! defined( 'ABSPATH' ) ) exit;

/**
 * WpieFileFinder class
 *
 * @author $Author: Vincent Weber <weepie-plugins@outlook.com> $
 */
class WpieFileFinder
{
	/**
	 * Number of files found
	 *
	 * @var integer
	 */
	public $filesCount = 0;

	/**
	 * Flag files are found
	 *
	 * @var bool
	 */
	public $filesFound = false;

	/**
	 * Flag if file has been touched
	 *
	 * @var bool
	 */
	public $fileCreated = false;

	/**
	 * @var \FilesystemIterator
	 */
	protected $iterator;

	/**
	 * @var array
	 */
	protected $files = [];

	/**
	 * @var string
	 */
	protected $extension;

	/**
	 * @var string
	 */
	protected $prefix;

	/**
	 * @var string
	 */
	protected $fileName;

	/**
	 * @var bool
	 */
	protected $recursive;

	/**
	 * @var boolean
	 */
	protected $checkExtension;

	/**
	 * @var boolean
	 */
	protected $checkPrefix;

	/**
	 * @var boolean
	 */
	protected $checkFileName;

	/**
	 * @var string
	 */
	protected $searchPath;

	/**
	 * Flag if finding the files have been done
	 *
	 * @var bool
	 */
	protected $did = false;

	/**
	 * Constructor
	 *
	 * @param string $path
	 * @param string $extension
	 * @param string $prefix
	 * @param string $fileName
	 * @param bool $recursive
	 * @param \FilesystemIterator $iterator
	 */
	public function __construct( $path = '', $extension = '', $prefix = '', $fileName = '', $recursive = true, $iterator = null )
	{
		if( !($iterator instanceof \FilesystemIterator) ) {
			$iterator = WpieFilesystemIteratorFactory::create( $path );
		}

		$this->iterator = $iterator;
		$this->extension = trim( $extension );
		$this->prefix = trim( $prefix );
		$this->fileName = trim( $fileName );
		$this->recursive = $recursive;

		$this->checkFileName = ( '' !== $this->fileName );

		// checking the file name overrules checking the extension and prefix
		if( !$this->checkFileName ) {
			$this->checkExtension = ( '' !== $this->extension );
			$this->checkPrefix = ( '' !== $this->prefix );
			$this->searchPath = $this->getPath();
		} else {
			$this->searchPath = $this->getPath() . DIRECTORY_SEPARATOR . $this->fileName;
		}
	}

	/**
	 * Find the files for given location and params
	 *
	 * @param \FilesystemIterator|null $iterator
	 * @param bool $forceCreate
	 *
	 * @uses self::findFilesForFileName()
	 * @uses self::findFilesForExtension()
	 * @uses self::findFilesForPrefix()
	 * @uses self::findFilesForExtensionPrefix()
	 * @uses self::findAllFiles()
	 *
	 * @return WpieFileFinder
	 */
	public function findFiles( $iterator = null, $forceCreate = false )
	{
		if( $this->checkFileName && $this->filesFound  && !$this->recursive ) {
			return $this;
		}

		// check if a valid iterator is passed
		$iterator = ( null !== $iterator && ( $iterator instanceof \FilesystemIterator ) ) ? $iterator : $this->iterator;

		// return files array by the appropriate method
		if( $this->checkFileName ) {
			$this->findFilesForFileName( $iterator );
		} elseif( $this->checkExtension && !$this->checkPrefix ) {
			$this->findFilesForExtension( $iterator );
		} elseif( !$this->checkExtension && $this->checkPrefix ) {
			$this->findFilesForPrefix( $iterator );
		} elseif( $this->checkExtension && $this->checkPrefix ) {
			$this->findFilesForExtensionPrefix( $iterator );
		} else {
			$this->findAllFiles( $iterator );
		}

		// set the files count and found flag
		$this->filesCount = count( $this->files );
		$this->filesFound = ( 0 < $this->filesCount );

		if( !$this->filesFound && $forceCreate && $this->checkFileName ) {
			$this->fileCreated = touch( $this->searchPath );
			return $this->findFiles();
		}

		// indicate file finding has been done
		$this->did = true;

		return $this;
	}

	/**
	 * Get files array
	 *
	 * @return array|bool array with WpieFile objects or false
	 */
	public function getFiles()
	{
		return $this->files;
	}

	/**
	 * Get the WpieFile instance when there is 1 file found
	 *
	 * @return WpieFile|bool
	 */
	public function getFile()
	{
		if( !$this->did ) {
			$this->findFiles();
		}

		if( 1 === $this->filesCount ) {
			$files = $this->getFiles();
			return $files[0];
		} else {
			return false;
		}
	}

	/**
	 * Get plain files path array
	 *
	 * @uses self::getFiles()
	 *
	 * @return array
	 */
	public function getFilesPathArray()
	{
		$paths = [];

		if( !$this->did ) {
			$this->findFiles();
		}

		$files = $this->getFiles();

		foreach( $files as $file ) {
			$paths[$file->getIndex()] = $file->getPathname();
		}

		return $paths;
	}

	/**
	 * Get plain relative files path array
	 *
	 * Use the $separator argument to split the path into relative
	 *
	 * @param string $separator
	 *
	 * @uses self::getFilesPathArray()
	 *
	 * @return array
	 */
	public function getFilesPathArrayRelative( $separator = '' )
	{
		$paths = $this->getFilesPathArray();

		if( is_string( $separator ) && '' !== $separator ) {
			foreach ( $paths as $idx => $path ) {
				$parts = explode( $separator, $path );
				if( isset( $parts[1] ) ) {
					$paths[$idx] =  $separator.str_replace( '\\', '/', $parts[1] );
				}
			}
		}

		return $paths;
	}

	/**
	 * Get the prefix
	 *
	 * @return string
	 */
	public function getPrefix()
	{
		return $this->prefix;
	}

	/**
	 * Get the path from the FilesystemIterator
	 *
	 * @uses \FilesystemIterator::getPath()
	 *
	 * @return string
	 */
	public function getPath()
	{
		return $this->iterator->getPath();
	}

	/**
	 * Get the full path the finder should search for
	 *
	 * @return string
	 */
	public function getSearchPath()
	{
		return $this->searchPath;
	}

	/**
	 * Get files array for files with file name
	 *
	 * @uses self::_findFilesForFilename()
	 *
	 * @return WpieFileFinder
	 */
	protected function findFilesForFileName( $iterator )
	{
		$this->_findFilesForFilename( $iterator );

		return $this;
	}

	/**
	 * Get files array for files with extension
	 *
	 * @uses self::_findFilesForExtension()
	 *
	 * @return WpieFileFinder
	 */
	protected function findFilesForExtension( $iterator )
	{
	  $this->_findFilesForExtension( $iterator );

	  return $this;
	}

	/**
	 * Get files array for files with prefix
	 *
	 * @uses self::_findFilesForPrefix()
	 *
	 * @return WpieFileFinder
	 */
	protected function findFilesForPrefix( $iterator )
	{
		$this->_findFilesForPrefix( $iterator );

		return $this;
	}

	/**
	 * Get files array for files with extension and prefix
	 *
	 * @uses self::_findFilesForExtensionPrefix()
	 *
	 * @return WpieFileFinder
	 */
	protected function findFilesForExtensionPrefix( $iterator )
	{
		$this->_findFilesForExtensionPrefix( $iterator );

		return $this;
	}

	/**
	 * Get files array with all files
	 *
	 * @uses self::_findAllFiles()
	 *
	 * @return WpieFileFinder
	 */
	protected function findAllFiles( $iterator )
	{
		$this->_findAllFiles( $iterator );

		return $this;
	}

	/**
	 * Determine if filename has prefix
	 *
	 * @param string $filename
	 * @return boolean
	 */
	protected function hasPrefix( $filename = '' )
	{
		if( $this->checkPrefix && 0 === strpos( $filename , $this->prefix ) ) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Determine if given extention matches the Constructor extention
	 *
	 * @param \SplFileInfo $file
	 * @return boolean
	 */
	protected function hasFileTheExtention( \SplFileInfo $file )
	{
		if( !$this->checkExtension ) {
			return false;
		}

		return ( $file->getExtension() === $this->extension ) ? true : false;
	}

	/**
	 * Create new file instance
	 *
	 * @param \SplFileInfo $file
	 * @return \WpieFw\Files\WpieFile
	 */
	protected function createFileInstance( \SplFileInfo $file )
	{
		return new WpieFile( $file->getRealPath() );
	}

	/**
	 * Get Files for given path
	 *
	 * @param string $path
	 *
	 * @uses WpieFilesystemIteratorFactory::create()
	 * @uses self::getFiles()
	 *
	 * @return array
	 */
	private function findFilesForPath( $path = '' )
	{
		$iterator = WpieFilesystemIteratorFactory::create( $path );

		$this->findFiles( $iterator );

		return $this;
	}

	/**
	 * Determine if needing and can apply a recursive file search
	 *
	 * @param \SplFileInfo $file
	 *
	 * @uses \SplFileInfo::isDir()
	 *
	 * @return boolean
	 */
	private function doRecursiveIterator( \SplFileInfo $file )
	{
		return $this->recursive && $file->isDir();
	}

	/**
	 * Get files array for files with file name
	 *
	 * @param \FilesystemIterator $iterator
	 *
	 * @uses self::doRecursiveIterator()
	 * @uses self::hasFileTheExtention()
	 * @uses self::createFileInstance()
	 */
	private function _findFilesForFilename( \FilesystemIterator $iterator )
	{
		foreach ( $iterator as $file ) {
			// check for directories
			if( $this->doRecursiveIterator( $file ) ) {
				$this->files += $this->findFilesForPath( $file->getFileInfo()->getRealPath() );
			} else {
				$filename = $file->getFilename();

				if( $this->checkFileName && $filename === $this->fileName ) {
					$this->files[] = $this->createFileInstance( $file );
				}
			}
		}
	}

	/**
	 * Get files array for files with extension
	 *
	 * @param \FilesystemIterator $iterator
	 *
	 * @uses self::doRecursiveIterator()
	 * @uses self::hasFileTheExtention()
	 * @uses self::createFileInstance()
	 */
	private function _findFilesForExtension( \FilesystemIterator $iterator )
	{
		foreach ( $iterator as $file ) {
			// check for directories
			if( $this->doRecursiveIterator( $file ) ) {
				$this->findFilesForPath( $file->getFileInfo()->getRealPath() );
			} else {
				if( !$this->hasFileTheExtention( $file ) ) {
					$this->files[] = $this->createFileInstance( $file );
				}
			}
		}
	}

	/**
	 * Get files array for files with prefix
	 *
	 * @param \FilesystemIterator $iterator
	 *
	 * @uses self::doRecursiveIterator()
	 * @uses self::hasPrefix()
	 * @uses self::createFileInstance()
	 */
	private function _findFilesForPrefix( \FilesystemIterator $iterator )
	{
		foreach ( $iterator as $file ) {
			// check for directories
			if( $this->doRecursiveIterator( $file ) ) {
				$this->findFilesForPath( $file->getFileInfo()->getRealPath() );
			} else {
				$filename = $file->getFilename();

				if( $this->checkPrefix && $this->hasPrefix( $filename ) ) {
					$this->files[] = $this->createFileInstance( $file );
				}
			}
		}
	}

	/**
	 * Get files array for files with extension and prefix
	 *
	 * @param \FilesystemIterator $iterator
	 *
	 * @uses self::doRecursiveIterator()
	 * @uses self::hasFileTheExtention()
	 * @uses self::hasPrefix()
	 */
	private function _findFilesForExtensionPrefix( \FilesystemIterator $iterator )
	{
		foreach ( $iterator as $file ) {
			// check for directories
			if( $this->doRecursiveIterator( $file ) ) {
				$this->findFilesForPath( $file->getFileInfo()->getRealPath() );
			} else {
				$filename = $file->getFilename();

				if( $this->checkPrefix && !$this->hasPrefix( $filename ) ) {
					continue;
				} elseif( !$this->hasFileTheExtention( $file ) ) {
					continue;
				} else {
					$this->files[] = $this->createFileInstance( $file );
				}
			}
		}
	}

	/**
	 * Find all files for current iterator
	 *
	 * @param \FilesystemIterator $iterator
	 *
	 * @uses self::doRecursiveIterator()
	 */
	private function _findAllFiles( \FilesystemIterator $iterator )
	{
		foreach ( $iterator as $file ) {
			// check for directories
			if( $this->doRecursiveIterator( $file ) ) {
				$this->files += $this->findFilesForPath( $file->getFileInfo()->getRealPath() );
			} else {
				$this->files[] = $this->createFileInstance( $file );
			}
		}
	}
}