<?php

/**
 * Part of the Joomla Framework Language Package
 *
 * @copyright  Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved.
 * @license    GNU General Public License version 2 or later; see LICENSE
 */

namespace Joomla\Language;

/**
 * Helper class for the Language package
 *
 * @since  2.0
 */
class LanguageHelper
{
    /**
     * Checks if a language exists.
     *
     * This is a simple, quick check for the directory that should contain language files for the given user.
     *
     * @param   string  $lang      Language to check.
     * @param   string  $basePath  Directory to check for the specified language.
     *
     * @return  boolean  True if the language exists.
     *
     * @since   2.0
     */
    public function exists(string $lang, string $basePath): bool
    {
        return is_dir($this->getLanguagePath($basePath, $lang));
    }

    /**
     * Returns a associative array holding the metadata.
     *
     * @param   string  $lang  The name of the language.
     * @param   string  $path  The filepath to the language folder.
     *
     * @return  array|null  If $lang exists return key/value pair with the language metadata, otherwise return NULL.
     *
     * @since   2.0
     */
    public function getMetadata(string $lang, string $path): ?array
    {
        $path = $this->getLanguagePath($path, $lang);
        $file = $lang . '.xml';

        $result = null;

        if (is_file("$path/$file")) {
            $result = $this->parseXMLLanguageFile("$path/$file");
        }

        if (empty($result)) {
            return null;
        }

        return $result;
    }

    /**
     * Returns a list of known languages for an area
     *
     * @param   string  $basePath  The basepath to use
     *
     * @return  array  key/value pair with the language file and real name.
     *
     * @since   2.0
     */
    public function getKnownLanguages(string $basePath): array
    {
        return $this->parseLanguageFiles($this->getLanguagePath($basePath));
    }

    /**
     * Get the path to a language
     *
     * @param   string  $basePath  The basepath to use.
     * @param   string  $language  The language tag.
     *
     * @return  string  Path to the language folder
     *
     * @since   2.0
     */
    public function getLanguagePath(string $basePath, string $language = ''): string
    {
        $dir = $basePath . '/language';

        if (!empty($language)) {
            $dir .= '/' . $language;
        }

        return $dir;
    }

    /**
     * Searches for language directories within a certain base dir.
     *
     * @param   string  $dir  directory of files.
     *
     * @return  array  Array holding the found languages as filename => real name pairs.
     *
     * @since   2.0
     */
    public function parseLanguageFiles(string $dir = ''): array
    {
        $languages = [];

        // Search main language directory for subdirectories
        foreach (glob($dir . '/*', GLOB_NOSORT | GLOB_ONLYDIR) as $directory) {
            // But only directories with lang code format
            if (preg_match('#/[a-z]{2,3}-[A-Z]{2}$#', $directory)) {
                $dirPathParts = pathinfo($directory);
                $file         = $directory . '/' . $dirPathParts['filename'] . '.xml';

                if (!is_file($file)) {
                    continue;
                }

                try {
                    // Get installed language metadata from xml file and merge it with lang array
                    if ($metadata = $this->parseXMLLanguageFile($file)) {
                        $languages = array_replace($languages, [$dirPathParts['filename'] => $metadata]);
                    }
                } catch (\RuntimeException $e) {
                }
            }
        }

        return $languages;
    }

    /**
     * Parse XML file for language information.
     *
     * @param   string  $path  Path to the XML files.
     *
     * @return  array|null  Array holding the found metadata as a key => value pair.
     *
     * @since   2.0
     * @throws  \RuntimeException
     */
    public function parseXmlLanguageFile(string $path): ?array
    {
        if (!is_readable($path)) {
            throw new \RuntimeException('File not found or not readable');
        }

        // Try to load the file
        $xml = simplexml_load_file($path);

        if (!$xml) {
            return null;
        }

        // Check that it's a metadata file
        if ((string) $xml->getName() != 'metafile') {
            return null;
        }

        $metadata = [];

        /** @var \SimpleXMLElement $child */
        foreach ($xml->metadata->children() as $child) {
            $metadata[$child->getName()] = (string) $child;
        }

        return $metadata;
    }
}
