#include "MKS42C.h"

MKS42C::MKS42C(HardwareSerial& serial, uint8_t address) {
    _serial = &serial;
    _addr = address;
    _isMoving = false;
}

// Вспомогательная функция для CRC (стр. 13 PDF)
uint8_t MKS42C::calculateCRC(uint8_t* data, uint8_t len) {
    uint16_t checksum = 0;
    for (uint8_t i = 0; i < len; i++) {
        checksum += data[i];
    }
    return (uint8_t)(checksum & 0xFF);
}

void MKS42C::sendRaw(uint8_t* data, uint8_t len) {
    _serial->write(data, len);
    _serial->write(calculateCRC(data, len));
}

void MKS42C::clearBuffer() {
    while (_serial->available()) {
        _serial->read();
    }
}

// --- УПРАВЛЕНИЕ ---

void MKS42C::setEnable(bool state) {
    // Команда 0xF3 (стр. 16 PDF)
    uint8_t cmd[] = {_addr, 0xF3, (uint8_t)(state ? 0x01 : 0x00)};
    sendRaw(cmd, 3);
}

void MKS42C::run(uint8_t dir, uint8_t speed) {
    // Команда 0xF6 (стр. 16 PDF)
    // dir: 0 - CW, 1 - CCW
    // speed: 0-127
    uint8_t val = ((dir & 0x01) << 7) | (speed & 0x7F);
    uint8_t cmd[] = {_addr, 0xF6, val};
    
    sendRaw(cmd, 3);
    _isMoving = true; 
    _lastMoveTime = millis();
    _lastAbsPos = readAbsolutePosition();
}

void MKS42C::run(int16_t speed) {
    uint8_t dir = (speed < 0) ? 1 : 0; // 1 - CCW, 0 - CW
    uint8_t absSpeed = abs(speed);
    
    if (absSpeed > 127) absSpeed = 127; // Максимальная скорость 7 бит

    // Формируем байт: 7-й бит это направление, 0-6 биты это скорость
    uint8_t val = (dir << 7) | (absSpeed & 0x7F);

    // Передаем пакет из 3 байт + CRC
    uint8_t cmd[] = {_addr, 0xF6, val};
    sendRaw(cmd, 3);
    
    _isMoving = true;
}

void MKS42C::stop() {
    // Команда 0xF7 (стр. 16 PDF)
    uint8_t cmd[] = {_addr, 0xF7};
    sendRaw(cmd, 2);
    _isMoving = false;
}

void MKS42C::rotateDegrees(float degrees, uint8_t speed) {
    // Команда 0xFD (стр. 17 PDF)
    // Используем проверенный коэффициент 3200 имп/оборот
    long pulses = (long)((degrees / 360.0) * 3200.0);
    uint8_t dir = (pulses >= 0) ? 0x00 : 0x01;
    uint32_t absP = abs(pulses);

    uint8_t cmd[] = {
        _addr, 
        0xFD, 
        (uint8_t)((dir << 7) | (speed & 0x7F)), 
        (uint8_t)((absP >> 24) & 0xFF), 
        (uint8_t)((absP >> 16) & 0xFF), 
        (uint8_t)((absP >> 8) & 0xFF), 
        (uint8_t)(absP & 0xFF)
    };
    sendRaw(cmd, 7);
    _isMoving = true;
    _lastMoveTime = millis();
    _lastAbsPos = readAbsolutePosition();
}

// --- ЧТЕНИЕ ДАННЫХ (8-байтовый пакет из V1.1.2) ---

float MKS42C::readPosition() {
    // 1. Убираем clearBuffer() отсюда — он может "съедать" начало ответа
    
    // 2. Шлем запрос
    uint8_t cmd[] = {_addr, 0x30};
    sendRaw(cmd, 2);
    delay(5);
    // 3. Ждем появления данных (увеличим до 200мс)
    unsigned long start = millis();
    while (_serial->available() < 8 && millis() - start < 200) {
        delay(1);
    }

    if (_serial->available() < 8) {
        // Если данных нет, попробуем еще раз послать запрос (иногда первый теряется)
        sendRaw(cmd, 2);
        start = millis();
        while (_serial->available() < 8 && millis() - start < 200) delay(1);
    }

    if (_serial->available() >= 8) {
        // 4. Ищем заголовок 0xE0
        while (_serial->available() > 0 && _serial->peek() != _addr) {
            _serial->read(); // Выкидываем мусор
        }

        if (_serial->available() >= 8) {
            uint8_t buf[8];
            _serial->readBytes(buf, 8);
            
            // Считаем CRC
            uint16_t sum = 0;
            for (int i = 0; i < 7; i++) sum += buf[i];
            
            if ((uint8_t)(sum & 0xFF) == buf[7]) {
                uint16_t value = ((uint16_t)buf[5] << 8) | buf[6];
                return (value * 360.0) / 65536.0;
            } else {
                Serial.print("CRC Err: "); Serial.println((uint8_t)(sum & 0xFF), HEX);
            }
        }
    } else {
        Serial.print("Buf low: "); Serial.println(_serial->available());
    }
    return -1.0;
}

long MKS42C::readAbsolutePosition() {
    static long lastValidPos = 0; // Храним последнее успешное значение
    
    uint8_t cmd[] = {_addr, 0x30};
    sendRaw(cmd, 2);

    delay(5);

    unsigned long start = millis();
    while (_serial->available() < 8 && millis() - start < 100);

    if (_serial->available() >= 8) {
        while (_serial->available() > 8 && _serial->peek() != _addr) _serial->read();

        uint8_t buf[8];
        _serial->readBytes(buf, 8);
        
        uint16_t sum = 0;
        for (int i = 0; i < 7; i++) sum += buf[i];
        
        if ((uint8_t)(sum & 0xFF) == buf[7]) {
            int32_t carry = ((int32_t)(int8_t)buf[1] << 24) | 
                            ((uint32_t)buf[2] << 16) | 
                            ((uint32_t)buf[3] << 8)  | 
                            (uint32_t)buf[4];
            uint16_t value = ((uint16_t)buf[5] << 8) | buf[6];
            lastValidPos = (long)((long long)carry * 65536LL + value);
        }
    }
    return lastValidPos; // Возвращаем последнее нормальное, если текущее битое
}

bool MKS42C::isBusy() {
    while (_serial->available() >= 3) {
        if (_serial->peek() != _addr) { _serial->read(); continue; }
        uint8_t b[3];
        _serial->readBytes(b, 3);
        if (b[1] == 0x02) { 
            _isMoving = false; 
            return false; 
        }
    }

    if (_isMoving) {
        long currentPos = readAbsolutePosition();
        
        // Если позиция изменилась — мотор точно едет
        if (currentPos != _lastAbsPos) {
            _lastAbsPos = currentPos;
            _lastMoveTime = millis();
            return true;
        } 
        
        if (millis() - _lastMoveTime > 150) {
            _isMoving = false;
        }
    }

    return _isMoving;
}