<?php
namespace Models;

class mertechscales {
	public $f3, $sets, $config;
	private $ip;
    private $port;
    private $password;
    private $timeout;
	public $errorCodes = [
        0 => "Ошибок нет",
        17 => "Ошибка в значении тары",
        110 => "Неверное количество строк у параметра „Название магазина“",
        120 => "Неизвестная команда",
        122 => "Неверный пароль",
        124 => "Неверное значение параметра",
        128 => "Неверный номер ПЛУ",
        129 => "Папка не существует",
        130 => "Неверный код товара",
        136 => "Неверный номер изображения",
        140 => "Пустое ПЛУ",
        161 => "Размер изображения превышает допустимый предел",
        171 => "Запрашиваемый файл пуст",
        172 => "В процессе выполнения",
        173 => "Камера распознавания выключена",
        174 => "Сбой работы с бэкапом",
        175 => "Не было запроса на создание файла бэкапа",
        176 => "Не хватает места на диске для создания бэкапа",
        177 => "Несоответствие версий БД",
        178 => "Несоответствие версий Service",
        179 => "Несоответствие прав доступа",
        180 => "Несоответствие версий камеры распознавания",
        181 => "Бэкап не содержит файл конфигурации",
        182 => "Бэкап не содержит папку MertechScale",
        183 => "Бэкап не содержит данные о товарах",
        184 => "Бэкап не содержит данные об этикетках",
        185 => "Бэкап не содержит файла камеры распознавания",
        186 => "Бэкап не содержит папки сценариев",
        187 => "Фасовщик с таким номером не найден",
        188 => "Устройство не проинициализировано",
        189 => "Старая версия прошивки без возможности сделать резервную копию",
        190 => "Камера распознавания не содержит данных для создания резервной копии",
        191 => "Отсутствует один из обязательных элементов этикетки",
        192 => "Сервис распознавания отключен",
        193 => "Процесс распознавания ещё не начинался",
        194 => "Процесс распознавания ещё не закончился",
        195 => "Нет данных об этикетке",
        196 => "Категория не найдена",
        197 => "Категория для переназначения не найдена",
        198 => "Штрихкод не поддерживается",
        199 => "Предыдущий запрос ещё не завершён",
        200 => "Нельзя зашифровать текущий сценарий",
        201 => "Ошибка шифрования сценария",
        202 => "Сценарий уже зашифрован",
        203 => "Нехватает свободного места на диске",
        204 => "Задача поставлена в очередь",
        205 => "Резервная копия не содержит статистики распознавания",
        206 => "Для печати необходим ненулевой вес",
        -1 => "Нет подключения",
        -2 => "Выполнение предыдущей команды ещё не закончено",
        -3 => "Данные ещё не готовы для получения",
        -4 => "Обработка файла на весах завершилась без ошибок",
        -5 => "Обработка файла на весах завершилась с ошибкой",
        -6 => "Неизвестный этап загрузки файла",
        -7 => "Не задан обязательный параметр",
        -8 => "Параметр задан не того типа",
        -9 => "Выполнение команды остановлено",
        -10 => "Невозможно прочитать ответ",
        -101 => "Неверная длина ответа",
        -102 => "Ответ не содержит STX",
        -103 => "Длина ответа не соответствует заявленной",
        -104 => "Неверное значение параметра",
        -105 => "Хэш-данные не совпадают",
        -106 => "Неверное значение использования группового кода",
        -107 => "На весах более новая версия протокола",
        -108 => "На весах устаревшая версия протокола",
        -109 => "IP адрес уже добавлен в список",
        -110 => "Функционал не для группового режима",
        -111 => "Ошибка выполнения группового запроса",
        -112 => "Файл получен",
        -113 => "Неверный файл",
        -114 => "Идёт процесс распознавания",
        -115 => "Локальный файл отсутствует",
        -116 => "Неверная длина ответа на стадии 9",
        -117 => "Неверная струтура файла JSON",
        -118 => "Неверная струтура команды",
        -119 => "Неверный ответ для двухбайтовой команды",
        -120 => "Неверная длина ответа для информации о весах",
        -121 => "Неверный код команды в ответе",
        -122 => "Ошибка разбора JSON данных",
        -123 => "Неверная структура ответа",
        -124 => "Ошибка чтения ошибки",
        -125 => "Недопустимый тип экспорта. Должен быть 0 (с очисткой) или 1 (без очистки)",
        -1000 => "Неизвестная ошибка"
    ];
	
	function __construct($f3, $sets, $config) {
		$this->f3=$f3;
		$this->sets=$sets;
		$this->config=$config;
		foreach ($config as $key => $value) {
			$this->$key=$value;
		}
	}

    /**
     * Очистка базы товаров
     */
    public function clearProducts() {
        return $this->sendCommand(0xB9);
    }

    /**
     * Очистка базы сообщений
     */
    public function clearMessages() {
        return $this->sendCommand(0xBA);
    }

    /**
     * Очистка базы кодов маркировки
     * @param int $productCode Код товара (0 - все товары)
     */
    public function clearMarkingCodes($productCode = 0) {
        // Преобразуем код товара в 4-байтное представление
        $productCodeBytes = pack('V', $productCode);
        return $this->sendCommand([0xFF, 0x11], $productCodeBytes);
    }

    /**
     * Получение JSON-данных с информацией о весах
     * @return array Массив с данными весов
     */
    public function getScaleInfo() {
        $response = $this->sendCommand([0xFF, 0x17], '', true);
        if($response==-1) return $response;
        return $this->parseScaleInfoResponse($response);
    }

    /**
     * Общая функция отправки команд
     */
    private function sendCommand($commandCode, $additionalData = '', $expectJsonResponse = false) {
        $socket = @fsockopen($this->ip, $this->port, $errno, $errstr, $this->timeout);
        
        if (!$socket) {
            return -1;
            //throw new \Exception("Connection failed: $errstr");
        }

       // Формируем тело команды
        $commandBody = '';
        
        // Байты команды
        if (is_array($commandCode)) {
            $commandBytes = array_map('chr', $commandCode);
            //print_r($commandBytes); echo "<br>";
            $commandBody .= implode('', $commandBytes);
            //print_r(bin2hex($commandBody)); echo "<br>";
        } else {
            $commandBody .= chr($commandCode);
        }
        
        // Пароль (4 байта little-endian) - только для команд очистки
        if ($commandCode !== 0xFF && $commandCode !== [0xFF, 0x17]) {
            //$commandBody .= pack('V', $this->password);
            $commandBody .= $this->password;
        }
        
        // Дополнительные данные
        $commandBody .= $additionalData;

        // Вычисляем длину оставшейся части команды
        $dataLength = strlen($commandBody);
        $lengthByte = ($dataLength > 255) ? chr(0xFF) : chr($dataLength);
        
        // Вставляем байт длины после STX
        $finalCommand = chr(0x02) . $lengthByte . $commandBody;

        // Добавляем 4 байта длины в начале (LittleEndian)
        $prefix = pack('V', strlen($finalCommand));
        $fullCommand = $prefix . $finalCommand;

        // Отправляем команду
        fwrite($socket, $fullCommand);
        
        //print_r(bin2hex($fullCommand)); echo "<br>";

        // Читаем ответ
        $response = fread($socket, 4096); // Увеличиваем буфер для JSON-ответов
        fclose($socket);
        if ($response === false) {
            return -10;
            //throw new \Exception("Failed to read response");
        }

        // Для JSON-ответов возвращаем сырые данные
        if ($expectJsonResponse) {
            return $response;
        }

        // Парсим стандартный ответ
        return $this->parseResponse($response, $commandCode);
    }

    /**
     * Парсинг стандартного ответа от весов
     */
    private function parseResponse($response, $sentCommandCode) {
        // Пропускаем первые 4 байта длины
        $responseData = substr($response, 4);
        
        if (strlen($responseData) < 4) {
            return -101;
            //throw new \Exception("Invalid response length");
        }

        $stx = ord($responseData[0]);
        $length = ord($responseData[1]);
        
        // Проверяем STX
        if ($stx !== 0x02) {
            return -102;
            //throw new \Exception("Invalid STX in response");
        }

        // Извлекаем код команды из ответа
        $responseCommandCode = ord($responseData[2]);
        
        // Для двухбайтных команд проверяем второй байт
        if (is_array($sentCommandCode) && $sentCommandCode[0] === 0xFF) {
            if (strlen($responseData) < 5) {
                return -119;
                //throw new \Exception("Invalid response for two-byte command");
            }
            $responseCommandCode = [ord($responseData[2]), ord($responseData[3])];
        }

        // Код результата (последний байт)
        $resultCode = ord($responseData[strlen($responseData) - 1]);

        return $resultCode;
    }

    /**
     * Парсинг ответа с JSON-данными о весах
     */
    private function parseScaleInfoResponse($response) {
        // Пропускаем первые 4 байта длины
        //print_r(bin2hex($response)); echo "<br>";
        $responseData = substr($response, 4);
        //print_r(bin2hex($responseData)); echo "<br>";
        
        if (strlen($responseData) < 7) {
            return -120;
            //throw new \Exception("Invalid response length for scale info");
        }

        $stx = ord($responseData[0]);
        $length = ord($responseData[1]);
        
        // Проверяем STX
        if ($stx !== 0x02) {
            return -102;
            //throw new \Exception("Invalid STX in response");
        }

        // Проверяем код команды (0xFF, 0x17)
        if (ord($responseData[2]) !== 0xFF || ord($responseData[3]) !== 0x17) {
            return -121;
            //throw new \Exception("Invalid command code in response");
        }

        // Код результата
        $resultCode = ord($responseData[4]);
        
        // Длина JSON-строки (2 байта, LittleEndian)
        $jsonLength = ord($responseData[5]) | (ord($responseData[6]) << 8);
        
        // Извлекаем JSON-строку
        if (strlen($responseData) < 7 + $jsonLength) {
            return -117;
            //throw new \Exception("JSON data truncated");
        }

        $jsonString = substr($responseData, 7, $jsonLength);
        
        // Декодируем JSON
        $scaleData = json_decode($jsonString, true);
        
        if (json_last_error() !== JSON_ERROR_NONE) {
            return -122;
            //throw new \Exception("JSON decode error: " . json_last_error_msg());
        }

        /*return [
            'result_code' => $resultCode,
            'data' => $scaleData
        ];*/
        return $scaleData;
    }
    
    /**
     * Загрузка обычного файла на весы
     * @param string $filePath Путь для сохранения файла на устройстве
     * @param string $localFilePath Локальный путь к файлу для загрузки
     * @return array|int Результат операции
     */
    public function uploadFile($filePath, $localFilePath) {
        if (!file_exists($localFilePath)) {
            return -115;
            //throw new \Exception("Local file not found: $localFilePath");
        }

        $fileContent = file_get_contents($localFilePath);
        $fileSize = strlen($fileContent);
        $fileHash = md5($fileContent, true); // raw binary hash

        // Этап 1: Отправка пути для сохранения файла
        $result1 = $this->sendFileStage1($filePath);
        if ($result1 !== 0) {
            return $result1;
        }

        // Этап 2: Отправка хэш-данных и параметров
        $result2 = $this->sendFileStage2($fileHash, $fileSize);
        if ($result2 !== 0) {
            return $result2;
        }

        // Этап 3: Отправка порций файла
        $result3 = $this->sendFileStage3($fileContent);
        if ($result3 !== 0) {
            return $result3;
        }

        // Этап 4: Проверка обработки файла
        return $this->sendFileStage9();
    }

    /**
     * Этап 1: Отправка пути для сохранения файла
     */
    private function sendFileStage1($filePath) {
        $filePathUtf8 = $filePath; // Предполагаем, что путь уже в UTF-8
        $pathLength = strlen($filePathUtf8);
        
        // Формируем данные для этапа 1
        $stageData = chr(0x01); // Код этапа 0x01
        $stageData .= pack('v', $pathLength); // 2 байта длины пути (little-endian)
        $stageData .= $filePathUtf8; // Текст пути

        return $this->sendFileCommand($stageData);
    }

    /**
     * Этап 2: Отправка хэш-данных и параметров
     */
    private function sendFileStage2($fileHash, $fileSize) {
        // Формируем данные для этапа 2
        $stageData = chr(0x02); // Код этапа 0x02
        $stageData .= $fileHash; // 16 байт хэш-данных
        $stageData .= chr(0x04); // Параметр: Размер файла
        $stageData .= pack('P', $fileSize); // 8 байт размера файла (little-endian)

        return $this->sendFileCommand($stageData);
    }

    /**
     * Этап 3: Отправка порций файла
     */
    private function sendFileStage3($fileContent) {
        $chunkSize = 60000; // Максимальный размер порции
        $fileSize = strlen($fileContent);
        $offset = 0;

        while ($offset < $fileSize) {
            $remaining = $fileSize - $offset;
            $currentChunkSize = min($chunkSize, $remaining);
            $chunkData = substr($fileContent, $offset, $currentChunkSize);
            $isLast = ($offset + $currentChunkSize >= $fileSize) ? 1 : 0;

            // Формируем данные для этапа 3
            $stageData = chr(0x03); // Код этапа 0x03
            $stageData .= chr($isLast); // Флаг последней порции
            $stageData .= pack('V', $offset); // Смещение в файле (4 байта little-endian)
            $stageData .= pack('v', $currentChunkSize); // Размер порции (2 байта little-endian)
            $stageData .= $chunkData; // Данные порции

            $result = $this->sendFileCommand($stageData);
            if ($result !== 0) {
                return $result;
            }

            $offset += $currentChunkSize;
        }

        return 0;
    }

    /**
     * Этап 9: Проверка отправляемого файла
     */
    private function sendFileStage9() {
        $stageData = chr(0x09); // Код этапа 0x09
        return $this->sendFileCommand($stageData, true);
    }

    /**
     * Отправка команды для загрузки файла
     */
    private function sendFileCommand($stageData, $isStage9 = false) {
        $socket = @fsockopen($this->ip, $this->port, $errno, $errstr, $this->timeout);

        if (!$socket) {
            return -1;
            //throw new \Exception("Connection failed: $errstr");
        }

        // Формируем тело команды
        $commandBody = chr(0xA9); // Код команды загрузки файла
        //$commandBody .= pack('V', $this->password); // Пароль (4 байта little-endian)
        $commandBody .= $this->password;
        $commandBody .= $stageData; // Данные этапа

        // Вычисляем длину оставшейся части команды
        $dataLength = strlen($commandBody);
        $lengthByte = ($dataLength > 255) ? chr(0xFF) : chr($dataLength);
        
        // Формируем полную команду
        $finalCommand = chr(0x02) . $lengthByte . $commandBody;

        // Добавляем 4 байта длины в начале (LittleEndian)
        $prefix = pack('V', strlen($finalCommand));
        $fullCommand = $prefix . $finalCommand;

        // Отправляем команду
        fwrite($socket, $fullCommand);
        
        // Читаем ответ
        $response = fread($socket, 4096);
        fclose($socket);
        if ($response === false) {
            return -10;
            throw new \Exception("Failed to read response");
        }

        // Парсим ответ
        return $this->parseFileResponse($response, $isStage9);
    }

    /**
     * Парсинг ответа на команду загрузки файла
     */
    private function parseFileResponse($response, $isStage9 = false) {
        // Пропускаем первые 4 байта длины
        $responseData = substr($response, 4);
        
        if (strlen($responseData) < 4) {
            return -101;
            //throw new \Exception("Invalid response length");
        }

        $stx = ord($responseData[0]);
        $length = ord($responseData[1]);
        $commandCode = ord($responseData[2]);
        
        // Проверяем STX и код команды
        if ($stx !== 0x02 || $commandCode !== 0xA9) {
            return -123;
            //throw new \Exception("Invalid response structure");
        }

        // Код результата
        $resultCode = ord($responseData[3]);

        // Для этапа 9 дополнительно парсим статус обработки
        if ($isStage9 && $resultCode === 0) {
            if (strlen($responseData) < 5) {
                return -116;
                //throw new \Exception("Invalid response length for stage 9");
            }

            $processingStatus = ord($responseData[4]);
            $errorMessage = '';

            // Если статус обработки = 2 (ошибка), читаем сообщение об ошибке
            if ($processingStatus === 2) {
                if (strlen($responseData) < 7) {
                    return -116;
                    //throw new \Exception("Invalid response length for stage 9 with error");
                }

                // Длина сообщения об ошибке (2 байта little-endian)
                $errorLength = ord($responseData[5]) | (ord($responseData[6]) << 8);
                
                if (strlen($responseData) < 7 + $errorLength) {
                    return -10;
                    //throw new \Exception("Error message truncated");
                }

                $errorMessage = substr($responseData, 7, $errorLength);
            }
            return $resultCode;
            /*return [
                'result_code' => $resultCode,
                'processing_status' => $processingStatus,
                'error_message' => $errorMessage
            ];*/
        }

        return $resultCode;
    }
    
    /**
     * Загрузка JSON-файла товаров на весы
     * @param string $localFilePath Локальный путь к JSON-файлу
     * @param int $exportType Тип экспорта: 0 - с очисткой базы, 1 - без очистки базы
     * @return array|int Результат операции
     */
    public function uploadProductsJson($localFilePath, $exportType = 0) {
        if (!file_exists($localFilePath)) {
            return -115;
            //throw new \Exception("Local file not found: $localFilePath");
        }

        $fileContent = file_get_contents($localFilePath);
        $fileSize = strlen($fileContent);
        $fileHash = md5($fileContent, true); // raw binary hash

        // Проверяем валидность exportType
        if ($exportType !== 0 && $exportType !== 1) {
            return -125;
            //throw new \Exception("Invalid export type. Must be 0 (with cleanup) or 1 (without cleanup)");
        }

        // Этап 2: Отправка хэш-данных и параметров
        $result2 = $this->sendProductsJsonStage2($fileHash, $fileSize, $exportType);
        if ($result2 !== 0) {
            return $result2;
        }

        // Этап 3: Отправка порций файла
        $result3 = $this->sendProductsJsonStage3($fileContent);
        if ($result3 !== 0) {
            return $result3;
        }

        // Этап 9: Проверка обработки файла
        return $this->sendProductsJsonStage9();
    }

    /**
     * Этап 2 для JSON-файла товаров: Отправка хэш-данных и параметров
     */
    private function sendProductsJsonStage2($fileHash, $fileSize, $exportType) {
        // Формируем данные для этапа 2
        $stageData = chr(0x02); // Код этапа 0x02
        $stageData .= $fileHash; // 16 байт хэш-данных
        $stageData .= chr(0x04); // Параметр: Размер файла
        $stageData .= pack('P', $fileSize); // 8 байт размера файла (little-endian)
        $stageData .= chr(0x01); // Параметр: Тип экспорта товаров
        $stageData .= chr($exportType); // Значение: 0 или 1

        return $this->sendProductsJsonCommand($stageData);
    }

    /**
     * Этап 3 для JSON-файла товаров: Отправка порций файла
     */
    private function sendProductsJsonStage3($fileContent) {
        $chunkSize = 60000; // Максимальный размер порции
        $fileSize = strlen($fileContent);
        $offset = 0;

        while ($offset < $fileSize) {
            $remaining = $fileSize - $offset;
            $currentChunkSize = min($chunkSize, $remaining);
            $chunkData = substr($fileContent, $offset, $currentChunkSize);
            $isLast = ($offset + $currentChunkSize >= $fileSize) ? 1 : 0;

            // Формируем данные для этапа 3
            $stageData = chr(0x03); // Код этапа 0x03
            $stageData .= chr($isLast); // Флаг последней порции
            $stageData .= pack('V', $offset); // Смещение в файле (4 байта little-endian)
            $stageData .= pack('v', $currentChunkSize); // Размер порции (2 байта little-endian)
            $stageData .= $chunkData; // Данные порции

            $result = $this->sendProductsJsonCommand($stageData);
            if ($result !== 0) {
                return $result;
            }

            $offset += $currentChunkSize;
        }

        return 0;
    }

    /**
     * Этап 9 для JSON-файла товаров: Проверка отправляемого файла
     */
    private function sendProductsJsonStage9() {
        $stageData = chr(0x09); // Код этапа 0x09
        return $this->sendProductsJsonCommand($stageData, true);
    }

    /**
     * Отправка команды для загрузки JSON-файла товаров
     */
    private function sendProductsJsonCommand($stageData, $isStage9 = false) {
        $socket = @fsockopen($this->ip, $this->port, $errno, $errstr, $this->timeout);
        
        if (!$socket) {
            return -1;
            //throw new \Exception("Connection failed: $errstr");
        }

        // Формируем тело команды
        $commandBody = chr(0xFF) . chr(0x13); // Код команды JSON-файла товаров
        //$commandBody .= pack('V', $this->password); // Пароль (4 байта little-endian)
        $commandBody .= $this->password;
        $commandBody .= $stageData; // Данные этапа

        // Вычисляем длину оставшейся части команды
        $dataLength = strlen($commandBody);
        $lengthByte = ($dataLength > 255) ? chr(0xFF) : chr($dataLength);
        
        // Формируем полную команду
        $finalCommand = chr(0x02) . $lengthByte . $commandBody;

        // Добавляем 4 байта длины в начале (LittleEndian)
        $prefix = pack('V', strlen($finalCommand));
        $fullCommand = $prefix . $finalCommand;

        // Отправляем команду
        fwrite($socket, $fullCommand);
        
        // Читаем ответ
        $response = fread($socket, 4096);
        fclose($socket);
        if ($response === false) {
            return -10;
            //throw new \Exception("Failed to read response");
        }

        // Парсим ответ
        return $this->parseProductsJsonResponse($response, $isStage9);
    }

    /**
     * Парсинг ответа на команду загрузки JSON-файла товаров
     */
    private function parseProductsJsonResponse($response, $isStage9 = false) {
        // Пропускаем первые 4 байта длины
        $responseData = substr($response, 4);
        
        if (strlen($responseData) < 5) {
            return -101;
            //throw new \Exception("Invalid response length");
        }

        $stx = ord($responseData[0]);
        $length = ord($responseData[1]);
        $commandCode1 = ord($responseData[2]);
        $commandCode2 = ord($responseData[3]);
        
        // Проверяем STX и код команды (0xFF, 0x13)
        if ($stx !== 0x02 || $commandCode1 !== 0xFF || $commandCode2 !== 0x13) {
            return -117;
            //throw new \Exception("Invalid response structure for products JSON");
        }

        // Код результата
        $resultCode = ord($responseData[4]);

        // Для этапа 9 дополнительно парсим статус обработки
        if ($isStage9 && $resultCode === 0) {
            if (strlen($responseData) < 6) {
                return -116;
                //throw new \Exception("Invalid response length for stage 9");
            }

            $processingStatus = ord($responseData[5]);
            $errorMessage = '';

            // Если статус обработки = 2 (ошибка), читаем сообщение об ошибке
            if ($processingStatus === 2) {
                if (strlen($responseData) < 8) {
                    return -116;
                    //throw new \Exception("Invalid response length for stage 9 with error");
                }

                // Длина сообщения об ошибке (2 байта little-endian)
                $errorLength = ord($responseData[6]) | (ord($responseData[7]) << 8);
                
                if (strlen($responseData) < 8 + $errorLength) {
                    return -124;
                    //throw new \Exception("Error message truncated");
                }

                $errorMessage = substr($responseData, 8, $errorLength);
            }
            return $resultCode;

            /*return [
                'result_code' => $resultCode,
                'processing_status' => $processingStatus,
                'error_message' => $errorMessage
            ];*/
        }

        return $resultCode;
    }
    
        /**
     * Удаление файла или папки на весах
     * @param string $path Путь к файлу или папке для удаления на весах
     * @return int Код результата операции
     */
    public function deleteFileOrFolder($path) {
        $socket = @fsockopen($this->ip, $this->port, $errno, $errstr, $this->timeout);
        
        if (!$socket) {
            return -1;
            //throw new \Exception("Connection failed: $errstr");
        }

        // Формируем тело команды
        $commandBody = chr(0xFF) . chr(0x35); // Код команды удаления
        $commandBody .= $this->password;
        
        // Длина пути (2 байта little-endian)
        $pathLength = strlen($path);
        $commandBody .= pack('v', $pathLength);
        
        // Сам путь
        $commandBody .= $path;

        // Вычисляем длину оставшейся части команды
        $dataLength = strlen($commandBody);
        $lengthByte = ($dataLength > 255) ? chr(0xFF) : chr($dataLength);
        
        // Формируем полную команду
        $finalCommand = chr(0x02) . $lengthByte . $commandBody;

        // Добавляем 4 байта длины в начале (LittleEndian)
        $prefix = pack('V', strlen($finalCommand));
        $fullCommand = $prefix . $finalCommand;

        // Отправляем команду
        fwrite($socket, $fullCommand);
        
        // Читаем ответ
        $response = fread($socket, 4096);
        fclose($socket);
        if ($response === false) {
            return -10;
            //throw new \Exception("Failed to read response");
        }

        // Парсим ответ
        return $this->parseDeleteResponse($response);
    }

    /**
     * Парсинг ответа на команду удаления файла или папки
     */
    private function parseDeleteResponse($response) {
        // Пропускаем первые 4 байта длины
        $responseData = substr($response, 4);
        
        if (strlen($responseData) < 5) {
            return -101;
            //throw new \Exception("Invalid response length for delete command");
        }

        $stx = ord($responseData[0]);
        $length = ord($responseData[1]);
        $commandCode1 = ord($responseData[2]);
        $commandCode2 = ord($responseData[3]);
        
        // Проверяем STX и код команды (0xFF, 0x35)
        if ($stx !== 0x02 || $commandCode1 !== 0xFF || $commandCode2 !== 0x35) {
            return -118;
            //throw new \Exception("Invalid response structure for delete command");
        }

        // Код результата (5-й байт)
        return ord($responseData[4]);
    }

    // Формируем файл для отправки
    function get_json($products) {
        $categories=array();
        $data=array(
            'categories'=>array(),
            'messages'=>array(),
            'products'=>array(),
            'images'=>array(),
            'time'=>0
        );
        foreach($products as $p) {
            $price=$p['price'];
            if($p['vat_type']>0) {
                $price-=$price/(100+$p['vat'])*$p['vat'];
            }
            if($p['img']!='') {
                $img=json_decode($p['img']);
                if(is_array($img)) {
                    $img=array_shift($img);
                    $file_info=pathinfo($img);
                    $img_file='tmp/'.$img;
                    //$this->f3->write($img_file, $this->f3->read('https://'.$this->config['api_data']['domen'].'/upload/'.$this->config['api_data']['subdomen'].'/250_250/'.$img));
                    $data['images'][]=array(
                        'path'=>$img_file,
                        'url'=>'https://'.$this->config['api_data']['domen'].'/upload/'.$this->config['api_data']['subdomen'].'/250_250/'.$img,
                        'name'=>'Products/'.$p['plu'].'.'.$file_info['extension']
                    );
                }
            }

            if($p['category_img']!='') {
                $file_info=pathinfo($p['category_img']);
                $img_file='tmp/'.$p['category_img'];
                //$this->f3->write($img_file, $this->f3->read('https://'.$this->config['api_data']['domen'].'/upload/'.$this->config['api_data']['subdomen'].'/250_250/'.$p['category_img']));
                $data['images'][]=array(
                    'path'=>$img_file,
                    'url'=>'https://'.$this->config['api_data']['domen'].'/upload/'.$this->config['api_data']['subdomen'].'/250_250/'.$p['category_img'],
                    'name'=>'Categories/mertech_category'.$p['category_id'].'.'.$file_info['extension']
                );
            }

            $price=round($price, 2);
            if(!isset($categories[$p['category_id']])) {
                $categories[$p['category_id']]=array(
                    'idCategory'=>$p['category_id'],
                    'name'=>$p['category_title']??'Без категории'
                );
            }
            
            $p['message'] = null;
            $other_data=json_decode($p['other_data'], true);
            if(is_array($other_data) && isset($other_data['compound']) && $other_data['compound']!='') {
                $message='Состав: '.$other_data['compound']."\n";
                if(isset($other_data['kkal']) && $other_data['kkal']>0) {
                    $message.="Ккал: ".$other_data['kkal'].'; ';
                }
                if(isset($other_data['proteins']) && $other_data['proteins']>0) {
                    $message.="Белки: ".$other_data['proteins'].'; ';
                }
                if(isset($other_data['fats']) && $other_data['fats']>0) {
                    $message.="Жиры: ".$other_data['fats'].'; ';
                }
                if(isset($other_data['carbohydrates']) && $other_data['carbohydrates']>0) {
                    $message.="Углеводы: ".$other_data['carbohydrates'].'; ';
                }
                $data['messages'][]=array(
                    'deleted'=>$p['date_delete']>0?true:false,
                    'id'=>$p['id'],
                    'value'=>$message
                );
                $p['message'] = $p['id'];
            }
            
            $data['products'][]=array(
                'id'=>$p['id'],
                'code'=>$p['id'],
                'buttonNumber'=>$p['plu'],
                'pluNumber'=>$p['plu'],
                'category'=>$p['category_id'],
                'name'=>$p['short_title'],
                'price'=>$price,
                'productType'=>'WEIGHT',
                'deleted'=>$p['date_delete']>0?true:false,
                'barcodeStructure'=>json_encode(array(array('number'=>'1', 'structure'=>'@{GTIN:7}@{QUANTITY:5}@{CHECKSUM:<EAN_13>}', 'type'=>8))),
                'gtin'=>$p['barcode']??'',
                'message'=>$p['message']
            );
            
            $data['time']=$p['date_update'];
        }

        $data['categories']=array_values($categories);
        return $data;
    }

    function add_products($products, $device, $exportType) {
        $data=$this->get_json($products);
        
        if(empty($data['categories']) && empty($data['products']) && empty($data['messages'])) {
            return array(
                'success'=>true
            );
        }

        $json=array(
            'categories'=>$data['categories'],
            'messages'=>$data['messages'],
            'products'=>$data['products'],
        );
        $images=$data['images'];

        $filename='tmp/clientsscales_'.$device['serial_number'].'.json';
        $this->f3->write($filename, json_encode($json));
        $res=$this->uploadProductsJson($filename, $exportType);
        unlink($filename);
        if($res!=0) {
            // Если ошибка, то сообщаем об этом
            return array(
                'success'=>false,
                'txt'=>' ip '.$this->config['ip'].'. Результат: '.$this->errorCodes[$res]
            );
        }
        if($images) {
            foreach($images as $i) {
                $this->f3->write($i['path'], $this->f3->read($i['url']));
                if(file_exists($i['path'])) {
                    sleep(1);
                    $res=$this->uploadFile($i['name'], $i['path']);
                    unlink($i['path']);
                }
            }
        }

        return array(
            'success'=>true,
            'time'=>$data['time']
        );
    }

    function delete_products() {
        $res=$this->clearProducts();
        if($res!=0) {
            // Если ошибка, то сообщаем об этом
            return array(
                'success'=>false,
                'txt'=>' ip '.$this->config['ip'].'. Результат: '.$this->errorCodes[$res]
            );
        }

        $this->clearMessages();

        return array(
            'success'=>true,
            'time'=>0
        );
    }
}
