<?php if (!defined('BASEPATH')) exit('No direct script access allowed');

// (c) https://github.com/IgorVBelousov/php_idn
// (c) http://belousovv.ru/myscript/phpIDN

/**
 * Finds the character code for a UTF-8 character: like ord() but for UTF-8.
 *
 * @author Nicolas Thouvenin <nthouvenin@gmail.com>
 * @copyright 2008 Nicolas Thouvenin
 * @license http://opensource.org/licenses/LGPL-2.1 LGPL v2.1
 */
function mso_ordUTF8($c, $index = 0, &$bytes = null)
{
    $len = strlen($c);
    $bytes = 0;
    if ($index >= $len)
        return false;
    $h = ord($c{
        $index});
    if ($h <= 0x7F) {
        $bytes = 1;
        return $h;
    } else if ($h < 0xC2)
        return false;
    else if ($h <= 0xDF && $index < $len - 1) {
        $bytes = 2;
        return ($h & 0x1F) << 6 | (ord($c{
            $index + 1}) & 0x3F);
    } else if ($h <= 0xEF && $index < $len - 2) {
        $bytes = 3;
        return ($h & 0x0F) << 12 | (ord($c{
            $index + 1}) & 0x3F) << 6
            | (ord($c{
                $index + 2}) & 0x3F);
    } else if ($h <= 0xF4 && $index < $len - 3) {
        $bytes = 4;
        return ($h & 0x0F) << 18 | (ord($c{
            $index + 1}) & 0x3F) << 12
            | (ord($c{
                $index + 2}) & 0x3F) << 6
            | (ord($c{
                $index + 3}) & 0x3F);
    } else
        return false;
}

/**
 * Encode UTF-8 domain name to IDN Punycode
 *
 * @param string $value Domain name
 * @return string Encoded Domain name
 *
 * @author Igor V Belousov <igor@belousovv.ru>
 * @copyright 2013, 2015 Igor V Belousov
 * @license http://opensource.org/licenses/LGPL-2.1 LGPL v2.1
 * @link http://belousovv.ru/myscript/phpIDN
 */
function mso_EncodePunycodeIDN($value)
{
    if (function_exists('idn_to_ascii')) {
        return idn_to_ascii($value);
    }

    /* search subdomains */
    $sub_domain = explode('.', $value);
    if (count($sub_domain) > 1) {
        $sub_result = '';
        foreach ($sub_domain as $sub_value) {
            $sub_result .= '.' . mso_EncodePunycodeIDN($sub_value);
        }
        return substr($sub_result, 1);
    }

    /* http://tools.ietf.org/html/rfc3492#section-6.3 */
    $n      = 0x80;
    $delta  = 0;
    $bias   = 72;
    $output = [];

    $input  = [];
    $str    = $value;
    while (mb_strlen($str, 'UTF-8') > 0) {
        array_push($input, mb_substr($str, 0, 1, 'UTF-8'));
        $str = (version_compare(PHP_VERSION, '5.4.8', '<')) ? mb_substr($str, 1, mb_strlen($str, 'UTF-8'), 'UTF-8') : mb_substr($str, 1, null, 'UTF-8');
    }

    /* basic symbols */
    $basic = preg_grep('/[\x00-\x7f]/', $input);
    $b = $basic;

    if ($b == $input) {
        return $value;
    }
    $b = count($b);
    if ($b > 0) {
        $output = $basic;
        /* add delimeter */
        $output[] = '-';
    }
    unset($basic);
    /* add prefix */
    array_unshift($output, 'xn--');

    $input_len = count($input);
    $h = $b;

    $ord_input = [];

    while ($h < $input_len) {
        $m = 0x10FFFF;
        for ($i = 0; $i < $input_len; ++$i) {
            $ord_input[$i] = mso_ordUtf8($input[$i]);
            if (($ord_input[$i] >= $n) && ($ord_input[$i] < $m)) {
                $m = $ord_input[$i];
            }
        }
        if (($m - $n) > (0x10FFFF / ($h + 1))) {
            return $value;
        }
        $delta += ($m - $n) * ($h + 1);
        $n = $m;

        for ($i = 0; $i < $input_len; ++$i) {
            $c = $ord_input[$i];
            if ($c < $n) {
                ++$delta;
                if ($delta == 0) {
                    return $value;
                }
            }
            if ($c == $n) {
                $q = $delta;
                for ($k = 36;; $k += 36) {
                    if ($k <= $bias) {
                        $t = 1;
                    } elseif ($k >= ($bias + 26)) {
                        $t = 26;
                    } else {
                        $t = $k - $bias;
                    }
                    if ($q < $t) {
                        break;
                    }
                    $tmp_int = $t + ($q - $t) % (36 - $t);
                    $output[] = chr(($tmp_int + 22 + 75 * ($tmp_int < 26)));
                    $q = ($q - $t) / (36 - $t);
                }

                $output[] = chr(($q + 22 + 75 * ($q < 26)));
                /* http://tools.ietf.org/html/rfc3492#section-6.1 */
                $delta = ($h == $b) ? $delta / 700 : $delta >> 1;

                $delta += intval($delta / ($h + 1));

                $k2 = 0;
                while ($delta > 455) {
                    $delta /= 35;
                    $k2 += 36;
                }
                $bias = intval($k2 + 36 * $delta / ($delta + 38));
                /* end section-6.1 */
                $delta = 0;
                ++$h;
            }
        }
        ++$delta;
        ++$n;
    }
    return implode('', $output);
}

/**
 * Decode IDN Punycode to UTF-8 domain name
 *
 * @param string $value Punycode
 * @return string Domain name in UTF-8 charset
 *
 * @author Igor V Belousov <igor@belousovv.ru>
 * @copyright 2013, 2015 Igor V Belousov
 * @license http://opensource.org/licenses/LGPL-2.1 LGPL v2.1
 * @link http://belousovv.ru/myscript/phpIDN
 */
function mso_DecodePunycodeIDN($value)
{
    if (function_exists('idn_to_utf8')) {
        return idn_to_utf8($value);
    }

    /* search subdomains */
    $sub_domain = explode('.', $value);
    if (count($sub_domain) > 1) {
        $sub_result = '';
        foreach ($sub_domain as $sub_value) {
            $sub_result .= '.' . mso_DecodePunycodeIDN($sub_value);
        }
        return substr($sub_result, 1);
    }

    /* search prefix */
    if (substr($value, 0, 4) != 'xn--') {
        return $value;
    } else {
        $bad_input = $value;
        $value = substr($value, 4);
    }

    $n      = 0x80;
    $i      = 0;
    $bias   = 72;
    $output = [];

    /* search delimeter */
    $d = strrpos($value, '-');

    if ($d > 0) {
        for ($j = 0; $j < $d; ++$j) {
            $c = $value[$j];
            $output[] = $c;
            if ($c > 0x7F) {
                return $bad_input;
            }
        }
        ++$d;
    } else {
        $d = 0;
    }

    while ($d < strlen($value)) {
        $old_i = $i;
        $w = 1;

        for ($k = 36;; $k += 36) {
            if ($d == strlen($value)) {
                return $bad_input;
            }
            $c = $value[$d++];
            $c = ord($c);

            $digit = ($c - 48 < 10) ? $c - 22 : (
                ($c - 65 < 26) ? $c - 65 : (
                    ($c - 97 < 26) ? $c - 97 : 36));
            if ($digit > (0x10FFFF - $i) / $w) {
                return $bad_input;
            }
            $i += $digit * $w;

            if ($k <= $bias) {
                $t = 1;
            } elseif ($k >= $bias + 26) {
                $t = 26;
            } else {
                $t = $k - $bias;
            }
            if ($digit < $t) {
                break;
            }

            $w *= 36 - $t;
        }

        $delta = $i - $old_i;

        /* http://tools.ietf.org/html/rfc3492#section-6.1 */
        $delta = ($old_i == 0) ? $delta / 700 : $delta >> 1;

        $count_output_plus_one = count($output) + 1;
        $delta += intval($delta / $count_output_plus_one);

        $k2 = 0;
        while ($delta > 455) {
            $delta /= 35;
            $k2 += 36;
        }
        $bias = intval($k2 + 36  * $delta / ($delta + 38));
        /* end section-6.1 */
        if ($i / $count_output_plus_one > 0x10FFFF - $n) {
            return $bad_input;
        }
        $n += intval($i / $count_output_plus_one);
        $i %= $count_output_plus_one;
        array_splice(
            $output,
            $i,
            0,
            html_entity_decode('&#' . $n . ';', ENT_NOQUOTES, 'UTF-8')
        );
        ++$i;
    }

    return implode('', $output);
}

# end of file
