<?php

namespace App\Repositories\Core;

use League\CLImate\CLImate;
use Timer;

class Debug
{
    // $is_debug binaire 0 ou 1 debug, 0 ou 1 force output
    public static $_instance;

    public static $app;

    public static $debugbar;

    private function __construct()
    {
        // $this->debugbar = DebugBar::getInstance()->debugbar;
    }

    public static function isDebugbar()
    {
        return class_exists('Barryvdh\Debugbar\ServiceProvider') ? true : false;
    }

    public static function isClockwork()
    {
        return class_exists('Clockwork\Support\Laravel\ClockworkServiceProvider') ? true : false;
    }

    public static function start($var = '', $params = [], $txt = '')
    {
        if (! static::isDebug()) {
            return false;
        }
        $var = (empty($var)) ? static::getMethod() : $var;
        $params = (empty($params)) ? static::getArgs() : $params;
        /*
        foreach ($params as $key => $value) {
            $params[$key] = substr($value,30);
        }
        */
        // TODO Fixer la longueur des params string  passés
        if (is_null($params)) {
            $params = [];
        }

        Timer::start($var, $params);

        if (static::isDebugbar()) {
            \Debugbar::startMeasure($var, $txt);
        }

        if (static::isClockwork()) {
            // clock()->startEvent($var, $txt);
        }
    }

    public static function stop($var = '')
    {
        if (! static::isDebug()) {
            return false;
        }
        $var = (empty($var)) ? static::getMethod() : $var;
        Timer::stop();

        if (static::isDebugbar()) {
            \Debugbar::stopMeasure($var);
        }

        if (static::isClockwork()) {
            // clock()->endEvent($var);
        }
    }

    public static function render($force = false)
    {
        static::dump((string) Timer::result(), '', $force);
    }

    public static function memory($force = false)
    {
        static::dump(memory_get_usage(), '', $force);
    }

    public static function breakpoint($msg = '', $cat = '', $force = true)
    {
        static::dump($msg, $cat, $force);
        static::header('paramètres');
        static::dump(static::getArgs(), '', $force);
        static::footer('paramètres');
        static::render($force);
        static::backtrace($force);
        exit;
    }

    /**
     * dump un message uniquement si debug est true
     *
     * @param  string  $msg [description]
     * @return [type]      [description]
     */
    public static function message($msg, $cat = '')
    {
        if (static::isDebug()) {
            static::dump($msg, $cat);
        }
    }

    /**
     * force la sortie d'un dump, sans passer par la debugbar ou test si debug est true
     *
     * @param  string  $msg [description]
     * @return [type]      [description]
     */
    public static function fdump($msg, $cat = '')
    {
        static::dump($msg, $cat, 3);
    }

    /**
     * dump un message suivant le handler de sortie prévu (log, debugbar, cli, ...)
     *
     * @param  [type]  $msg   [description]
     * @param  bool  $force si true, force la sortie en output direct
     * @return [type]         [description]
     */
    public static function dump($msg, $cat = '', $force = false)
    {
        $cat = $cat ? $cat : self::getClass();
        if ($force || self::isForcedOutput()) {
            dump(self::getLocation());
            dump($msg);
        }

        if (! self::isDebug()) {
            return;
        }
        if (static::isCLI()) {
            self::dumpCli($msg, $cat);
        }
        if (static::isDebugbar()) {
            self::dumpDebugbar($msg, $cat);
        }
        if (static::isClockwork()) {
            self::dumpClockwork($msg, $cat);
        }

    }

    public static function dumpDebugbar($msg, $cat = '', $force = false)
    {
        \Debugbar::addMessage(self::getLocation(), $cat);
        \Debugbar::addMessage($msg, $cat);
    }

    public static function dumpClockwork($msg, $cat = '')
    {
        clock($msg);
    }

    public static function dumpCli($msg, $cat = '')
    {
        $climate = new CLImate;
        // $climate->yellow()->bold()->out($message);
        // $climate->white()->bold()->out($output);
        // $climate->out($msg);
        // dump(self::getLocation());
        // dump($msg);
    }

    public static function header($titre = '')
    {
        static::dump("*********** $titre ************");
    }

    public static function footer($titre = '')
    {
        static::dump("*********** Fin $titre ************");
    }

    public static function isDebug()
    {
        return self::getIsDebug() && 1;
    }

    public static function isForcedOutput()
    {
        return self::getIsDebug() > 1;
    }

    public static function getIsDebug()
    {
        $caller = (array) static::getCaller();
        // dump($caller);
        if ($caller['class']) {
            if (isset($caller['class']::$is_debug)) {
                $is_debug = $caller['class']::$is_debug;
            } else {
                $is_debug = false;
            }
        } else {
            dump('la isDebug::165');
            dump($caller);
            $is_debug = true;
        }

        return $is_debug;
    }

    public static function backtrace($force = false)
    {
        $txt = '';
        $backtrace = debug_backtrace();
        $backtrace = array_reverse($backtrace);
        foreach ($backtrace as $item) {
            $caller = isset($item['class']) ? $item['class'].$item['type'].$item['function'] : $item['function'];
            $place = isset($item['file']) ? $item['file'].' at '.$item['line'] : '';
            $txt .= "$caller | $place \n";
        }
        static::dump($txt, '', $force);
        // dump($backtrace);
    }

    public static function getLocation()
    {
        return static::getMethod().' at '.static::getFile().' line '.static::getLine();
    }

    public static function getMethod()
    {
        return static::getClass().static::getType().static::getFunction();
    }

    public static function getClass()
    {
        return static::getCaller()->class;
    }

    public static function getFunction()
    {
        return static::getCaller()->function;
    }

    public static function getType()
    {
        return static::getCaller()->type;
    }

    public static function getArgs()
    {
        // dump(static::getCaller()->args);
        return static::getCaller()->args;
    }

    public static function getLine()
    {
        return static::getParent()->line;
    }

    public static function getFile()
    {
        return static::getParent()->file;
    }

    public static function getCaller()
    {
        $backtrace = debug_backtrace();
        // dump($backtrace);
        $k = 1;
        while ($backtrace[$k]['class'] == 'App\Repositories\Core\Debug') {
            $k++;
        }

        return (object) $backtrace[$k];
    }

    public static function getParent()
    {
        $backtrace = debug_backtrace();
        // dump($backtrace);
        $k = 1;
        while ($backtrace[$k]['class'] == 'App\Repositories\Core\Debug') {
            $k++;
        }

        return (object) $backtrace[$k - 1];
    }

    public static function getRoot()
    {
        $backtrace = debug_backtrace();
        $object = isset($backtrace[0]['object']) ? $backtrace[0]['object'] : null;
        $k = 1;
        while (isset($backtrace[$k]) && (! isset($backtrace[$k]['object']) || $object === $backtrace[$k]['object'])) {
            $k++;
        }

        return isset($backtrace[$k]['object']) ? $backtrace[$k]['object'] : null;
    }

    public static function isCLI()
    {
        return PHP_SAPI == 'cli';
    }
}
