<?php  // vim:set et ts=4 sw=4 sts=4:
# Copyright (C) 2003-2014 hipplay Inc.
#
# This is UNPUBLISHED PROPRIETARY SOURCE CODE OF hipplay Inc.
# The copyright notice above does not evidence any actual or intended
# publication of such source code. The contents of this file may not
# be disclosed to third parties, copied or duplicated in any form, in
# whole or in part, without the prior written permission of
# hipplay Inc.
#
namespace HipPlayConnect\FileSystem;


class AxPathMapper {

    const DEBUG_MAPPING = FALSE;

    static $MOUNT_MAP  = NULL;
    static $PATH_TO_DEVICE_MAP = NULL;
    static $DEVICE_TO_PATH_MAP = NULL;
    static $HOST_TO_GUEST_MAP = NULL;

    public static function mapMount($hostPath) {
        if (self::DEBUG_MAPPING)
            error_log("Host path: $hostPath");

        $mountMap = self::getMountMap();
        foreach ($mountMap as $host => $guest)
            if (substr($hostPath, 0, strlen($host)) == $host)
                return $guest . substr($hostPath, strlen($host));

        return NULL;
    }

    /**
     * Locate the filesystem path from a file id
     *
     * @param string $fileId The file identifier
     *
     * @return string The filesystem path of the specified file
     */
    public static function mapFilePath ($fileId) {
        if (self::DEBUG_MAPPING)
            error_log("File ID: $fileId");

        // Separate the device id from the subpath
        list($uuid, $subpath) = explode(":", $fileId);

        if (self::DEBUG_MAPPING) {
            error_log("UUID: $uuid");
            error_log("SubPath: $subpath");
        }

        // Calculate the mountpoint from the UUID
        $deviceMap = self::getDeviceMap();
        if (!isset($deviceMap[$uuid]))
            return NULL;

        $mountPoint = $deviceMap[$uuid];

        if (self::DEBUG_MAPPING)
            error_log("Mount Point: $mountPoint");

        // Parse the file subpath
        $filePathParts = explode('/', self::normalizePath($subpath, FALSE));

        // Check for hidden or empty filenames
        foreach ($filePathParts as $filename)
            if (strlen($filename) == 0 || substr($filename, 0, 1) == '.')
                return NULL;

        // Combine the mount point with the validated parts
        $filePath = implode('/', array_merge(array($mountPoint), $filePathParts));

        if (self::DEBUG_MAPPING)
            error_log("Mapped Path: $filePath");

        if (!file_exists($filePath) || is_link($filePath)) {
            error_log("link or directory does not exist: $filePath");
            return NULL;
        }

        return $filePath;
    }

    /**
     * Locate the filesystem path from a file id
     *
     * @param string $filePath The file identifier
     *
     * @return string The filesystem path of the specified file
     */
    protected static function mapFileId ($filePath)
    {
        $filePath = self::normalizePath($filePath);
        $filePathLen = strlen($filePath);
        if (self::DEBUG_MAPPING)
            error_log("File Path: $filePath");

        $pathMap = self::getPathMap();
        foreach ($pathMap as $sourcePath => $device) {
            $sourcePathLen = strlen($sourcePath);
            if ($filePathLen < $sourcePathLen + 1)
                continue;

            if (substr($filePath, 0, $sourcePathLen) != $sourcePath)
                continue;

            if (substr($filePath, $sourcePathLen, 1) != '/')
                continue;

            $subpath = substr($filePath, $sourcePathLen);

            if (self::DEBUG_MAPPING) {
                error_log("Mount Point: $device");
                error_log("Subpath: $subpath");
                error_log("File ID: $device:$subpath");
            }

            return $device . ":" . $subpath;
        }

        error_log("No mount point found for $filePath");
        return NULL;
    }

    protected static function getDeviceMap () {
        self::ensurePathMapping();
        return self::$DEVICE_TO_PATH_MAP;
    }

    protected static function getMountMap () {
        self::ensurePathMapping();
        return self::$MOUNT_MAP;
    }

    protected static function getPathMap () {
        self::ensurePathMapping();
        return self::$PATH_TO_DEVICE_MAP;
    }

    protected static function ensurePathMapping() {
        if (self::$DEVICE_TO_PATH_MAP === NULL ||
            self::$PATH_TO_DEVICE_MAP === NULL ||
            self::$MOUNT_MAP === NULL) {
            $maps = self::loadPathMapping();
            # TODO: Throw an exception
            self::parsePathMapping($maps);
        }
    }

    protected static function parsePathMapping(array $maps) {
        self::$PATH_TO_DEVICE_MAP = array();
        self::$DEVICE_TO_PATH_MAP = array();
        self::$MOUNT_MAP = array();

        foreach ($maps as $map) {
            list($host,$guest,$uuid) = $map;
            $guest = self::normalizePath($guest);
            self::$DEVICE_TO_PATH_MAP[$uuid] = $guest;
            self::$PATH_TO_DEVICE_MAP[$guest] = $uuid;
            self::$MOUNT_MAP[$host] = $guest;
        }

        if (self::DEBUG_MAPPING) {
            error_log("Mount Mapping: " . json_encode(self::$MOUNT_MAP, JSON_PRETTY_PRINT));
            error_log("Device Mapping: " . json_encode(self::$DEVICE_TO_PATH_MAP, JSON_PRETTY_PRINT));
        }
    }

    protected static function loadPathMapping () {
        static $mappingPath = '/tmpHost/path_mapping.txt';
        $fPathMap = @fopen($mappingPath, 'r');
        if (FALSE === $fPathMap) {
            error_log("Could not load path mapping.");
            return NULL;
        }

        $maps = array();
        while (FALSE != ($line = fgets($fPathMap)))
            $maps[] = explode(" ", trim($line));

        return $maps;
    }

    protected static function normalizePath ($path, $leading = TRUE) {
        $path = trim($path, '/');
        $path = str_replace('//', '/', $path);

        return $leading
                ? '/' . $path
                : $path;
    }

} 