const SerialPort = require('serialport');

const { connectEmit, disconnectEmit, errorEmit, dataEmit, statusChangedEmit, emitter } = require('./events.js');

const neuroFlugerFunctions = require('./functions/neuroFluger.js'); // Функции взаимодействия с arduino для NeuroFluger
const neuroFlugerCheckConfig = require('./checing_configs/neuroFluger.js'); // Функция для проверки параметров arduino для NeuroFluger

const lukoilFunktions = require('./functions/lukoil.js'); // Функции взаимодействия с arduino для LUKOIL
const lukoilCheckConfig = require('./checing_configs/lukoil.js') // Функция для проверки параметров arduino для для LUKOIL

let arduino = null; // Данне arduino
let ledArduinoConfig = null; // Конфигурации arduino
let incompleteData = '';
let connected = false; // Статус подключения
let project = null; // Название проекта

const baudRate = 9600;

exports.on = (event, action) => {
  emitter.on(event, action);
};

// Функция, проверяющая поля объекта с конфигурацей для arduino 
exports.connectArduino = (initLedArduino) => {
  if (!initLedArduino || !initLedArduino.project) {
    errorEmit('Ошибка: объект конфигурации не содержит информацию о проекте.');
    return;
  }
  
  project = initLedArduino.project;
  let checkConfig = null
  switch(project) {
    case 'neuro-fluger':
      checkConfig = neuroFlugerCheckConfig.checkArduinoConfig(initLedArduino);
      break;
    case 'lukoil':
      checkConfig = lukoilCheckConfig.checkArduinoConfig(initLedArduino);
      break;
    default:
      errorEmit(`Неизвестный проект: ${project}`);
      return;
  }

  if (!checkConfig) {
    errorEmit(`Конфигурация для проекта ${project} не прошла проверку.`);
    return;
  }

  try {
    ledArduinoConfig = initLedArduino;
    ledArduinoConfig.status = 'disconnected'; // Изначально устройство отключено
    ledArduinoConfig.type = null; // Тип запроса по умолчанию
    findPort(ledArduinoConfig);
  } catch (error) {
    errorEmit('В работе модуля Arduino возникли ошибки:', error)
  }
};

// Функция для поиска порта arduino
const findPort = (ledArduinoConfig) => {
  if (connected) return;

  //Если порт задан, пытаемся подключиться к нему
  if (ledArduinoConfig.port) {
    arduino = new SerialPort(ledArduinoConfig.port, { autoOpen: false, baudRate: baudRate });
    
    arduino.open((error) => {
      if (error) {
        errorEmit(`Не удалось подключиться к порту "${ledArduinoConfig.port}". Ошибка: ${error.message}`);
        dataEmit(
          `Попытка подключения к LedArduino по порту ${ledArduinoConfig.port}. Следующая попытка через ${ledArduinoConfig.reconnectIntervalValue} секунд`,
        );
        setTimeout(() => {
          findPort(ledArduinoConfig);
        }, ledArduinoConfig.reconnectIntervalValue * 1000);
      } else {
        dataEmit(`Успешно подключились к порту LedArduino (${ledArduinoConfig.port})`);
        connecting(arduino);
      }
    });
  } else {
    //Иначе проходимся по всем портам и ищем "arduino" 
    SerialPort.list()
      .then((ports) => {
        const port =
          process.platform === 'linux'
            ? ports.find((port) => /arduino/i.test(port.pnpId))
            : ports.find((port) => /VID_2341&PID_0043/i.test(port.pnpId));
        if (!port) {
          errorEmit('Устройство LedArduino не подключено к компьютеру');
          dataEmit(
            `Попытка подключения к LedArduino по порту ${ledArduinoConfig.port}. Следующая попытка через ${ledArduinoConfig.reconnectIntervalValue} секунд`,
          );
          setTimeout(() => {
            findPort(ledArduinoConfig);
          }, ledArduinoConfig.reconnectIntervalValue * 1000);
        }
        ledArduinoConfig.port = port.path;
        arduino = new SerialPort(ledArduinoConfig.port, { autoOpen: false, baudRate: baudRate });
        arduino.open((error) => {
          if (error) {
            errorEmit(
              `Модуль Arduino найден на порту "${ledArduinoConfig.port}", однако подключиться к нему не удалось. Ошибка: ${error.message}`,
            );
            dataEmit(
              `Попытка подключения к LedArduino по порту ${ledArduinoConfig.port}. Следующая попытка через ${ledArduinoConfig.reconnectIntervalValue} секунд`,
            );
            setTimeout(() => {
              findPort(ledArduinoConfig);
            }, ledArduinoConfig.reconnectIntervalValue * 1000);
          } else {
            dataEmit(`Успешно подключились к порту LedArduino (${ledArduinoConfig.port})`);
            connecting(arduino);
          }
        });
      })
      .catch((error) => errorEmit(`Не удалось получить список COM-портов компьютера. Ошибка: ${error.message}`));
  }
};

// Функция для подключения к arduino в случае успешного нахождения порта
const connecting = (arduino) => {
  let result = null;

  // Функция для отправки инициализационного конфига в зависимости от проекта
  const initializationСommand = (arduino, arg) => {
    switch(project) {
      case 'neuro-fluger':
        result = neuroFlugerFunctions.sendConfigInitializeCommand(arg.brightness, arg.numLeds, arduino)
        break;
      case 'lukoil':
        result = lukoilFunktions.sendConfigInitializeCommand(arg, arduino)
        break;
      default:
        errorEmit(`Неизвестный проект: ${project}`);
        return;
    }
    if (!result) {
      findPort(ledArduinoConfig);
    }
  }
  
  // Функция для отправки команды ждущего режима в зависимости от проетка
  const idleCommand = (arduino, arg) => {
    let result = null;

    switch(project) {
      case 'neuro-fluger':
        result = neuroFlugerFunctions.sendInitializeHEXCommand(arg.color_Wait, arg.duration, arduino)
        break;
      case 'lukoil':
        // Отсутствует ждущий режим
        break;
      default:
        errorEmit(`Неизвестный проект: ${project}`);
        return;
    }
    if (!result) {
      findPort(ledArduinoConfig);
    }
  }

  connected = true;
  connectEmit(`Установленно соединение с Arduino по порту: ${arduino.path}`)

  arduino.on('open', () => {
    connected = true;
    connectEmit(`Установленно соединение с Arduino по порту: ${arduino.path}`)  
  });

  arduino.on('data', (data) => {
    let oldStatus = ledArduinoConfig.status;
    const receivedData = incompleteData + data.toString();
    incompleteData = '';
    const messages = receivedData.split('\n');
    incompleteData = messages.pop();

    for (const message of messages) {
      const trimmedMessage = message.trim();
      if (trimmedMessage.includes('Waiting for configData command...')) {
        initializationСommand(arduino, ledArduinoConfig)
        ledArduinoConfig.status = 'initializng';
        ledArduinoConfig.type = null;     
      }
      if (trimmedMessage.includes('Arduino connected!')) {    
        idleCommand(arduino, ledArduinoConfig)
        ledArduinoConfig.status = 'in work';
        ledArduinoConfig.type = 'default';
      }
      if (trimmedMessage.includes('Executing special effect...')) {
        ledArduinoConfig.status = 'executing effect';
        ledArduinoConfig.type = 'special'; 
      }
      if (trimmedMessage.includes('Special effect executed.')) {
        ledArduinoConfig.status = 'waiting command'; 
        ledArduinoConfig.type = 'default'; 
      }
      if (trimmedMessage.includes('Executing navigation effect...')) {
        ledArduinoConfig.status = 'executing effect';
        ledArduinoConfig.type = 'navigation'; 
      }
      if (trimmedMessage.includes('Navigation effect executed.')) {
        ledArduinoConfig.status = 'waiting command';
        ledArduinoConfig.type = 'default'; 
      }
      if (trimmedMessage.includes('LED strip reset.')) {
        ledArduinoConfig.status = 'disconnected';
        ledArduinoConfig.type = null;
      }
    }  
    if (oldStatus !== ledArduinoConfig.status) {
      let statusObject = {
        status: ledArduinoConfig.status,
        effectType: ledArduinoConfig.type,
      };
      statusChangedEmit(statusObject); 
    } 
  });

  arduino.on('close', () => {
    if (connected) {
      connected = false;
      arduino.close((err) => {
        if (err) {
          console.error(`Ошибка при закрытии порта: ${err.message}`);
          disconnectEmit(`Связь с Arduino потеряна. Порт ${arduino.path} закрыт. Светодиодная лента неактивна`);
        }
        dataEmit(
          `Попытка подключения к LedArduino по порту ${arduino.path}. Следующая попытка через ${ledArduinoConfig.reconnectIntervalValue} секунд`,
        );
        setTimeout(() => {
          findPort(ledArduinoConfig);
        }, ledArduinoConfig.reconnectIntervalValue * 1000);
      });
    } else {
      disconnectEmit(`Порт ${arduino.path} закрыт. Светодиодная лента неактивна.`);
    };
  });
};

// Функция для вызова команд управления сценариями светодиодной ленты в зависимости от проекта
exports.callCommand = (command, arg) => {
  let result = null;

  switch (project) {
    case 'neuro-fluger':
      result = neuroFlugerFunctions.sendCommand(command, arg.color, arduino);
      break;
    case 'lukoil':
      result = lukoilFunktions.sendCommand(command, arg, arduino);
      break;
    default:
      errorEmit(`Неизвестный проект: ${project}`);
      return;
  }
  if (!result) {
    findPort(ledArduinoConfig);
  }
};

