<?php

namespace app\common\library;

use fast\Random;
use think\facade\Log;

class Gateqrcode
{

    const USERKEY1 = '00543210';
    const USERKEY2 = '00543210';
    const OPENSECONDS = '0'; //开门时长（秒）；0表示默认时长

    /**
     * 生成开门二维码
     *
     * @param string $date 日期
     * @param int $times 可开门次数
     * @param array $devices 设备mac地址
     * @param string $extra 自定义额外信息
     * @return void
     */
    public static function generate_qrcode($date, $times, $devices = [], $extra = 'custom')
    {
        if ($times > 255) {
            Log::write('开门次数不能大于255！');
            return false;
        }
        $device_count = count($devices);

        $part1 = $extra . '|';
        $part2 = $card_no = Random::numeric(8);
        $part3 = '';
        $part4 = self::USERKEY2;
        $open_limit = '01'; //00表示无时间限制；01表示有开始时间和结束时间
        $open_type = $device_count > 0 ? '02' : '00'; //00直接开门；01以项目编号+楼栋编号参数匹配来开门；02以设备MAC地址后六位匹配来开门
        $open_seconds = self::str_hex_format(self::OPENSECONDS, 4);
        $hex_times = self::str_hex_format($times, 2);
        $part5 = $open_limit . $hex_times . $open_type . $open_seconds . '000000';
        $part6 = substr(date('YmdHis', strtotime($date) - 30 * 60), 2) . substr(date('YmdHis', strtotime($date . " +1 day") + 30 * 60), 2);
        $device_str = '';
        foreach ($devices as $device) {
            $device_str .= substr($device, -6);
        }
        $part7 = str_repeat('0', 2 - strlen(hexdec($device_count))) . hexdec($device_count) . $device_str;

        $encryptStr = $part5 . $part6 . $part7;
        var_dump($encryptStr);
        $encryptStrLen = strlen($encryptStr); //加密的字符串长度
        $encryptLen = (int)($encryptStrLen / 8); //加密长度
        $remaider = (int)(($encryptStrLen % 8) / 2);  //加密余数
        if ($remaider != 0) {
            $encryptLen++; //加密余数不为0，加密长度+1
        }
        $hexEncryptLen = self::str_hex_format($encryptLen, 2);
        $hexRemaider = self::str_hex_format($remaider, 2);
        $part3 = $hexEncryptLen . $hexRemaider;
        $encryptArr = [];
        for ($i = 0; $i < $encryptLen; $i++) {
            if($remaider && $i==$encryptLen-1){
                $str = substr($encryptStr, $i * 8, $remaider*2);
            }else{
                $str = substr($encryptStr, $i * 8, 8);
            }
            $encryptArr[$i] = self::myintval32(hexdec($str));
        }
        $userkey1 = self::myintval32(hexdec(self::USERKEY1));
        $userkey2 = self::myintval32(hexdec(self::USERKEY2));
        $key = [$userkey2, $userkey1, $userkey2, $userkey1];
        $encryptRes = self::encrypt($encryptArr, $key, $encryptLen);
        // var_dump($encryptArr);
        $originArr = self::decrypt($encryptRes, $key, $encryptLen);
        // var_dump($originArr);
        $originStr = '';
        for ($i = 0; $i < $encryptLen-1; $i++) {
            $originStr .= self::str_hex_format($originArr[$i]);
        }
        // var_dump($remaider);
        if ($remaider == 1) {
            $originStr .= substr(self::str_hex_format($originArr[$encryptLen-1]),-$remaider*2);
        } elseif ($remaider == 2) {
            $originStr .= substr(self::str_hex_format($originArr[$encryptLen-1]),-$remaider*2);
        } elseif ($remaider == 3) {
            $originStr .= substr(self::str_hex_format($originArr[$encryptLen-1]),-$remaider*2);
        } else {
            $originStr .= self::str_hex_format($originArr[$encryptLen-1]);
        }
        var_dump($originStr);
        $encryptResStr = '';
        for ($i = 0; $i < $encryptLen; $i++) {
            $encryptResStr .= self::str_hex_format($encryptRes[$i]);
            // var_dump($encryptResStr);exit;
        }
        return $part1 . $part2 . $part3 . $part4 . $encryptResStr;
    }

    public static function encrypt(array $v, array $k, $n)
    {
        $y = 0;
        $p = 0;
        $rounds = intval(6 + 52 / $n);
        $sum = 0;
        $z = $v[$n - 1];
        $delta = 0x9E3779B9;
        do {
            $sum = $sum + $delta;
            $e = ($sum >> 2 & 0x07ffffff) & 3;
            for ($p = 0; $p < $n - 1; $p++) {
                $y = $v[$p + 1];
                $z = $v[$p] = self::myintval32($v[$p] + self::mx($sum, $y, $z, $p, $e, $k));
                // var_dump($z);
            }
            $y = $v[0];
            // var_dump($y);
            $z = $v[$n - 1] = self::myintval32($v[$n - 1] + self::mx($sum, $y, $z, $p, $e, $k));
            $rounds--;
        } while ($rounds > 0);
        return $v;
    }

    public static function decrypt(array $v, array $k, int $n)
    {
        $sum = $e = $p = 0;
        $delta = 0x9E3779B9;
        $rounds = intval(6 + 52 / $n);
        $sum = $rounds * $delta;
        $z = $v[$n - 1];
        $y = $v[0];
        do {
            $e = ($sum >> 2 & 0x07ffffff) & 3;
            for ($p = $n - 1; $p > 0; $p--) {
                $z = $v[$p - 1];
                $y = $v[$p] = self::myintval32($v[$p] - self::mx($sum, $y, $z, $p, $e, $k));
            }
            $z = $v[$n - 1];
            $y = $v[0] = self::myintval32($v[0] - self::mx($sum, $y, $z, $p, $e, $k));
            $sum = $sum - $delta;
        } while ($sum != 0);
        return $v;
    }

    private static function str_hex_format($str, $size = 8, $isPrefix = true)
    {
        $hex = strtoupper(dechex($str));
        if ($isPrefix) {
            return str_pad($hex, $size, '0', STR_PAD_LEFT);
        } else {
            return str_pad($hex, $size, '0');
        }
    }

    private static function mx($sum, $y, $z, $p, $e, $k)
    {
        return ((($z >> 5 & 0x07ffffff) ^ $y << 2) + (($y >> 3 & 0x1fffffff) ^ $z << 4)) ^ (($sum ^ $y) + ($k[$p & 3 ^ $e] ^ $z));
    }

    private static function myintval32($value)
    {
        $value = ($value & 0xffffffff);
        if ($value & 0x80000000) {
            $value = - ((~$value & 0xffffffff) + 1);
        }
        return $value;
    }

    private static function myintval32_2($value)
    {
        if ($value < -2147483648) {
            return - (- ($value) & 0xffffffff);
        } elseif ($value > 2147483647) {
            return ($value & 0xffffffff);
        }
        return $value;
    }

}
