<?php
/**
 * Copyright (c) 2015 Ori Livneh <ori@wikimedia.org>
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * @file
 * @author Ori Livneh <ori@wikimedia.org>
 */

namespace RelPath;

/**
 * Split a path into path components.
 *
 * @param string $path File path.
 * @return array Array of path components.
 */
function splitPath( $path ) {
	$fragments = array();

	while ( true ) {
		$cur = dirname( $path );
		if ( $cur === $path || ( $cur === '.' && basename( $path ) === $path ) ) {
			break;
		}

		$fragment = trim( substr( $path, strlen( $cur ) ), '/' );

		if ( !$fragments ) {
			$fragments[] = $fragment;
		} elseif ( $fragment === '..' && basename( $cur ) !== '..' ) {
			$cur = dirname( $cur );
		} elseif ( $fragment !== '.' ) {
			$fragments[] = $fragment;
		}

		$path = $cur;
	}

	if ( $path !== '' ) {
		$fragments[] = trim( $path, '/' );
	}

	return array_reverse( $fragments );
}

/**
 * Return a relative filepath to path either from the current directory or from
 * an optional start directory. Both paths must be absolute.
 *
 * @param string $path File path.
 * @param string $start Start directory. Optional; if not specified, the current
 *  working directory will be used.
 * @return array|bool Array of path components or false if file does not exist.
 */
function getRelativePath( $path, $start = null ) {
	if ( $start === null ) {
		$start = getcwd();
	}

	if ( substr( $path, 0, 1 ) !== '/' || substr( $start, 0, 1 ) !== '/' ) {
		return false;
	}

	$pathParts = splitPath( $path );
	$countPathParts = count( $pathParts );

	$startParts = splitPath( $start );
	$countStartParts = count( $startParts );

	$commonLength = min( $countPathParts, $countStartParts );
	for ( $i = 0; $i < $commonLength; $i++ ) {
		if ( $startParts[$i] !== $pathParts[$i] ) {
			break;
		}
	}

	$relList = ( $countStartParts > $i )
		? array_fill( 0, $countStartParts - $i, '..' )
		: array();

	$relList = array_merge( $relList, array_slice( $pathParts, $i ) );

	return implode( '/', $relList ) ?: '.';
}


/**
 * Join path components.
 *
 * @param string $base Base path.
 * @param string $path File path to join to base path.
 * @return string
 */
function joinPath( $base, $path ) {
	if ( substr( $path, 0, 1 ) === '/' ) {
		return $path;  // $path is absolute.
	}

	if ( substr( $base, 0, 1 ) !== '/' ) {
		return false;  // $base is relative.
	}

	$pathParts = splitPath( $path );
	$resultParts = splitPath( $base );

	while ( ( $part = array_shift( $pathParts ) ) !== null ) {
		switch ( $part ) {
		case '.':
			break;
		case '..':
			if ( count( $resultParts ) > 1 ) {
				array_pop( $resultParts );
			}
			break;
		default:
			$resultParts[] = $part;
			break;
		}
	}

	return implode( '/', $resultParts );
}
