<?php
#BEGIN_LICENSE
#-------------------------------------------------------------------------
# Module: CMSMSExt (c) 2020-2021 by CMS Made Simple Foundation
#  An add-on module for CMS Made Simple to provide useful functions
#  and commonly used gui capabilities to other modules.
#-------------------------------------------------------------------------
# A fork of:
#
# Module: CGExtensions (c) 2008-2014 by Robert Campbell
#         (calguy1000@cmsmadesimple.org)
#
#-------------------------------------------------------------------------
#
# CMSMS - CMS Made Simple is (c) 2006 - 2021 by CMS Made Simple Foundation
# CMSMS - CMS Made Simple is (c) 2005 by Ted Kulp (wishy@cmsmadesimple.org)
# Visit the CMSMS Homepage at: http://www.cmsmadesimple.org
#
#-------------------------------------------------------------------------
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# However, as a special exception to the GPL, this software is distributed
# as an addon module to CMS Made Simple.  You may not use this software
# in any Non GPL version of CMS Made simple, or in any version of CMS
# Made simple that does not indicate clearly and obviously in its admin
# section that the site was built with CMS Made simple.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Or read it online: http://www.gnu.org/licenses/licenses.html#GPL
#
#-------------------------------------------------------------------------
#END_LICENSE

/**
 * A simple class for utilities related to manipulating directories.
 * and searching.  and to include builtin caching
 *
 * @package CMSMSExt
 * @category Utilities
 * @author  calguy1000 <calguy1000@cmsmadesimple.org>
 * @copyright Copyright 2010 by Robert Campbell
 */

//declare(strict_types=1);# revisit

/**
 * A simple class for utilities related to manipulating directories.
 * and searching.  and to include builtin caching
 *
 * @package CMSMSExt
 */
final class xt_dir
{
    /**
     * @ignore
     */
    private function __construct() {}

    /**
     * Recursively remove a directory
     * an alias for recursive_remove_directory
     *
     * @param string $directory The absolute path to the directory to be removed.
     */
    public static function recursive_rmdir(string $directory)
    {
        return self::recursive_remove_directory($directory);
    }
  
  
  /**
   * Recursively remove a directory
   *
   * @param string $directory The absolute path to the directory to be removed.
   *
   * @return bool
   *
   * @see \CMSMSExt\FS::dir_recursive_delete($directory)
   * @deprecated
   */
    public static function recursive_remove_directory(string $directory)
    {
//        if(substr($directory,-1) == '/') $directory = substr($directory,0,-1);
//        if(!file_exists($directory) || !is_dir($directory))	{
//            return FALSE;
//        }
//        elseif(is_readable($directory)) {
//            $handle = opendir($directory);
//            while (FALSE !== ($item = readdir($handle))) {
//                if($item != '.' && $item != '..') {
//                    $path = $directory.'/'.$item;
//                    if (is_dir($path)) {
//                        self::recursive_remove_directory($path);
//                    } else {
//                        unlink($path);
//                    }
//                }
//            }
//            closedir($handle);
//            if(!rmdir($directory)) return FALSE;
//        }
//        return TRUE;
      
      return \CMSMSExt\FS::dir_recursive_delete($directory);
    }


    /**
     * Return a list of all of the directories inside a parent
     * This method is NOT recursive
     *
     * @param string $parent The absolute path to the directory to search
     * @return mixed An array of directories directly below the parent, or false.
     */
    static public function dir_list(string $parent)
    {
//        if( empty($parent) ) return false;
//        if( !is_dir($parent) ) return false;
//
//        $dh = opendir($parent);
//        if( !$dh ) return false;
//
//        $results = array();
//        while( ($file = readdir($dh)) !== false ) {
//            if( $file == '.' || $file == '..' ) continue;
//            if( startswith($file,'.') ) continue;
//            if( is_dir($parent.'/'.$file) ) $results[] = $file;
//        }
//        closedir($dh);
//
//        if( count($results) == 0 ) return false;
//        return $results;
      
      $ret =\CMSMSExt\FS::dir_list($parent);
      
      return count($ret) ? $ret : FALSE;
    }
  
  
  /**
   * A function to return a list of all files in a directory that match a regular expression.
   * This method is not recursive.
   *
   * @param string $dir    The absolute path to the directory to search
   * @param string $regexp The regular expression
   * @param int    $limit  The maximum number of results to return.
   *
   * @return array|bool
   * @deprecated
   */
    public static function file_list_regexp(string $dir,string $regexp,int $limit=1000000)
    {
        if( empty($dir) ) return false;
        if( !is_dir($dir) ) return false;

        $dh = opendir($dir);
        if( !$dh ) return false;

        $results = array();
        while( ($file = readdir($dh)) !== false && count($results) < $limit  ) {
            if( $file == '.' || $file == '..' ) continue;
            if( preg_match( '/'.$regexp.'/', $file ) ) $results[] = $file;
        }
        closedir($dh);

        if( count($results) == 0 ) return false;
        return $results;
    }


    /**
     * Recursively create a subdirectory till the path exists.
     *
     * @deprecated
     * @see \CMSMSExt\FS::mkdirr($pathname);
     * @param string $pathname The path name to create
     * @param int $mode The octal permission for the newly created subdirectories
     * @param callable $callback An optional callback to call after creating the method.
     * @return bool
     */
    public static function mkdirr (string $pathname,int $mode = 0755,callable $callback = null) : bool
    {
//        // Check if directory already exists
//        if (empty($pathname) || is_dir ($pathname)) return TRUE;
//
//        // Ensure a file does not already exist with the same name
//        if (is_file ($pathname)) return TRUE; // RC: Modification such that this isn't an error
//
//        // Crawl up the directory tree
//        $next_pathname = dirname ($pathname);
//        if (self::mkdirr ($next_pathname, $mode)) {
//            if (!is_file ($pathname)) {
//                $res = mkdir ($pathname, $mode);
//                if( $res && is_callable($callback) ) call_user_func($callback,$pathname);
//                return $res;
//            }
//        }
//    return FALSE;
      
      $ret = \CMSMSExt\FS::mkdir_r($pathname, $mode);
  
      if( $ret && is_callable($callback) )
      {
        $callback($pathname);
      }

        return $ret;
    }


    /**
     * Given a directory, and a list of extensions, return a list of matching files.
     * This method will not return any directories.
     * This method is not recursive.
     *
     * @deprecated
     * @see \CMSMSExt\FS::get_file_list()
     *
     * @param string $dir The search directory
     * @param string $extensions A comma delimited list of extensions to return
     * @param bool $sorted Whether the output should be sorted (natural case) or not.
     * @return string[]
     */
    public static function get_file_list(string $dir,string $extensions,bool $sorted = true)
    {
//        $filetypes = array();
//        if( !empty($extensions) && !is_array($extensions) ) $filetypes = explode(',',$extensions);
//
//        $tmp = array();
//        foreach( $filetypes as $one ) {
//            $one = strtolower(trim($one));
//            if( $one ) $tmp[] = $one;
//        }
//        $filetypes = null;
//        if( count($tmp) ) $filetypes = $tmp;
//
//        $files = array();
//        $dh = opendir( $dir );
//        if( $dh ) {
//            while (false !== ($file = readdir($dh))) {
//                if( $file == '.' || $file == '..' ) continue;
//
//                $fullpath = cms_join_path($dir,$file);
//                if( is_dir( $fullpath ) ) continue;
//
//                $ext = substr(strrchr($file, '.'), 1);
//                if( !empty($filetypes) > 0 && !in_array( $ext, $filetypes ) ) continue;
//
//                $files[$file] = $file;
//            }
//            closedir($dh);
//        }
//
//        if( $sorted ) natcasesort($files);
//
//        return $files;
      
      return \CMSMSExt\FS::get_file_list($dir, $extensions);
    }
  
  
  /**
   * Given a filename, test if it matches the specified pattern
   *
   * @param string $filename       The test filename (string) or an array of strings
   * @param mixed  $pattern        An array of string patterns, or a single string.
   * @param bool   $case_sensitive Whether the pattern(s) is/are case sensitive
   * @param bool   $allow_empty    Whether or not an empty pattern is allowed (to match anything) (deprecated)
   *
   * @return bool
   */
    public static function file_matches_pattern(string $filename,$pattern,bool $case_sensitive = TRUE,bool $allow_empty = TRUE)
    {
        if( !is_array($pattern) ) {
            if( !$pattern && $allow_empty ) return TRUE; // no pattern = match everything
            $pattern = array($pattern);
        }
        for( $i = 0, $n = count($pattern); $i < $n; $i++ ) {
            $f = $filename;
            $p = $pattern[$i];
            if( !$p ) continue;
            if( !$case_sensitive ) {
                $f = strtolower($filename);
                $p = strtolower($pattern[$i]);
            }
            if( fnmatch($p,$f) ) return TRUE;
        }
        return FALSE;
    }
  
  /**
   * Similar to the PHP glob method this function will search all files in the directory (and below it) and return all
   * files that match the pattern.
   *
   * @param string $path           The full path to the input directory
   * @param mixed  $pattern        An array of string patterns.  Or a single string
   * @param string $mode           Either FILES, DIRS, or FULL.  If 'FILES', only files matching the pattern are
   *                               returned.  If 'DIRS' only directories are returned.  IF 'FULL', any file or
   *                               directory matching the pattern is returned.
   * @param mixed  $excludepattern An array of string patterns to exclude.  Or a simple string
   * @param int    $maxdepth       The maximum directory level to search.  Default is infinite.
   * @param int    $d              Internal
   *
   * @return array|void
   */
  public static function recursive_glob(
    string $path, $pattern, string $mode = 'FULL', $excludepattern = '', int $maxdepth = -1, int $d = 0
  )
  {
    if( !endswith($path, '/') ) { $path .= '/'; }
    
    $dirlist = [];
    
    if(
      ('FILES' !== $mode) && self::file_matches_pattern(basename($path), $pattern) &&
      !self::file_matches_pattern(basename($path), $excludepattern)
    )
    { $dirlist[] = $path; }
    
    if(!is_dir($path)) { return; }
    
    if($handle = opendir($path))
    {
      while(FALSE !== ($file = readdir($handle)))
      {
        if('.' === $file || '..' === $file) { continue; }
        
        $fs = $path . $file;
        
        if( !@is_dir($fs) && !startswith($file, '.') && !startswith($file, '_') )
        {
          if(
            'DIRS' !== $mode && self::file_matches_pattern($file, $pattern) &&
            !self::file_matches_pattern($file, $excludepattern, FALSE, FALSE)
          ) { $dirlist[] = $fs; }
        }
        else if(
          $d >= 0 && ($d < $maxdepth || $maxdepth < 0)  &&  !startswith($file, '.') && !startswith($file, '_') &&
          !self::file_matches_pattern($file, $excludepattern, TRUE, FALSE)
        )
        {
          $tmp = self::recursive_glob($fs . '/', $pattern, $mode, $excludepattern, $maxdepth, $d + 1);
          $dirlist = array_merge($dirlist, $tmp);
        }
      }
      closedir($handle);
    }
    
    return $dirlist;
  }
} // end of class

#
# EOF
#
