<?php
// phpMQTT.php - Modifikasi Khusus HiveMQ Cloud & Hosting Murah
namespace Bluerhinos;

class phpMQTT {
    private $socket;
    private $msgid = 1;
    private $keepalive = 10;
    private $timesince_ping = 0;
    private $server;
    private $port;
    private $clientid;
    private $username;
    private $password;

    public function __construct($server, $port, $clientid) {
        $this->server = $server;
        $this->port = $port;
        $this->clientid = $clientid;
    }

    public function connect($clean = true, $will = NULL, $username = NULL, $password = NULL) {
        $this->username = $username;
        $this->password = $password;
        
        // MENYUSUN KONTEKS SSL + SNI + ALPN (WAJIB UNTUK HIVEMQ CLOUD SERVERLESS)
        $context = stream_context_create([
            'ssl' => [
                'verify_peer' => false,
                'verify_peer_name' => false,
                'allow_self_signed' => true,
                'SNI_enabled' => true,
                'peer_name' => str_replace('tls://', '', $this->server),
                'alpn_protocols' => 'mqtt' // <--- SUNTIKAN UTAMA: Memberitahu HiveMQ bahwa kita menggunakan protokol MQTT
            ]
        ]);


        $this->socket = stream_socket_client($this->server . ':' . $this->port, $errno, $errstr, 5, STREAM_CLIENT_CONNECT, $context);

        if (!$this->socket) {
            return false;
        }

        stream_set_timeout($this->socket, 5);
        
        // Prosedur Jabat Tangan Protokol MQTT murni
        $i = 0;
        $buffer = "";
        $buffer .= chr(0x00).chr(0x04)."MQTT";
        $buffer .= chr(0x04);
        
        $connect_flags = 0;
        if($clean) $connect_flags |= 0x02;
        if($username != NULL) $connect_flags |= 0x80;
        if($password != NULL) $connect_flags |= 0x40;
        
        $buffer .= chr($connect_flags);
        $buffer .= chr($this->keepalive >> 8).chr($this->keepalive & 0xff);
        $buffer .= $this->strlength($this->clientid);
        
        if($username != NULL) $buffer .= $this->strlength($username);
        if($password != NULL) $buffer .= $this->strlength($password);
        
        $this->send_packet(chr(0x10), $buffer);
        
        $header = $this->read_bytes(4);
        if(strlen($header) == 4 && ord($header[0]) == 0x20 && ord($header[3]) == 0x00) {
            return true;
        }
        
        return false;
    }

    public function publish($topic, $content, $qos = 0, $retain = 0) {
        $buffer = $this->strlength($topic);
        if($qos > 0) {
            $this->msgid++;
            $buffer .= chr($this->msgid >> 8).chr($this->msgid & 0xff);
        }
        $buffer .= $content;
        $cmd = chr(0x30 | ($qos << 1) | $retain);
        $this->send_packet($cmd, $buffer);
    }

    public function close() {
        if($this->socket) {
            $this->send_packet(chr(0xe0), "");
            fclose($this->socket);
        }
    }

    private function send_packet($cmd, $buffer) {
        $len = strlen($buffer);
        $string = $cmd;
        do {
            $digit = $len % 128;
            $len = (int)($len / 128);
            if ($len > 0) $digit = $digit | 0x80;
            $string .= chr($digit);
        } while ($len > 0);
        $string .= $buffer;
        fwrite($this->socket, $string, strlen($string));
    }

    private function read_bytes($len) {
        $string = "";
        while(strlen($string) < $len && !feof($this->socket)) {
            $string .= fread($this->socket, $len - strlen($string));
        }
        return $string;
    }

    private function strlength($str) {
        $len = strlen($str);
        return chr($len >> 8).chr($len & 0xff).$str;
    }
}
