<?php
/*
 *   $Id$
 *
 *   AbanteCart, Ideal OpenSource Ecommerce Solution
 *   http://www.AbanteCart.com
 *
 *   Copyright © 2011-2025 Belavier Commerce LLC
 *
 *   This source file is subject to Open Software License (OSL 3.0)
 *   License details are bundled with this package in the file LICENSE.txt.
 *   It is also available at this URL:
 *   <http://www.opensource.org/licenses/OSL-3.0>
 *
 *  UPGRADE NOTE:
 *    Do not edit or add to this file if you wish to upgrade AbanteCart to newer
 *    versions in the future. If you wish to customize AbanteCart for your
 *    needs, please refer to http://www.AbanteCart.com for more information.
 */

class ADebug
{
    static public $checkpoints = [];
    static public $queries = [];
    static public $queries_time = 0;
    static private $debug = 0; //off
    static private $debug_level = 0; //only exceptions
    static private $_is_init = false; //class init
    static private $_is_error = false;

    static private function isActive()
    {
        if (!self::$_is_init) {
            if (defined('INSTALL')) {
                self::$debug = 1;
                self::$debug_level = 0;
            } else {
                if (class_exists('Registry')) {
                    $registry = Registry::getInstance();
                    if ($registry->has('config')) {

                        if ($registry->get('config')->has('config_debug')) {
                            self::$debug = $registry->get('config')->get('config_debug');
                        }

                        if ($registry->get('config')->has('config_debug_level')) {
                            self::$debug_level = $registry->get('config')->get('config_debug_level');
                        }

                    }
                }
            }
            self::$_is_init = true;
        }

        return self::$debug;
    }

    static function set_query($query, $time, $backtrace)
    {
        if (!self::isActive()) {
            return false;
        }
        self::$queries[] = [
            'sql'  => $query,
            'time' => sprintf('%01.5f', $time),
            'file' => $backtrace['file'],
            'line' => $backtrace['line'],
        ];
        self::$queries_time += $time;
        return true;
    }

    static function checkpoint($name)
    {
        if (!self::isActive()) {
            return false;
        }

        $e = new Exception();
        self::$checkpoints[] = [
            'name'           => $name,
            'time'           => self::microtime(),
            'memory'         => memory_get_usage(),
            'included_files' => count(get_included_files()),
            'queries'        => count(self::$queries),
            'type'           => 'checkpoint',
            'trace'          => $e->getTraceAsString(),
        ];
        return true;
    }

    static function variable($name, $variable)
    {
        if (!self::isActive()) {
            return false;
        }

        ob_start();
        echo '<pre>';
        print_r($variable);
        echo '</pre>';
        $msg = ob_get_clean();

        self::$checkpoints[] = [
            'name' => $name,
            'msg'  => $msg,
            'type' => 'variable',
        ];
        return true;
    }

    static function error($name, $code, $msg)
    {

        self::$checkpoints[] = [
            'name'           => $name,
            'time'           => self::microtime(),
            'memory'         => memory_get_usage(),
            'included_files' => count(get_included_files()),
            'queries'        => count(self::$queries),
            'msg'            => $msg,
            'code'           => $code,
            'type'           => 'error',
        ];
        self::$_is_error = true;
    }

    static function warning($name, $code, $msg)
    {

        self::$checkpoints[] = [
            'name'           => $name,
            'time'           => self::microtime(),
            'memory'         => memory_get_usage(),
            'included_files' => count(get_included_files()),
            'queries'        => count(self::$queries),
            'msg'            => $msg,
            'code'           => $code,
            'type'           => 'warning',
        ];
        self::$_is_error = true;
    }

    static function microtime()
    {
        list($usec, $sec) = explode(' ', microtime());
        return ((float)$usec + (float)$sec);
    }

    static function display_queries($start, $end)
    {

        if ($end - $start <= 0) {
            return null;
        }

        echo '<table class="mysql" >
            <tr>
                <td><b>Time</b></td>
                <td><b>File</b></td>
                <td><b>Line</b></td>
                <td><b>SQL</b></td>
            </tr>';
        for ($i = $start; $i < $end; $i++) {
            $key = $i;
            $query = self::$queries[$key];

            echo '<tr ' . ($key % 2 ? 'class="even"' : '') . '>
                       <td><b>' . $query['time'] . '</b></td>
                       <td>' . $query['file'] . '</td>
                       <td>' . $query['line'] . '</td>
                       <td>' . $query['sql'] . '</td>
                  </tr>';
        }
        echo '</table>';
    }

    static function display_errors()
    {
        if (!self::$_is_error) {
            return null;
        }
        echo '<table class="debug_info" >
                    <tr>
                        <td><b>Name</b></td>
                        <td><b>Info</b></td>
                    </tr>';

        $show = ['error', 'warning'];
        foreach (self::$checkpoints as $c) {
            if (!in_array($c['type'], $show)) {
                continue;
            }
            echo '<tr class="debug_' . $c['type'] . '"><td><b>' . $c['code'] . '::' . $c['name'] . '</b><br /></td>';
            echo '<td>' . $c['msg'] . '<br /></td>';
        }
        echo '</table>';
    }

    static function display()
    {
        if (!self::isActive()) {
            return false;
        }
        $previous = [];
        $cumulative = [];

        $first = true;

        ob_start();

        switch (self::$debug_level) {

            case 0 :
                //show only exceptions
                //shown in Abc_Exception::displayError
                break;
            case 1 :
                //show errors and warnings
                self::display_errors();
                break;
            case 2 :
                // #1 + mysql site load, php file execution time and page elements load time
                self::display_errors();
                $time = 0.0;
                //count php execution time
                foreach (self::$checkpoints as $name => $c) {
                    if ($c['type'] != 'checkpoint') {
                        continue;
                    }
                    if ($first) {
                        $first = false;
                        $cumulative = $c;
                    }
                    $time = sprintf("%01.4f", $c['time'] - $cumulative['time']);
                }
                echo '<div class="debug_info">';
                echo 'Queries - ' . count(self::$queries) . '<br />';
                echo 'Queries execution time - ' . sprintf('%01.5f', self::$queries_time) . '<br />';
                echo 'PHP Execution time - ' . $time . '<br />';
                echo '</div>';
                break;

            case 3 :
            case 4 :
            case 5 :
                // #2 + basic logs and stack of execution
                // #3 + dump mysql statements
                // #4 + call stack
                echo '<table class="debug_info" >
                    <tr>
                        <td><b>Name</b></td>
                        <td><b>Info</b></td>
                    </tr>';

                foreach (self::$checkpoints as $c) {
                    echo '<tr class="debug_' . $c['type'] . '" ><td><b>' . $c['name'] . '</b><br /></td>';
                    echo '<td>';
                    if ($first && $c['type'] != 'variable') {
                        $previous = [
                            'time'           => $c['time'],
                            'memory'         => 0,
                            'included_files' => 0,
                            'queries'        => 0,
                        ];
                        $first = false;
                        $cumulative = $c;
                    }

                    switch ($c['type']) {
                        case 'variable':
                        case 'error':
                        case 'warning':
                            echo $c['msg'] . '<br />';
                            break;
                        case 'checkpoint':
                            echo '- Memory: ' . (number_format($c['memory'] - $previous['memory'])) . ' ('
                                . number_format($c['memory']) . ')' . '<br />';
                            echo '- Files: ' . ($c['included_files'] - $previous['included_files']) . ' ('
                                . $c['included_files'] . ')' . '<br />';
                            echo '- Queries: ' . ($c['queries'] - $previous['queries']) . ' (' . $c['queries'] . ')' . '<br />';
                            echo '- Time: ' . sprintf("%01.4f", $c['time'] - $previous['time'])
                                . ' (' . sprintf("%01.4f", $c['time'] - $cumulative['time']) . ')' . '<br />';
                            if (self::$debug_level > 3) {
                                self::display_queries($previous['queries'], $c['queries']);
                            }
                            if (self::$debug_level > 4) {
                                echo '<pre>' . $c['trace'] . '</pre>';
                            }
                            $previous = $c;
                            break;
                    }

                    echo '</td></tr>';
                }
                echo '</table>';

                break;

            default:

        }

        $debug = ob_get_clean();
        switch (self::$debug) {
            case 1:
                //show
                echo $debug;
                break;
            case 2:
                //log
                $text = strip_tags(str_replace('<br />', "\r\n", $debug));
                if ($text) {
                    require_once(DIR_CORE . 'lib' . DS . 'log.php');
                    $registry = Registry::getInstance();
                    if ($registry->has('log')) {
                        $log = $registry->get('log');
                    } else {
                        $log = new ALog(DIR_LOGS . 'error.txt');
                    }
                    $log->write($text);
                }
                break;
            default:
        }
        return true;
    }

}