<?php

namespace App\Helpers;

class LZStringHelper
{
    /**
     * Decompresses a string compressed with LZ-String's compressToEncodedURIComponent method.
     * This is a PHP port of the JavaScript LZ-String library.
     *
     * @param  string  $compressed
     * @return string|null
     */
    public static function decompressFromEncodedURIComponent($compressed)
    {
        if ($compressed === null || $compressed === '') {
            return '';
        }

        // Replace URL-safe characters back
        $compressed = str_replace(' ', '+', $compressed);

        return self::decompress(strlen($compressed), 32, function ($index) use ($compressed) {
            return self::getBaseValue(self::$keyStrUriSafe, $compressed[$index]);
        });
    }

    private static $keyStrUriSafe = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$';

    private static function getBaseValue($alphabet, $character)
    {
        $pos = strpos($alphabet, $character);

        return $pos !== false ? $pos : -1;
    }

    private static function decompress($length, $resetValue, $getNextValue)
    {
        $dictionary = [];
        $enlargeIn = 4;
        $dictSize = 4;
        $numBits = 3;
        $entry = '';
        $result = [];
        $data = ['val' => $getNextValue(0), 'position' => $resetValue, 'index' => 1];

        for ($i = 0; $i < 3; $i++) {
            $dictionary[$i] = chr($i);
        }

        $bits = 0;
        $maxpower = pow(2, 2);
        $power = 1;

        while ($power != $maxpower) {
            $resb = $data['val'] & $data['position'];
            $data['position'] >>= 1;

            if ($data['position'] == 0) {
                $data['position'] = $resetValue;
                $data['val'] = $getNextValue($data['index']++);
            }

            $bits |= ($resb > 0 ? 1 : 0) * $power;
            $power <<= 1;
        }

        $next = $bits;

        switch ($next) {
            case 0:
                $bits = 0;
                $maxpower = pow(2, 8);
                $power = 1;

                while ($power != $maxpower) {
                    $resb = $data['val'] & $data['position'];
                    $data['position'] >>= 1;

                    if ($data['position'] == 0) {
                        $data['position'] = $resetValue;
                        $data['val'] = $getNextValue($data['index']++);
                    }

                    $bits |= ($resb > 0 ? 1 : 0) * $power;
                    $power <<= 1;
                }

                $c = chr($bits);
                break;

            case 1:
                $bits = 0;
                $maxpower = pow(2, 16);
                $power = 1;

                while ($power != $maxpower) {
                    $resb = $data['val'] & $data['position'];
                    $data['position'] >>= 1;

                    if ($data['position'] == 0) {
                        $data['position'] = $resetValue;
                        $data['val'] = $getNextValue($data['index']++);
                    }

                    $bits |= ($resb > 0 ? 1 : 0) * $power;
                    $power <<= 1;
                }

                $c = chr($bits);
                break;

            case 2:
                return '';
        }

        $dictionary[$dictSize++] = $c;
        $w = $c;
        $result[] = $c;

        while (true) {
            if ($data['index'] > $length) {
                return '';
            }

            $bits = 0;
            $maxpower = pow(2, $numBits);
            $power = 1;

            while ($power != $maxpower) {
                $resb = $data['val'] & $data['position'];
                $data['position'] >>= 1;

                if ($data['position'] == 0) {
                    $data['position'] = $resetValue;
                    $data['val'] = $getNextValue($data['index']++);
                }

                $bits |= ($resb > 0 ? 1 : 0) * $power;
                $power <<= 1;
            }

            $c = $bits;

            switch ($c) {
                case 0:
                    $bits = 0;
                    $maxpower = pow(2, 8);
                    $power = 1;

                    while ($power != $maxpower) {
                        $resb = $data['val'] & $data['position'];
                        $data['position'] >>= 1;

                        if ($data['position'] == 0) {
                            $data['position'] = $resetValue;
                            $data['val'] = $getNextValue($data['index']++);
                        }

                        $bits |= ($resb > 0 ? 1 : 0) * $power;
                        $power <<= 1;
                    }

                    $dictionary[$dictSize++] = chr($bits);
                    $c = $dictSize - 1;
                    $enlargeIn--;
                    break;

                case 1:
                    $bits = 0;
                    $maxpower = pow(2, 16);
                    $power = 1;

                    while ($power != $maxpower) {
                        $resb = $data['val'] & $data['position'];
                        $data['position'] >>= 1;

                        if ($data['position'] == 0) {
                            $data['position'] = $resetValue;
                            $data['val'] = $getNextValue($data['index']++);
                        }

                        $bits |= ($resb > 0 ? 1 : 0) * $power;
                        $power <<= 1;
                    }

                    $dictionary[$dictSize++] = chr($bits);
                    $c = $dictSize - 1;
                    $enlargeIn--;
                    break;

                case 2:
                    return implode('', $result);
            }

            if ($enlargeIn == 0) {
                $enlargeIn = pow(2, $numBits);
                $numBits++;
            }

            if (isset($dictionary[$c])) {
                $entry = $dictionary[$c];
            } else {
                if ($c === $dictSize) {
                    $entry = $w.$w[0];
                } else {
                    return null;
                }
            }

            $result[] = $entry;

            $dictionary[$dictSize++] = $w.$entry[0];
            $enlargeIn--;

            $w = $entry;

            if ($enlargeIn == 0) {
                $enlargeIn = pow(2, $numBits);
                $numBits++;
            }
        }
    }
}
