<?php

namespace Library;

/**
 * Logger trait to add logging functionality to a class.
 * If no logger is set all logging methods, error(), info(), etc. do nothing.
 * The context for a log message can be provided with the ndc_push() and ndc_pop methods.
 * NDC stands for Nested Diagnostic Context
 *
 * To use this trait add
 *   use \Library\Logger;
 * at the begin of your class declaration.
 */
trait LoggerTrait {

    /**
     * PSR-3 compatible logger instance, if not set no logging is done
     * @var \Psr\Log\LoggerInterface
     */
    protected $logger;

    /**
     * nested diagnostic context, used as context for log messages
     * @var array
     */
    protected $ndc_context = [];


    /**
     * initialize logger
     *
     * @param array $settings with (optional) keys: name, handler, path, level and format
     * @param string $base_dir base directory for relative paths
     * @return $this
     */
    public function init_logger(array $settings, string $base_dir = ".") {

        $name = isset($settings["name"]) ? $settings["name"] : "default";
        // create the logger
        $logger = new \Monolog\Logger($name);

        // add handlers
        if (isset($settings["handler"])) {
            foreach ($settings["handler"] as $handler) {
                $this->add_handler($logger, $handler, $base_dir);
            }
        } else {
            // if no handler array the settings
            // then the handler parameters are in the settings (backward compatibility)
            $this->add_handler($logger, $settings, $base_dir);
        }

        return $this->set_logger($logger);
    }


    /**
     * initialize logger
     *
     * @param Monolog\Logger $logger logger instance
     * @param array $settings with (optional) keys: path, level and format
     * @param string $base_dir base directory for relative paths
     * @return $this
     */
    protected function add_handler($logger, array $settings, string $base_dir) {

        $path = isset($settings["path"]) ? $settings["path"] : "php://stderr";
        // adjust log path if it is not a php i/o stream or absolute path
        // so only adjust relative paths
        if (strncmp($path, 'php:', 4) != 0 && strncmp($path, '/', 1)) {
            $path = "$base_dir/$path";
        }
        $level = isset($settings["level"]) ? $settings["level"] : "info";
        $format = isset($settings["format"]) ? $settings["format"] : "[%datetime%] %channel% %level_name% %extra% %context%: %message%";

        // add stream handler
        $stream = new \Monolog\Handler\StreamHandler($path, $level);
        $formatter = new \Monolog\Formatter\LineFormatter("$format\n");
        $stream->setFormatter($formatter);
        $logger->pushHandler($stream);
    }

    /**
     * get logger instance
     * @return \Psr\Log\LoggerInterface or null if not set
     */
    public function get_logger() {

        return $this->logger;
    }


    /**
     * set logger to send log messages to
     * @param \Psr\Log\LoggerInterface $logger
     * @return $this
     */
    public function set_logger(\Psr\Log\LoggerInterface $logger) {

        $this->logger = $logger;
        return $this;
    }

    /**
     * debug message
     * @param string $message
     * @param array $context if not set the ndc is used
     * @return $this
     */
    public function debug(string $message, array $context = null) {

        if (isset($this->logger)) {
            $this->logger->debug($message, isset($context) ? $context : $this->ndc_context);
        }
        return $this;
    }

    /**
     * info message
     * @param string $message
     * @param array $context if not set the ndc is used
     * @return $this
     */
    public function info(string $message, array $context = null) {

        if (isset($this->logger)) {
            $this->logger->info($message, isset($context) ? $context : $this->ndc_context);
        }
        return $this;
    }

    /**
     * warning message
     * @param string $message
     * @param array $context if not set the ndc is used
     * @return $this
     */
    public function warning(string $message, array $context = null) {

        if (isset($this->logger)) {
            $this->logger->warning($message, isset($context) ? $context : $this->ndc_context);
        }
        return $this;
    }

    /**
     * error message
     * @param string $message
     * @param array $context if not set the ndc is used
     * @return $this
     */
    public function error(string $message, array $context = null) {

        if (isset($this->logger)) {
            $this->logger->error($message, isset($context) ? $context : $this->ndc_context);
        }
        return $this;
    }

    /**
     * add message to the ndc (nested diagnostic context)
     * @param string $message
     * @return $this
     */
    public function ndc_push(string $message){

        $this->ndc_context[] = $message;
        return $this;
    }

    /**
     * remove last message from the ndc (nested diagnostic context)
     * @return $this
     */
    public function ndc_pop(){

        array_pop($this->ndc_context);
        return $this;
    }

}