<?php
/**
 * @author: Jörg Reinholz, fastix WebDesign & Consult, Kassel - http://www.fastix.org/
 * @return: array
 * @see: phpdoc functions opendir, readdir, sort, date
 * @uses: humanReadableBin.php, sort_2D_hash.php
**/

require_once './lib/humanReadableBin.php';
require_once './lib/sort_2D_hash.php';

class Dir2Array
{
    protected   $DH              = false;
    protected   $dir             = false;
    private     $sortItem        = 'name';
    private     $sortUpDown      = 'up';
    private     $sortOrder       = SORT_REGULAR;
    private     $foldersFirst    = 0;
    private     $showHiddenFiles = 0;
    private     $showDirLinks    = 0;
    private     $arIgnoreFilesRegex = array();
    private     $errors = false;
    public      $filetimemHumanStyle = 'Y-m-d H:i'; # the same format like in date()

    public function __construct ( $dir = false ) {

        $dir = realpath($dir);

        if (! $dir) {
            trigger_error(__FILE__ . ' class Dir2Array: Es wurde kein existierendes Verzeichnis übergeben.', E_USER_ERROR);
            setError (1, E_USER_ERROR, 'Es wurde kein existierendes Verzeichnis angebeben.');
            return false;
        }

        if (! is_dir($dir)) {
            trigger_error(__FILE__ . ' class Dir2Array: Das Verzeichnis "' . $dir . '" existiert nicht.', E_USER_NOTICE);
            setError (2, E_USER_ERROR, 'Das Verzeichnis "' . $dir . '" existiert nicht.');
            return false;
        }

        if (! is_readable($dir)) {
            trigger_error(__FILE__ . '  class Dir2Array: Das Verzeichnis "' . $dir . '" ist nicht lesbar.', E_USER_NOTICE);
            setError (4, E_USER_ERROR, 'Das Verzeichnis "' . $dir . '" ist nicht lesbar.');
            return false;
        }



        $this -> sortOrder        = ( SORT_NATURAL | SORT_FLAG_CASE );
        $this -> showFoldersFirst = true;
        $this -> showHiddenFiles  = false;
        $this -> showDirLinks     = false;
        $this -> DH = opendir($dir);
        $this -> dir = $dir;
        $this -> arIgnoreFilesRegex[0] = '/\.projekte_dir_cache/';
        return true;
    }


    private function setError ($errorNumber, $ErrorType, $ErrorString) {
        $arErrors = $this -> errors;
        if (! $arError) {
            $errIndex=0;
        } else {
            $newErrorIndex = count($arError);
            $arErrors[$newErrorIndex]['ErrorNumber']=$errorNumber;
            $arErrors[$newErrorIndex]['ErrorType']=$errorType;
            $arErrors[$newErrorIndex]['ErrorString']=$errorString;
        }
        $this -> errors = $arErrors;
    }

    public function getErrors () {
        return $this -> errors;
    }

    public function getRealDir () {
        return $this -> dir;
    }


    public function setSortItem($sortItem) {

    $arGueltige = array(
            'name',
            'time',
            'size',
            'mime'
        );
        if (! in_array($sortItem, $arGueltige) ) {
            trigger_error(__FILE__ . 'class Dir2Array: sorItem() wurde eine ungültiges SortItem übergeben', E_USER_NOTICE);
            setError (8, E_USER_NOTICE, 'class Dir2Array: sortItem() wurde ein ungültiger Wert übergeben.  Die Aktion wird ignoriert.');
            return false;
        } else {
            $this -> sortItem = $sortItem;
            return true;
        }
    }

    public function setSortOrder($sortOrder) {
        if (! is_int($sortOrder)) {
            trigger_error(__FILE__ . 'class Dir2Array: sortOrder() wurde ein nichtnumerischer Wert übergeben', E_USER_NOTICE);
            setError (16, E_USER_NOTICE, 'sortOrder() wurde ein nichtnumerischer Wert übergeben. Die Aktion wird ignoriert.');
            return false;
        }

        $arGueltige = array(
            SORT_REGULAR,
            SORT_NUMERIC,
            SORT_STRING,
            SORT_LOCALE_STRING,
            SORT_NATURAL,
            ( SORT_STRING | SORT_FLAG_CASE ),
            ( SORT_NATURAL | SORT_FLAG_CASE )
        );

        if (! in_array($sortOrder, $arGueltige) ) {
            trigger_error(__FILE__ . 'class Dir2Array: sortOrder() wurde eine ungültige SortOrder übergeben', E_USER_NOTICE);
            setError (32, E_USER_NOTICE, 'class Dir2Array: sortOrder() wurde ein ungültiger Wert übergeben.  Die Aktion wird ignoriert.');
            return false;
        } else {
            $this -> sortOrder = $sortOrder;
            return true;
        }
    }

    public function setUpDown ($v) {
        $v = strtolower($v);
        if ('up' == $v or 'down' == $v) {
            $this -> sortUpDown = $v;
            return true;
        } else {
            trigger_error(__FILE__ . 'class Dir2Array: setUpDown wurde ein ungültiger Wert ('.$v.') übergeben. Gültig ist "up" oder "down". Die Aktion wird ignoriert.', E_USER_NOTICE);
            setError (32, E_USER_NOTICE, 'class Dir2Array: setUpDown wurde ein ungültiger Wert ('.$v.') übergeben. Gültig ist "up" oder "down". Die Aktion wird ignoriert.');
            return false;
        }
    }
    public function setShowFoldersFirst($v) {
        $this -> showFoldersFirst = $v;
        return true;
    }

    public function setShowHiddenFiles($v) {
        $this -> showHiddenFiles = $v;
        return true;
    }

    public function setShowDirLinks($v) {
        $this -> showDirLinks = $v;
        return true;
    }

    public function addIgnoreFilesRegex($v) {
        $ar = $this -> arIgnoreFilesRegex;
        $ar[] = $v;
        $this -> arIgnoreFilesRegex = $ar;
        return true;
    }

    public function removeIgnoreFilesRegex($v) {
        $ar = $this -> arIgnoreFilesRegex;
        if ( in_array ($v, $ar) ) {
            $ar2 = array();
            foreach ($ar as $s) {
                if ($s != $v) { $ar2[] = $s; }
            }
            $this -> arIgnoreFilesRegex = $ar2;
            return true;
        } else {
            return false;
        }
    }

    public function getArray() {

        $cacheName =
              '.projekte_dir_cache/' . ','
            . $this -> sortItem . ','
            . $this -> sortUpDown . ','
            . $this -> sortOrder . ','
            . $this -> foldersFirst . ','
            . $this -> showHiddenFiles . ','
            . $this -> showDirLinks . ','
            . $this -> filetimemHumanStyle . ','
            . 'I:' . str_replace('/', '#', implode('I:', $this -> arIgnoreFilesRegex) );

            $cacheNameOpen=str_replace(array('*', '?'), array('\*', '\?'), $cacheName);

        #print '<pre>' . htmlspecialchars($cacheName) . '</pre>';

        if ( is_file($this -> dir . '/'.$cacheNameOpen) ) {
           return json_decode(file_get_contents($this -> dir . '/' .$cacheNameOpen), 1);
        }

        $dirs  = array();
        $files = array();
        $ar    = array();

        $sortItem    = $this -> sortItem;
        $sortUpDown  = $this -> sortUpDown;

        if (! $this -> DH ) {
            return false;
        }
        $dir = $this->dir . '/';

        while (false !== ($s = readdir($this -> DH))) {
            $insert = false;
            $c = $s[0];
            $showHiddenFiles    = $this -> showHiddenFiles;
            $showDirLinks       = $this -> showDirLinks;
            $arIgnoreFilesRegex = $this -> arIgnoreFilesRegex;

            if ( $showHiddenFiles or '.' != $c ) { $insert = true; }
            if ( (!$showDirLinks) and ($s == '.' or $s == '..') ) { $insert = false; }
            if ($insert and $arIgnoreFilesRegex) {
                foreach ($arIgnoreFilesRegex as $pattern) {
                    if ($insert and preg_match( $pattern, $s ) ) { $insert = false; }
                }
            }

            if ($insert) {
                if ( is_dir( $dir . $s ) ) {
                    $dirs[] = $s;
                } elseif ( is_file( $dir . $s ) ) {
                    $files[] = $s;
                }
            }
        }

        $arNames = array();

        $showFoldersFirst = $this -> showFoldersFirst;
        if ($showFoldersFirst) {
            sort($dirs,  $this -> sortOrder);
            if ('name' == $sortItem and 'down' == $sortUpDown) { $dirs = array_reverse($dirs); }
            foreach($dirs as $s) { $arNames[] = $s; }
            sort($files, $this -> sortOrder);
            if ('name' == $sortItem and 'down' == $sortUpDown) { $files = array_reverse($files); }
            foreach($files as $s) { $arNames[] = $s; }
        } else {
            foreach($dirs  as $s) { $arNames[] = $s; }
            foreach($files as $s) { $arNames[] = $s; }
            sort($arNames,  $this -> sortOrder);
            if ('name' == $sortItem and 'down' == $sortUpDown) { $arNames = array_reverse($arNames); }
        }
        $ar = array();


        if( ! function_exists('finfo_open') ) {
            trigger_error(__FILE__ . 'class Dir2Array: file_info ist nicht installiert', E_USER_NOTICE);
            setError (32, E_USER_NOTICE, 'Die PHP-Erweiterung file_info ist nicht installiert. Rückfall auf mime_content_type()');
            $resFinfo = false;
        } else {
            $resFinfo = new finfo(FILEINFO_MIME);
        }

        if (count($arNames)) {
            foreach ($arNames as $s) {
                $f = $dir . $s;
                $ar[$s]['type'] = filetype($f);
                $ar[$s]['size'] = filesize($f);
                $ar[$s]['sizeHuman'] = humanReadableBin($ar[$s]['size'], 2, '&nbsp;' ,'B');
                $ar[$s]['filemtime'] = filemtime($f);
                $ar[$s]['filemtimeHuman'] = date($this -> filetimemHumanStyle, $ar[$s]['filemtime']);

                if ($resFinfo) {
                    $info = $resFinfo -> file($f);
                    if ( strpos($info, ';') ) {
                        $parts=explode(';', $info, 2);
                        $ar[$s]['mime'] = $parts[0];
                        $ar[$s]['charset']=str_replace( ' charset=', '', $parts[1] );
                    } else {
                        $ar[$s]['mime']=$info;
                        $ar[$s]['charset'] = false;
                    }
                } else {
                    $ar[$s]['mime'] = mime_content_type($f);
                    $ar[$s]['charset'] = false;

                }
            }
        }

        if ('mime' == $sortItem) {
            $ar = sort_2D_hash( $ar, 'mime', SORT_STRING );
            if ('down' == $sortUpDown) {
                $ar = array_reverse($ar);
            }
        } elseif ('size' == $sortItem) {
            $ar = sort_2D_hash( $ar, 'size', SORT_NUMERIC );
            if ('down' == $sortUpDown) {
                $ar = array_reverse($ar);
            }
        } elseif ('time' == $sortItem) {
            $ar = sort_2D_hash( $ar, 'filemtime', SORT_NUMERIC );
            if ('down' == $sortUpDown) {
                $ar = array_reverse($ar);
            }
        }


        ## Cache: ##
        if (! is_dir( $dir . '/.projekte_dir_cache/' ) ) {
            if (is_writable($dir) ) {
                mkdir($dir . '/.projekte_dir_cache/');
            }
        }
        if (is_dir( $dir . '/.projekte_dir_cache/' ) ) {
            file_put_contents( $dir . '/'.$cacheName, json_encode($ar, JSON_PRETTY_PRINT) );
        }
        return $ar;
    }
}