<?php

namespace Sibs\Stargate\Utils;

defined('ABSPATH') || exit;

class SibsBrowser
{
    public const BROWSER_UNKNOWN = 'unknown';
    public const VERSION_UNKNOWN = 'unknown';

    public const BROWSER_OPERA = 'Opera';
    public const BROWSER_OPERA_MINI = 'Opera Mini';
    public const BROWSER_WEBTV = 'WebTV';
    public const BROWSER_IE = 'Internet Explorer';
    public const BROWSER_POCKET_IE = 'Pocket Internet Explorer';
    public const BROWSER_KONQUEROR = 'Konqueror';
    public const BROWSER_ICAB = 'iCab';
    public const BROWSER_OMNIWEB = 'OmniWeb';
    public const BROWSER_FIREBIRD = 'Firebird';
    public const BROWSER_FIREFOX = 'Firefox';
    public const BROWSER_ICEWEASEL = 'Iceweasel';
    public const BROWSER_SHIRETOKO = 'Shiretoko';
    public const BROWSER_MOZILLA = 'Mozilla';
    public const BROWSER_AMAYA = 'Amaya';
    public const BROWSER_LYNX = 'Lynx';
    public const BROWSER_SAFARI = 'Safari';
    public const BROWSER_IPHONE = 'iPhone';
    public const BROWSER_IPOD = 'iPod';
    public const BROWSER_IPAD = 'iPad';
    public const BROWSER_CHROME = 'Chrome';
    public const BROWSER_ANDROID = 'Android';
    public const BROWSER_GOOGLEBOT = 'GoogleBot';
    public const BROWSER_SLURP = 'Yahoo! Slurp';
    public const BROWSER_W3CVALIDATOR = 'W3C Validator';
    public const BROWSER_BLACKBERRY = 'BlackBerry';
    public const BROWSER_ICECAT = 'IceCat';
    public const BROWSER_NOKIA_S60 = 'Nokia S60 OSS Browser';
    public const BROWSER_NOKIA = 'Nokia Browser';
    public const BROWSER_MSN = 'MSN Browser';
    public const BROWSER_MSNBOT = 'MSN Bot';

    public const BROWSER_NETSCAPE_NAVIGATOR = 'Netscape Navigator';
    public const BROWSER_GALEON = 'Galeon';
    public const BROWSER_NETPOSITIVE = 'NetPositive';
    public const BROWSER_PHOENIX = 'Phoenix';

    public const PLATFORM_UNKNOWN = 'unknown';
    public const PLATFORM_WINDOWS = 'Windows';
    public const PLATFORM_WINDOWS_CE = 'Windows CE';
    public const PLATFORM_APPLE = 'Apple';
    public const PLATFORM_LINUX = 'Linux';
    public const PLATFORM_OS2 = 'OS/2';
    public const PLATFORM_BEOS = 'BeOS';
    public const PLATFORM_IPHONE = 'iPhone';
    public const PLATFORM_IPOD = 'iPod';
    public const PLATFORM_IPAD = 'iPad';
    public const PLATFORM_BLACKBERRY = 'BlackBerry';
    public const PLATFORM_NOKIA = 'Nokia';
    public const PLATFORM_FREEBSD = 'FreeBSD';
    public const PLATFORM_OPENBSD = 'OpenBSD';
    public const PLATFORM_NETBSD = 'NetBSD';
    public const PLATFORM_SUNOS = 'SunOS';
    public const PLATFORM_OPENSOLARIS = 'OpenSolaris';
    public const PLATFORM_ANDROID = 'Android';

    public const OPERATING_SYSTEM_UNKNOWN = 'unknown';
    private $_agent = '';
    private $_browser_name = '';
    private $_version = '';
    private $_platform = '';
    private $_os = '';
    private $_is_mobile = false;
    private $_is_robot = false;

    public function __construct($useragent = '')
    {
        $this->reset();

        if ('' != $useragent) {
            $this->setUserAgent($useragent);
        } else {
            $this->determine();
        }
    }

    /**
     * Returns a formatted string with a summary of the details of the browser.
     *
     * @return string formatted string with a summary of the browser
     */
    public function __toString()
    {
        return '<strong>'.__('Browser Name', 'sibs-woo-stg').":</strong> {$this->getBrowser()}<br/>\n"
               .'<strong>'.__('Browser Version', 'sibs-woo-stg').":</strong> {$this->getVersion()}<br/>\n"
               .'<strong>'.__('Browser User Agent String', 'sibs-woo-stg').":</strong> {$this->getUserAgent()}<br/>\n"
               .'<strong>'.__('Platform', 'sibs-woo-stg').":</strong> {$this->getPlatform()}<br/>";
    }

    /**
     * Reset all properties.
     */
    public function reset()
    {
        $this->_agent = $_SERVER['HTTP_USER_AGENT'] ?? '';
        $this->_browser_name = self::BROWSER_UNKNOWN;
        $this->_version = self::VERSION_UNKNOWN;
        $this->_platform = self::PLATFORM_UNKNOWN;
        $this->_os = self::OPERATING_SYSTEM_UNKNOWN;
        $this->_is_mobile = false;
        $this->_is_robot = false;
    }

    /**
     * Check to see if the specific browser is valid.
     *
     * @param string $browserName
     *
     * @return bool
     */
    public function isBrowser($browserName)
    {
        return 0 == strcasecmp($this->_browser_name, trim($browserName));
    }

    /**
     * The name of the browser.  All return types are from the class contants.
     *
     * @return string Name of the browser
     */
    public function getBrowser()
    {
        return $this->_browser_name;
    }

    /**
     * Set the name of the browser.
     *
     * @param $browser The name of the Browser
     */
    public function setBrowser($browser)
    {
        return $this->_browser_name = $browser;
    }

    /**
     * The name of the platform.  All return types are from the class contants.
     *
     * @return string Name of the browser
     */
    public function getPlatform()
    {
        return $this->_platform;
    }

    /**
     * Set the name of the platform.
     *
     * @param $platform The name of the Platform
     */
    public function setPlatform($platform)
    {
        return $this->_platform = $platform;
    }

    /**
     * The version of the browser.
     *
     * @return string Version of the browser (will only contain alpha-numeric characters and a period)
     */
    public function getVersion()
    {
        return $this->_version;
    }

    /**
     * Set the version of the browser.
     *
     * @param $version The version of the Browser
     */
    public function setVersion($version)
    {
        $this->_version = preg_replace('/[^0-9,.,a-z,A-Z-]/', '', $version);
    }

    /**
     * Is the browser from a mobile device?
     *
     * @return bool
     */
    public function isMobile()
    {
        return $this->_is_mobile;
    }

    /**
     * Is the browser from a robot (ex Slurp,GoogleBot)?
     *
     * @return bool
     */
    public function isRobot()
    {
        return $this->_is_robot;
    }

    /**
     * Get the user agent value in use to determine the browser.
     *
     * @return string The user agent from the HTTP header
     */
    public function getUserAgent()
    {
        return $this->_agent;
    }

    /**
     * Set the user agent value (the construction will use the HTTP header value - this will overwrite it).
     *
     * @param $agent_string The value for the User Agent
     */
    public function setUserAgent($agent_string)
    {
        $this->reset();
        $this->_agent = $agent_string;
        $this->determine();
    }

    /**
     * Used to determine if the browser is actually "chromeframe".
     *
     * @return bool
     */
    public function isChromeFrame()
    {
        return false !== strpos($this->_agent, 'chromeframe');
    }

    /**
     * Set the Browser to be mobile.
     *
     * @param bool
     * @param mixed $value
     */
    protected function setMobile($value = true)
    {
        $this->_is_mobile = $value;
    }

    /**
     * Set the Browser to be a robot.
     *
     * @param bool
     * @param mixed $value
     */
    protected function setRobot($value = true)
    {
        $this->_is_robot = $value;
    }

    /**
     * Protected routine to calculate and determine what the browser is in use (including platform).
     */
    protected function determine()
    {
        $this->checkPlatform();
        $this->checkBrowsers();
    }

    /**
     * Protected routine to determine the browser type.
     *
     * @return bool
     */
    protected function checkBrowsers()
    {
        return
            $this->checkBrowserFirefox()
            || $this->checkBrowserChrome()

            // common mobile
            || $this->checkBrowserAndroid()
            || $this->checkBrowseriPad()
            || $this->checkBrowseriPod()
            || $this->checkBrowseriPhone()

            // common bots
            || $this->checkBrowserGoogleBot()
            || $this->checkBrowserMSNBot()

            // WebKit base check (post mobile and others)
            || $this->checkBrowserSafari()

            // everyone else
            || $this->checkBrowserW3CValidator()
            || $this->checkBrowserMozilla(); // Mozilla is such an open standard that you must check it last
    }

    protected function checkBrowserGoogleBot()
    {
        if (false !== stripos($this->_agent, 'googlebot')) {
            $aresult = explode('/', stristr($this->_agent, 'googlebot'));
            $aversion = explode(' ', $aresult[1]);
            $this->setVersion(str_replace(';', '', $aversion[0]));
            $this->_browser_name = self::BROWSER_GOOGLEBOT;
            $this->setRobot(true);

            return true;
        }

        return false;
    }

    protected function checkBrowserMSNBot()
    {
        if (false !== stripos($this->_agent, 'msnbot')) {
            $aresult = explode('/', stristr($this->_agent, 'msnbot'));
            $aversion = explode(' ', $aresult[1]);
            $this->setVersion(str_replace(';', '', $aversion[0]));
            $this->_browser_name = self::BROWSER_MSNBOT;
            $this->setRobot(true);

            return true;
        }

        return false;
    }

    protected function checkBrowserW3CValidator()
    {
        if (false !== stripos($this->_agent, 'W3C-checklink')) {
            $aresult = explode('/', stristr($this->_agent, 'W3C-checklink'));
            $aversion = explode(' ', $aresult[1]);
            $this->setVersion($aversion[0]);
            $this->_browser_name = self::BROWSER_W3CVALIDATOR;

            return true;
        }
        if (false !== stripos($this->_agent, 'W3C_Validator')) {
            // Some of the Validator versions do not delineate w/ a slash - add it back in
            $ua = str_replace('W3C_Validator ', 'W3C_Validator/', $this->_agent);
            $aresult = explode('/', stristr($ua, 'W3C_Validator'));
            $aversion = explode(' ', $aresult[1]);
            $this->setVersion($aversion[0]);
            $this->_browser_name = self::BROWSER_W3CVALIDATOR;

            return true;
        }

        return false;
    }

    protected function checkBrowserChrome()
    {
        if (false !== stripos($this->_agent, 'Chrome')) {
            $aresult = explode('/', stristr($this->_agent, 'Chrome'));
            $aversion = explode(' ', $aresult[1]);
            $this->setVersion($aversion[0]);
            $this->setBrowser(self::BROWSER_CHROME);

            return true;
        }

        return false;
    }

    protected function checkBrowserFirefox()
    {
        if (false === stripos($this->_agent, 'safari')) {
            if (preg_match('/Firefox[\/ \(]([^ ;\)]+)/i', $this->_agent, $matches)) {
                $this->setVersion($matches[1]);
                $this->setBrowser(self::BROWSER_FIREFOX);

                return true;
            }
            if (preg_match('/Firefox$/i', $this->_agent, $matches)) {
                $this->setVersion('');
                $this->setBrowser(self::BROWSER_FIREFOX);

                return true;
            }
        }

        return false;
    }

    protected function checkBrowserMozilla()
    {
        if (false !== stripos($this->_agent, 'mozilla') && preg_match('/rv:[0-9].[0-9][a-b]?/i', $this->_agent) && false === stripos($this->_agent, 'netscape')) {
            $aversion = explode(' ', stristr($this->_agent, 'rv:'));
            preg_match('/rv:[0-9].[0-9][a-b]?/i', $this->_agent, $aversion);
            $this->setVersion(str_replace('rv:', '', $aversion[0]));
            $this->setBrowser(self::BROWSER_MOZILLA);

            return true;
        }
        if (false !== stripos($this->_agent, 'mozilla') && preg_match('/rv:[0-9]\.[0-9]/i', $this->_agent) && false === stripos($this->_agent, 'netscape')) {
            $aversion = explode('', stristr($this->_agent, 'rv:'));
            $this->setVersion(str_replace('rv:', '', $aversion[0]));
            $this->setBrowser(self::BROWSER_MOZILLA);

            return true;
        }
        if (false !== stripos($this->_agent, 'mozilla') && preg_match('/mozilla\/([^ ]*)/i', $this->_agent, $matches) && false === stripos($this->_agent, 'netscape')) {
            $this->setVersion($matches[1]);
            $this->setBrowser(self::BROWSER_MOZILLA);

            return true;
        }

        return false;
    }

    protected function checkBrowserSafari()
    {
        if (false !== stripos($this->_agent, 'Safari') && false === stripos($this->_agent, 'iPhone') && false === stripos($this->_agent, 'iPod')) {
            $aresult = explode('/', stristr($this->_agent, 'Version'));

            if (isset($aresult[1])) {
                $aversion = explode(' ', $aresult[1]);
                $this->setVersion($aversion[0]);
            } else {
                $this->setVersion(self::VERSION_UNKNOWN);
            }
            $this->setBrowser(self::BROWSER_SAFARI);

            return true;
        }

        return false;
    }

    protected function checkBrowseriPhone()
    {
        if (false !== stripos($this->_agent, 'iPhone')) {
            $aresult = explode('/', stristr($this->_agent, 'Version'));

            if (isset($aresult[1])) {
                $aversion = explode(' ', $aresult[1]);
                $this->setVersion($aversion[0]);
            } else {
                $this->setVersion(self::VERSION_UNKNOWN);
            }
            $this->setMobile(true);
            $this->setBrowser(self::BROWSER_IPHONE);

            return true;
        }

        return false;
    }

    protected function checkBrowseriPad()
    {
        if (false !== stripos($this->_agent, 'iPad')) {
            $aresult = explode('/', stristr($this->_agent, 'Version'));

            if (isset($aresult[1])) {
                $aversion = explode(' ', $aresult[1]);
                $this->setVersion($aversion[0]);
            } else {
                $this->setVersion(self::VERSION_UNKNOWN);
            }
            $this->setMobile(true);
            $this->setBrowser(self::BROWSER_IPAD);

            return true;
        }

        return false;
    }

    protected function checkBrowseriPod()
    {
        if (false !== stripos($this->_agent, 'iPod')) {
            $aresult = explode('/', stristr($this->_agent, 'Version'));

            if (isset($aresult[1])) {
                $aversion = explode(' ', $aresult[1]);
                $this->setVersion($aversion[0]);
            } else {
                $this->setVersion(self::VERSION_UNKNOWN);
            }
            $this->setMobile(true);
            $this->setBrowser(self::BROWSER_IPOD);

            return true;
        }

        return false;
    }

    protected function checkBrowserAndroid()
    {
        if (false !== stripos($this->_agent, 'Android')) {
            $aresult = explode(' ', stristr($this->_agent, 'Android'));

            if (isset($aresult[1])) {
                $aversion = explode(' ', $aresult[1]);
                $this->setVersion($aversion[0]);
            } else {
                $this->setVersion(self::VERSION_UNKNOWN);
            }
            $this->setMobile(true);
            $this->setBrowser(self::BROWSER_ANDROID);

            return true;
        }

        return false;
    }

    /**
     * Determine the user's platform.
     */
    protected function checkPlatform()
    {
        if (false !== stripos($this->_agent, 'windows')) {
            $this->_platform = self::PLATFORM_WINDOWS;
        } elseif (false !== stripos($this->_agent, 'iPad')) {
            $this->_platform = self::PLATFORM_IPAD;
        } elseif (false !== stripos($this->_agent, 'iPod')) {
            $this->_platform = self::PLATFORM_IPOD;
        } elseif (false !== stripos($this->_agent, 'iPhone')) {
            $this->_platform = self::PLATFORM_IPHONE;
        } elseif (false !== stripos($this->_agent, 'mac')) {
            $this->_platform = self::PLATFORM_APPLE;
        } elseif (false !== stripos($this->_agent, 'android')) {
            $this->_platform = self::PLATFORM_ANDROID;
        } elseif (false !== stripos($this->_agent, 'linux')) {
            $this->_platform = self::PLATFORM_LINUX;
        } elseif (false !== stripos($this->_agent, 'Nokia')) {
            $this->_platform = self::PLATFORM_NOKIA;
        } elseif (false !== stripos($this->_agent, 'BlackBerry')) {
            $this->_platform = self::PLATFORM_BLACKBERRY;
        } elseif (false !== stripos($this->_agent, 'FreeBSD')) {
            $this->_platform = self::PLATFORM_FREEBSD;
        } elseif (false !== stripos($this->_agent, 'OpenBSD')) {
            $this->_platform = self::PLATFORM_OPENBSD;
        } elseif (false !== stripos($this->_agent, 'NetBSD')) {
            $this->_platform = self::PLATFORM_NETBSD;
        } elseif (false !== stripos($this->_agent, 'OpenSolaris')) {
            $this->_platform = self::PLATFORM_OPENSOLARIS;
        } elseif (false !== stripos($this->_agent, 'SunOS')) {
            $this->_platform = self::PLATFORM_SUNOS;
        } elseif (false !== stripos($this->_agent, 'OS\/2')) {
            $this->_platform = self::PLATFORM_OS2;
        } elseif (false !== stripos($this->_agent, 'BeOS')) {
            $this->_platform = self::PLATFORM_BEOS;
        } elseif (false !== stripos($this->_agent, 'win')) {
            $this->_platform = self::PLATFORM_WINDOWS;
        }
    }
}
