<?php
/**
 * Piwik - Open source web analytics
 *
 * @link http://piwik.org
 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
 *
 */
namespace Piwik\ViewDataTable;

use Piwik\API\Proxy;
use Piwik\Common;
use Piwik\Piwik;
use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable;

/**
 * Provides a means of creating {@link Piwik\Plugin\ViewDataTable} instances by ID.
 *
 * ### Examples
 * 
 * **Creating a ViewDataTable for a report**
 * 
 *     // method in MyPlugin\Controller
 *     public function myReport()
 *     {
 *         $view = Factory::build('table', 'MyPlugin.myReport');
 *         $view->config->show_limit_control = true;
 *         $view->config->translations['myFancyMetric'] = "My Fancy Metric";
 *         return $view->render();
 *     }
 * 
 * **Displaying a report in another way**
 * 
 *     // method in MyPlugin\Controller
 *     // use the same data that's used in myReport() above, but transform it in some way before
 *     // displaying.
 *     public function myReportShownDifferently()
 *     {
 *         $view = Factory::build('table', 'MyPlugin.myReport', 'MyPlugin.myReportShownDifferently');
 *         $view->config->filters[] = array('MyMagicFilter', array('an arg', 'another arg'));
 *         return $view->render();
 *     }
 * 
 * **Force a report to be shown as a bar graph**
 * 
 *     // method in MyPlugin\Controller
 *     // force the myReport report to show as a bar graph if there is no viewDataTable query param,
 *     // even though it is configured to show as a table.
 *     public function myReportShownAsABarGraph()
 *     {
 *         $view = Factory::build('graphVerticalBar', 'MyPlugin.myReport', 'MyPlugin.myReportShownAsABarGraph',
 *                                $forceDefault = true);
 *         return $view->render();
 *     }
 * 
 *
 * @api
 */
class Factory
{
    /**
     * Cache for getDefaultTypeViewDataTable result.
     *
     * @var array
     */
    private static $defaultViewTypes = null;

    /**
     * Creates a {@link Piwik\Plugin\ViewDataTable} instance by ID. If the **viewDataTable** query parameter is set,
     * this parameter's value is used as the ID.
     * 
     * See {@link Piwik\Plugin\ViewDataTable} to read about the visualizations that are packaged with Piwik.
     * 
     * @param string|null $defaultType A ViewDataTable ID representing the default ViewDataTable type to use. If
     *                                 the **viewDataTable** query parameter is not found, this value is used as
     *                                 the ID of the ViewDataTable to create.
     *                                 
     *                                 If a visualization type is configured for the report being displayed, it
     *                                 is used instead of the default type. (See {@hook ViewDataTable.getDefaultType}).
     *                                 If nothing is configured for the report and `null` is supplied for this
     *                                 argument, **table** is used.
     * @param string|false $apiAction The API method for the report that will be displayed, eg,
     *                               `'UserSettings.getBrowser'`.
     * @param string|false $controllerAction The controller name and action dedicated to displaying the report. This
     *                                       action is used when reloading reports or changing the report visualization.
     *                                       Defaulted to `$apiAction` if `false` is supplied.
     * @param bool $forceDefault If true, then the visualization type that was configured for the report will be
     *                           ignored and `$defaultType` will be used as the default.
     * @throws \Exception
     * @return \Piwik\Plugin\ViewDataTable
     */
    public static function build($defaultType = null, $apiAction = false, $controllerAction = false, $forceDefault = false)
    {
        if (false === $controllerAction) {
            $controllerAction = $apiAction;
        }

        $defaultViewType = self::getDefaultViewTypeForReport($apiAction);

        if (!$forceDefault && !empty($defaultViewType)) {
            $defaultType = $defaultViewType;
        }

        $type = Common::getRequestVar('viewDataTable', false, 'string');
        // Common::getRequestVar removes backslashes from the defaultValue in case magic quotes are enabled.
        // therefore do not pass this as a default value to getRequestVar()
        if ('' === $type) {
            $type = $defaultType ? : HtmlTable::ID;
        }

        $visualizations = Manager::getAvailableViewDataTables();

        if (array_key_exists($type, $visualizations)) {
            return new $visualizations[$type]($controllerAction, $apiAction);
        }

        if (class_exists($type)) {
            return new $type($controllerAction, $apiAction);
        }

        if (array_key_exists($defaultType, $visualizations)) {
            return new $visualizations[$defaultType]($controllerAction, $apiAction);
        }

        if (array_key_exists(HtmlTable::ID, $visualizations)) {
            return new $visualizations[HtmlTable::ID]($controllerAction, $apiAction);
        }

        throw new \Exception('No visualization found to render ViewDataTable');
    }

    /**
     * Returns the default viewDataTable ID to use when determining which visualization to use.
     */
    private static function getDefaultViewTypeForReport($apiAction)
    {
        $defaultViewTypes = self::getDefaultTypeViewDataTable();
        return isset($defaultViewTypes[$apiAction]) ? $defaultViewTypes[$apiAction] : false;
    }

    /**
     * Returns a list of default viewDataTables ID to use when determining which visualization to use for multiple
     * reports.
     */
    private static function getDefaultTypeViewDataTable()
    {
        if (null === self::$defaultViewTypes) {
            self::$defaultViewTypes = array();
            /**
             * Triggered when gathering the default view types for all available reports.
             * 
             * If you define your own report, you may want to subscribe to this event to
             * make sure the correct default Visualization is used (for example, a pie graph,
             * bar graph, or something else).
             *
             * If there is no default type associated with a report, the **table** visualization
             * used.
             * 
             * **Example**
             * 
             *     public function getDefaultTypeViewDataTable(&$defaultViewTypes)
             *     {
             *         $defaultViewTypes['Referrers.getSocials']       = HtmlTable::ID;
             *         $defaultViewTypes['Referrers.getUrlsForSocial'] = Pie::ID;
             *     }
             * 
             * @param array &$defaultViewTypes The array mapping report IDs with visualization IDs.
             */
            Piwik::postEvent('ViewDataTable.getDefaultType', array(&self::$defaultViewTypes));
        }

        return self::$defaultViewTypes;
    }
}
