#include <Arduino.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <ESP32Servo.h>

#include <WebServer.h>
#include <HTTPClient.h>
#include <FS.h>
#include "SPIFFS.h"
#include "ESPmDNS.h"
#include <Ticker.h>

Servo myServo;

const int minPulse = 500;   // 最小脉宽(微秒)
const int maxPulse = 2500;  // 最大脉宽(微秒)
int servoPin = 17;          //H4  默认使用，可根据实际连接修改
const int centerPos = 90;   // 舵机中心位置(对应物理90度)

int baudRate = 115200;

Ticker myTicker;
String flashWifi = "/wifi.txt";  //wifi密码存放文件
WebServer webServer;
String ssid;
String passwd;
String html;

WiFiClient wificlient;

const char* mqtt_server = "wulian.nobiggie.cn";
const char* mqtt_username = "ccccccc";
const char* mqtt_password = "abcdefg";
const char* sub = "wifi/car003/control";
const char* pub = "wifi/car003/status";
PubSubClient client(wificlient);

int h8 = 21;  //车灯


int pwmPin = 4;                   //PWM控制
const uint32_t pwmFreq = 1000;    // PWM频率1kHz（1-10kHz适合电机控制）
const uint8_t pwmResolution = 8;  // 8位分辨率（0-255）

int PwmNow = 125;    //初始值 49%
int fadeAmount = 5;  //累加数

void setup(void) {
  Serial.begin(baudRate);
  pinMode(h8, OUTPUT);

  pinMode(pwmPin, OUTPUT);

  // 手动选择通道并绑定引脚
  if (ledcAttach(pwmPin, pwmFreq, pwmResolution)) {
    Serial.printf("PWM配置成功：频率=%dHz，分辨率=%dbits\n", pwmFreq, pwmResolution);
  } else {
    Serial.println("PWM配置失败，请检查参数或通道占用情况");
  }
  ledcWrite(pwmPin, PwmNow);

  if (FlashConnetWifi() == false) {
    Serial.println("启动AP");
    HtmlSetWifi();
  }
  digitalWrite(h8, HIGH);
  // 为舵机分配PWM定时器
  ESP32PWM::allocateTimer(0);
  myServo.setPeriodHertz(50);  // 标准舵机频率50Hz
  delay(100);
  // 初始化舵机连接
  myServo.attach(servoPin, minPulse, maxPulse);

  // 移动到中心位置
  Serial.println("移动到中心位置");
  myServo.write(centerPos);
  delay(500);
  Serial.println("舵机已就绪");

  client.setServer(mqtt_server, 1883);
  client.setCallback(mqttCallback);
}

void loop(void) {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
}

void mqttCallback(char* topic, byte* payload, unsigned int length) {
  String temp;
  for (int i = 0; i < length; i++) {
    temp += (char)payload[i];
  }
  Serial.println(temp);
  if (temp.indexOf("forward") != -1) {  //前进 66-100
    int ln = temp.length();
    int plusIndex = temp.indexOf("+");
    int angle = temp.substring(plusIndex + 1).toInt();
    ledcWrite(pwmPin, angle);
    return;
  }
  if (temp.indexOf("backward") != -1) {  //后退 33-66
    int ln = temp.length();
    int plusIndex = temp.indexOf("+");
    int angle = temp.substring(plusIndex + 1).toInt();
    ledcWrite(pwmPin, angle);
    return;
  }
  if (temp.indexOf("left") != -1) {  //left+转弯角度 0-45 即90度-48度
    int ln = temp.length();
    int plusIndex = temp.indexOf("+");
    int angle = temp.substring(plusIndex + 1).toInt();
    angle = angle > 45 ? 45 : angle;
    angle = 90 + angle;
    rightGo(angle);
    return;
  }
  if (temp.indexOf("right") != -1) {  //right+转弯角度  0-45 即90度-135度
    int ln = temp.length();
    int plusIndex = temp.indexOf("+");
    int angle = temp.substring(plusIndex + 1).toInt();
    angle = angle > 45 ? 45 : angle;
    angle = 90 - angle;
    leftGo(angle);
    return;
  }
  if (temp == "md") {
    aligning();
  }
  if (temp = "stop") {
    aligning();
  }


  //temp = "收到消息:" + temp;
  //client.publish(pub, temp.c_str());
}

void reconnect() {
  while (!client.connected()) {
    String clientId = "ESP32Client-";
    clientId += String(random(0xffff), HEX);

    if (client.connect(clientId.c_str(), mqtt_username, mqtt_password)) {  // 无认证时留空
      Serial.println("MQTT 连接成功");
      client.subscribe(sub);
    } else {
      Serial.print("连接失败, rc=");
      Serial.print(client.state());
      delay(5000);
    }
  }
}

void leftGo(int angle) {
  // 验证角度范围
  if (angle < 45) angle = 45;
  if (angle > 90) angle = 90;
  myServo.write(angle);
}
void aligning() {
  myServo.write(centerPos);  // 确保到达目标位置
}
void rightGo(int angle) {
  if (angle < 90) angle = 90;
  if (angle > 135) angle = 135;
  myServo.write(angle);
}

void HtmlSetWifi() {
  WifiScan();
  apStart();
  delay(1000);
  Serial.println("start webserver");
  //5分后重启 防止停电后复电，路由器连接WIFI速度慢于ESP导致ESP连接不到路由器进入无限AP模式，所以这里只让AP激活5分钟后就重启
  myTicker.once(300, espReboot);
  delay(200);
  webserverStart();
  while (1) {
    webServer.handleClient();  //检查网络服务请求并处理
  }
}
void espReboot() {
  ESP.restart();
}
//扫描附件WIFI
void WifiScan() {
  WiFi.disconnect();
  Serial.begin(baudRate);
  WiFi.mode(WIFI_STA);
  delay(100);
  int n = WiFi.scanNetworks();
  if (n > 0) {
    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 = "扫描不到网络";
  }
}
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(baudRate);
  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(500);
      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(baudRate);
  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) {
    Serial.println(String("Soft-AP IP address = ") + WiFi.softAPIP());
  }
}
void webserverStart() {
  webServer.begin(80);            // 启用网络服务
  webServer.on("/", handleRoot);  // / 在HTTP中，表示请求首页服务，
  webServer.on("/setWifi", setWifi);
  webServer.onNotFound(handleNotFound);  // 当服务器没找到所需的服务，调用404函数
  Serial.println("webserver started");
}
bool FlashConnetWifi() {
  Serial.begin(baudRate);
  delay(200);
  SPIFFS.begin();
  if (SPIFFS.exists(flashWifi) == false) {
    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("FlashConnet...");
  int ii = 0;
  for (ii; ii < 20; ii++) {
    delay(500);
    Serial.print(" . ");
    if (WiFi.status() == WL_CONNECTED) {
      Serial.println("flash connet success");
      return true;
    }
  }
  Serial.println("flash read error");
  return false;
}