<?php
/**
 * Base32 encoding/decoding for TOTP secrets.
 *
 * @package ZA_Creative_Login_Shield
 */

defined( 'ABSPATH' ) || exit;

/**
 * Base32 encoder/decoder used for Google Authenticator TOTP secret keys.
 */
class ZACLS_Base32 {

	/**
	 * Base32 character set.
	 *
	 * @var string
	 */
	private static $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';

	/**
	 * Decode a Base32-encoded string.
	 *
	 * @param string $data The encoded data.
	 * @return string
	 */
	public static function decode( $data ) {
		if ( empty( $data ) ) {
			return '';
		}

		$data      = strtoupper( $data );
		$data      = str_replace( '=', '', $data );
		$buffer    = 0;
		$bits_left = 0;
		$output    = '';
		$len       = strlen( $data );

		for ( $i = 0; $i < $len; $i++ ) {
			$char_pos = strpos( self::$chars, $data[ $i ] );
			if ( false === $char_pos ) {
				continue;
			}
			$buffer     = ( $buffer << 5 ) | $char_pos;
			$bits_left += 5;
			if ( $bits_left >= 8 ) {
				$bits_left -= 8;
				$output    .= chr( ( $buffer >> $bits_left ) & 0xFF );
			}
		}

		return $output;
	}

	/**
	 * Encode data to Base32.
	 *
	 * @param string $data The raw data.
	 * @return string
	 */
	public static function encode( $data ) {
		if ( empty( $data ) ) {
			return '';
		}

		$buffer    = 0;
		$bits_left = 0;
		$output    = '';
		$len       = strlen( $data );

		for ( $i = 0; $i < $len; $i++ ) {
			$buffer     = ( $buffer << 8 ) | ord( $data[ $i ] );
			$bits_left += 8;
			while ( $bits_left >= 5 ) {
				$bits_left -= 5;
				$output    .= self::$chars[ ( $buffer >> $bits_left ) & 0x1F ];
			}
		}

		if ( $bits_left > 0 ) {
			$output .= self::$chars[ ( $buffer << ( 5 - $bits_left ) ) & 0x1F ];
		}

		return $output;
	}
}
