<?php class diskusage implements crudable {

    const FILESYSTEM_CACHE_ICEBEAR = 'filesystem';
    const FILESYSTEM_CACHE_FORMULATRIX = 'filesystem_formulatrix';
    const FILESYSTEM_CACHE_RIGAKU = 'filesystem_rigaku';

	/**
	 * @param array $request
	 * @return array|null
	 * @throws ServerException
	 */
    public static function getAll(array $request=[]): ?array {
        if (strtoupper(substr(PHP_OS, 0, 3)) !== 'LIN') {
            throw new ServerException('Disk usage information is not available because the server is not running Linux.');
        }

        $cache = static::FILESYSTEM_CACHE_ICEBEAR;
        if (!caching::has($cache)) {
            $info = self::getDiskUsage();
            caching::setContent($cache, $info, '+12 hours');
        } else {
            $info = caching::getContent($cache);
        }
        //TODO It would be more generic, and future-proof, to "ls" the cache dir and iterate through all filesystem_*
        foreach([static::FILESYSTEM_CACHE_FORMULATRIX,static::FILESYSTEM_CACHE_RIGAKU] as $cache){
            if (caching::has($cache)) {
                $cachedInfo = caching::getContent($cache);
                $info['total'] = (int)($info['total']) + (int)($cachedInfo['total']);
                $info['rows'] = array_merge($info['rows'], $cachedInfo['rows']);
            }
        }
        return $info;
    }

    /**
     * @throws BadRequestException
     */
    public static function getFieldValidations(): array { throw new BadRequestException('Not implemented'); }

    /**
     * @throws BadRequestException
     */
    public static function getFieldHelpTexts(): array { throw new BadRequestException('Not implemented'); }

	/**
	 * @param int $id
	 * @return array|null
	 * @throws BadRequestException
	 */
    public static function getById(int $id): ?array { throw new BadRequestException('Not implemented'); }

	/**
	 * @param string $name
	 * @return array|null
	 * @throws BadRequestException
	 */
    public static function getByName(string $name): ?array {
        throw new BadRequestException('Not implemented');
    }

	/**
	 * @param string $key
	 * @param string $value
	 * @param array $request
	 * @return array|null
	 * @throws BadRequestException
	 */
	public static function getByProperty(string $key, string $value, array $request = []): ?array {
		throw new BadRequestException('Not implemented');
	}

	/**
	 * @param array $keyValuePairs
	 * @param array $request
	 * @return array|null
	 * @throws BadRequestException
	 */
	public static function getByProperties(array $keyValuePairs, array $request = []): ?array {
		throw new BadRequestException('Not implemented');
	}

	/**
     * @param array $request
     * @throws BadRequestException
     */
    public static function create(array $request=[]): array {
        throw new BadRequestException('Not implemented');
    }

	/**
	 * @param int $id
	 * @param array $request
	 * @return array
	 * @throws BadRequestException
	 */
    public static function update(int $id, array $request=[]): array {
        throw new BadRequestException('Not implemented');
    }

	/**
	 * @param int $id
	 * @return array
	 * @throws BadRequestException
	 */
    public static function delete(int $id): array {
        throw new BadRequestException('Not implemented');
    }

    /**
     * @throws BadRequestException
     */
    public static function canCreate(): bool {
        throw new BadRequestException('Not implemented');
    }

	/**
	 * @param int $id
	 * @return bool
	 * @throws BadRequestException
	 */
    public static function canUpdate(int $id): bool {
        throw new BadRequestException('Not implemented');
    }

    /**
     * @return array
     * @throws ServerException
     */
    private static function getDiskUsage(): array {
        $result = shell_exec('df -h');
        //Expected output format
        //          $result='Filesystem                         Size  Used Avail Use% Mounted on
        //          udev                               2.0G  4.0K  2.0G   1% /dev
        //          tmpfs                              392M  628K  392M   1% /run
        //          /dev/dm-0                          143G  4.9G  131G   4% /
        //          none                               4.0K     0  4.0K   0% /sys/fs/cgroup
        //          none                               5.0M     0  5.0M   0% /run/lock
        //          none                               2.0G     0  2.0G   0% /run/shm
        //          none                               100M     0  100M   0% /run/user
        //          /dev/sdb1                          1.8T  589G  1.2T  33% /icebearstore
        //          //130.231.188.9/RockMakerStorage   1.9T  1.4T  452G  76% /RockMakerStorage
        //          //130.231.188.9/RockMakerStorage2  2.8T  2.6T  223G  93% /RockMakerStorage2
        //          /dev/sda1                          236M   40M  184M  18% /boot';
        if (!str_starts_with($result, 'Filesystem')) {
            throw new ServerException('Disk usage information is not available. Either it was not in an expected format, or there was an error.');
        }

        $result = explode("\n", $result);
        array_shift($result); //Lose the header
        $disks = array();
        foreach ($result as $disk) {
            $disk = trim($disk);
            if ('' == $disk) {
                continue;
            }
            $disk = preg_split('/\s+/', $disk);

            if (count($disk) !== 6) {
                throw new ServerException('Disk usage information is not available. It was not in an expected format.');
            }

            //Not interested if below 50GB
            $total = $disk[1];
            if (false !== stripos($total, 'K') || false !== stripos($total, 'M') ||
                (false !== stripos($total, 'G') && (int)$total < 50)) {
                continue;
            }

            $disks[] = array(
                'mountpoint' => $disk[5],
                'filesystem' => $disk[0],
                'size' => $disk[1] . 'B',
                'used' => $disk[2] . 'B',
                'free' => $disk[3] . 'B',
                'percent' => (int)$disk[4],
            );
        }
        return array(
            'total' => count($disks),
            'rows' => $disks
        );
    }

	/**
	 * Converts a raw number of bytes into a human-friendly string like "1.35TB"
	 * @param int $numBytes
	 * @return string
	 */
    public static function bytesToFriendlyString(int $numBytes): string {
        $sizes = array(
            'KB' => 1024,
            'MB' => 1024 * 1024,
            'GB' => 1024 * 1024 * 1024,
            'TB' => 1024 * 1024 * 1024 * 1024,
        );
        $ret = $numBytes . 'B';
        foreach ($sizes as $k => $v) {
            if ($numBytes / $v >= 1) {
                $ret = number_format($numBytes / $v, 2) . $k;
            } else {
                break;
            }
        }
        return $ret;
    }

}