#include <Arduino.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include <WiFiClientSecure.h>
#include <WebSocketsClient.h>
#include <driver/i2s.h>
#include <ArduinoJson.h>
#include <FS.h>
#include "SPIFFS.h"
#include "ESPmDNS.h"
#include <Ticker.h>
#include <WebServer.h>
#include <HTTPClient.h>

#define d17 17
const int headerSize = 44;  // wave header size
WiFiMulti WiFiMulti;
WebSocketsClient webSocket;
DynamicJsonDocument jsonBuffer(1024);
JsonObject jsonb;
String EventStr;
char *result;
bool bl;
// Digital I/O used
#define I2S_DOUT 25
#define I2S_BCLK 27
#define I2S_LRC 26

Ticker myTicker;
//被读取的文件位置和名称
String flashWifi = "/wifi.txt";
WebServer webServer(80);  // 创建网络服务器对象，HTTP服务的标准端口为：80
String ssid = "";
String passwd = "";
String html;
int botelu = 115200;
void HtmlSetWifi() {
  WifiScan();
  apStart();
  delay(1000);
  Serial.println("启动网页配置网络");
  //5分后重启 防止停电后复电，路由器连接WIFI速度慢于ESP导致ESP连接不到路由器进入无限AP模式，所以这里只让AP激活5分钟后就重启
  myTicker.once(300, EspStart);
  delay(200);
  webserverStart();
  while (1) {
    webServer.handleClient();  // 检查网络服务请求并处理
  }
}


void EspStart() {
  ESP.restart();
}
//扫描附件WIFI
void WifiScan() {
  Serial.begin(botelu);
  WiFi.disconnect();
  WiFi.mode(WIFI_STA);
  delay(100);
  int n = WiFi.scanNetworks();
  if (n > 0) {
    Serial.println(n);
    String a = "附近WiFi <select id=\"ssid\">";
    for (int i = 0; i < n; i++) {
      Serial.println(WiFi.SSID(i));
      a = a + "<option value=\"" + WiFi.SSID(i) + "\">" + WiFi.SSID(i) + "</option>";
      delay(50);
    }
    String HtmlStart = String("") + "<!DOCTYPE html>" + "<html>" + "<head>" + "<meta charset=\"utf-8\">" + "<meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no\" />" + "<title>设置WiFi</title>" + "<link rel=\"stylesheet\" href=\"\">" + "</head>" + "<script>" + "onload  = function() {" + "document.getElementById(\"up\").onclick = function(){" + "var ssida=document.getElementById(\"ssid\").value;" + "var ssid=ssida.replace(/\\s+/g, \" \");" + "var passwda=document.getElementsByTagName('input')[0].value;" + "var passwd=passwda.replace(/\\s+/g, \" \");" + "xhr = new XMLHttpRequest();" + "xhr.open(\"POST\",\"./setWifi\",true);" + "var posts = (\"ssid=\"+ssid+\"&passwd=\"+ passwd);" + "xhr.setRequestHeader(\"Content-Type\",\"application/x-www-form-urlencoded\");" + "xhr.send(posts);" + "xhr.onreadystatechange = function(){" + "if(this.readyState == 4 && this.status == 200){" + "alert(this.responseText);" + "}" + "}" + "}" + "}" + "</script>" + "<body>";
    String HtmlEnd = String("") + "<p>WiFi密码<input type=\"text\" id=\"passwd\"></p>" + "<input type=\"submit\" id=\"up\" value=\"设 置\">" + "<hr/>" + "<div>如果附近WiFi没有显示到自己的WIFI<div>" + "<div>请重启或离信号近点</div>" + "<div>连接成功AP热点退出转WIFI_STA</div>" + "</body>" + "</html>";
    a = a + "</select>";
    html = HtmlStart + a + HtmlEnd;
  } else {
    html = "扫描不到网络";
    Serial.println("扫描不到网络");
  }
}
void setWifi() {
  ssid = webServer.arg(0);
  passwd = webServer.arg(1);
  const char *Ssid = ssid.c_str();
  const char *Passwd = passwd.c_str();
  String sp = ssid + "&" + passwd;

  Serial.println(sp);
  Serial.begin(botelu);
  delay(500);
  WiFi.begin(Ssid, Passwd);
  WiFi.mode(WIFI_STA);
  delay(200);
  Serial.println("wifi_connet");
  int ii = 0;
  for (ii; ii < 20; ii++) {
    delay(500);
    Serial.print(".");
    if (WiFi.status() == WL_CONNECTED) {
      webServer.send(200, "text/plane", "Wifi密码正确,ESP准备重启");
      webServer.close();
      delay(500);
      WiFi.disconnect();
      delay(500);
      //写入数据
      SPIFFS.format();
      delay(500);
      SPIFFS.begin();
      File dataFile = SPIFFS.open(flashWifi, "w");
      dataFile.println(sp);
      dataFile.close();
      Serial.println("HTML设置网络连接成功");
      delay(1000);
      ESP.restart();
    }
  }
  webServer.send(200, "text/plane", "Wifi密码错误,请重新输入");
}
//------- 展示页面-------
void handleRoot() {
  webServer.send(500, "text/html", html);
}
void handleNotFound(void) {
  webServer.send(404, "text/plain", "404-The page is not Found!");
}


void apStart() {
  Serial.begin(botelu);
  WiFi.mode(WIFI_AP);
  IPAddress local_IP(192, 168, 168, 2);
  IPAddress gateway(192, 168, 168, 1);
  IPAddress subnet(255, 255, 255, 0);
  WiFi.softAPConfig(local_IP, gateway, subnet);

  //启动AP模式，并设置账号和密码
  boolean result = WiFi.softAP("192.168.168.2", "192.168.168.2");
  if (result) {
    //输出 soft-ap ip地址
    //Serial.println(String("Soft-AP IP address = ") + WiFi.softAPIP());
  }
}
void webserverStart() {
  //-----------------启动webserver网络服务--------------------
  webServer.begin();              // 启用网络服务
  webServer.on("/", handleRoot);  // / 在HTTP中，表示请求首页服务，
  webServer.on("/setWifi", setWifi);
  webServer.onNotFound(handleNotFound);  // 当服务器没找到所需的服务，调用404函数
  Serial.println("webserver started");
}

bool FlashConnetWifi() {
  Serial.begin(botelu);
  delay(500);
  //是否存在数据
  SPIFFS.begin();
  if (SPIFFS.exists(flashWifi) == false) {
    Serial.print(flashWifi);
    Serial.println(" NOT FOUND");
    return false;
  }

  File dateFile = SPIFFS.open(flashWifi, "r");
  String result;
  for (int i = 0; i < dateFile.size(); i++) {
    //Serial.print((char)dateFile.read());
    result += (char)dateFile.read();
  }
  //去除前后空格
  result.trim();
  Serial.println(result);
  int i = result.indexOf("&");

  String wifiname = result.substring(0, i);
  String wifipw = result.substring(i + 1);
  const char *WifiName = wifiname.c_str();
  const char *WifiPw = wifipw.c_str();
  Serial.println(WifiName);
  Serial.println(WifiPw);
  WiFi.begin(WifiName, WifiPw);
  delay(500);
  WiFi.mode(WIFI_STA);
  Serial.println("FlashConnetWifi->connet");
  int ii = 0;
  for (ii; ii < 20; ii++) {
    delay(500);
    Serial.print(" . ");
    if (WiFi.status() == WL_CONNECTED) {
      Serial.println("闪存读取,连接成功");
      return true;
    }
  }
  Serial.println("闪存读取,连接失败");
  return false;
}

#define FLASH_RECORD_SIZE (I2S_CHANNEL_NUM * I2S_SAMPLE_RATE * I2S_SAMPLE_BITS / 8 * RECORD_TIME)
void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) {
  const uint8_t *src = (const uint8_t *)mem;
  Serial.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len);
  for (uint32_t i = 0; i < len; i++) {
    if (i % cols == 0) {
      Serial.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i);
    }
    Serial.printf("%02X ", *src);
    src++;
  }
  Serial.printf("\n");
}

void webSocketEvent(WStype_t type, uint8_t *payload, size_t length) {

  switch (type) {
    case WStype_DISCONNECTED:
      Serial.printf("[WSc] Disconnected!\n");
      break;
    case WStype_CONNECTED:
      Serial.printf("[WSc] Connected to url: %s\n", payload);

      // send message to server when Connected
      //webSocket.sendTXT("Connected");
      break;
    case WStype_TEXT:
      Serial.printf("[WSc] get text: %s\n", payload);
      result = "";
      result = reinterpret_cast<char *>(payload);
      EventStr = "";
      EventStr = String(result);

      if (EventStr == "monitor") {
        byte data[]={0xAA,0x55,0x02,0x55,0xAA};
        Serial.write(data,sizeof(data));
        digitalWrite(d17, LOW);
      }
      if (EventStr == "unmonitor") {
        byte data[]={0xAA,0x55,0x00,0x55,0xAA};
        Serial.write(data,sizeof(data));
       // digitalWrite(d17, HIGH);
      }
      break;
    case WStype_BIN:
      Serial.printf("[WSc] get binary length: %u\n", length);
      hexdump(payload, length);

      // webSocket.sendBIN(payload, length);
      break;
    case WStype_ERROR:
    case WStype_FRAGMENT_TEXT_START:
    case WStype_FRAGMENT_BIN_START:
    case WStype_FRAGMENT:
    case WStype_FRAGMENT_FIN:
      break;
  }
}

void setup() {
  // Serial.begin(921600);
  Serial.begin(115200);
  pinMode(d17, INPUT_PULLUP);
  digitalWrite(d17, HIGH);
  Serial.println();
  Serial.println();
  Serial.println();
  //SPIFFSInit();
  // 初始化I2S接口
  i2s_config_t i2sConfig = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
    .sample_rate = 16000,  // 采样率
    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
    .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
    .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count = 4,
    .dma_buf_len = 8
  };

  i2s_pin_config_t pinConfig = {
    .bck_io_num = 2,     // BCK引脚
    .ws_io_num = 15,     // WS引脚
    .data_out_num = -1,  // 不输出音频数据
    .data_in_num = 13    // DATA输入引脚
  };

  i2s_driver_install(I2S_NUM_0, &i2sConfig, 0, NULL);

  i2s_set_pin(I2S_NUM_0, &pinConfig);


  for (uint8_t t = 4; t > 0; t--) {
    Serial.printf("[SETUP] BOOT WAIT %d...\n", t);
    Serial.flush();
    delay(1000);
  }

  if (FlashConnetWifi() == false) {
    HtmlSetWifi();
  }

  webSocket.begin("192.168.0.250", 100, "/service/wss");
  webSocket.onEvent(webSocketEvent);
  webSocket.setAuthorization("user=client1&passwd=123456789");
  webSocket.setReconnectInterval(5000);

}

void loop() {
  webSocket.loop();
  if (digitalRead(d17) == LOW) {
    if (bl == false) {
      //String str3 = "3";
      bl = true;
      webSocket.sendTXT("start");
    } else {
      Serial.println("audio add");
      size_t bytesRead;
      int16_t buffer[1024];
      int8_t d_buff[1024];

      // 从I2S接口读取音频数据
      i2s_read(I2S_NUM_0, (void *)buffer, sizeof(buffer), &bytesRead, portMAX_DELAY);
      webSocket.sendBIN((uint8_t *)buffer, sizeof(buffer));
      delay(10);
    }
  } else if (bl == true) {
    Serial.println("audio end");
    bl = false;
    //String str4 = "4";
    webSocket.sendTXT("end");
  }
}

// 生成wav header，32bit 位深  byte header[headerSize]; wavHeader(header, FLASH_RECORD_SIZE); // 生成wave header
void wavHeader(byte *header, int wavSize) {  // 数字小端格式，字符大端格式
  header[0] = 'R';
  header[1] = 'I';
  header[2] = 'F';
  header[3] = 'F';
  unsigned int fileSize = wavSize + headerSize - 8;
  header[4] = (byte)(fileSize & 0xFF);  // file size, 4byte integer
  header[5] = (byte)((fileSize >> 8) & 0xFF);
  header[6] = (byte)((fileSize >> 16) & 0xFF);
  header[7] = (byte)((fileSize >> 24) & 0xFF);
  header[8] = 'W';
  header[9] = 'A';
  header[10] = 'V';
  header[11] = 'E';
  header[12] = 'f';
  header[13] = 'm';
  header[14] = 't';
  header[15] = ' ';
  header[16] = 0x10;  // length of format data = 16, 4byte integer
  header[17] = 0x00;
  header[18] = 0x00;
  header[19] = 0x00;
  header[20] = 0x01;  // format type:1(PCM), 2byte integer
  header[21] = 0x00;
  header[22] = 0x01;  // channel number:1, 2byte integer
  header[23] = 0x00;
  header[24] = 0x80;  // sample rate:16000=0x00003E80, 4byte integer
  header[25] = 0x3E;
  header[26] = 0x00;
  header[27] = 0x00;
  header[28] = 0x00;  // SampleRate*BitPerSample*ChannelNum/8=16000*32*1/8=64000=0x0000FA00, 4byte integer
  header[29] = 0xFA;
  header[30] = 0x00;
  header[31] = 0x00;
  header[32] = 0x04;  // BitPerSample*ChannelNum/8 = 4, 2byte integer
  header[33] = 0x00;
  header[34] = 0x20;  // BitPerSample:32 = 0x0020, 2byte integer
  header[35] = 0x00;
  header[36] = 'd';
  header[37] = 'a';
  header[38] = 't';
  header[39] = 'a';
  header[40] = (byte)(wavSize & 0xFF);
  header[41] = (byte)((wavSize >> 8) & 0xFF);
  header[42] = (byte)((wavSize >> 16) & 0xFF);
  header[43] = (byte)((wavSize >> 24) & 0xFF);
}
// digital to analog：麦克风输入为digital code(binary)， 需要将编码转换成相应的电压值才能播放
// 先转换成电压值
//i2s_adc_data_scale(flash_write_buff, (uint8_t*)i2s_read_buff, i2s_read_len);
// file.write((const byte*)  flash_write_buff, i2s_read_len);
void i2s_adc_data_scale(uint8_t *d_buff, uint8_t *s_buff, uint32_t len) {
  uint32_t j = 0;
  uint32_t dac_value = 0;
  // 一个采样点是4byte，每4个byte
  for (int i = 0; i < len; i += 4) {
    dac_value = ((((uint16_t)(s_buff[i + 3] & 0xf) << 8) | ((s_buff[i + 2]))));
    d_buff[j++] = 0;
    d_buff[j++] = 0;
    d_buff[j++] = 0;
    d_buff[j++] = dac_value * 128 / 2048;
  }
}
