package controller

/*
#include <uvdbus/smart_sound.h>
*/
import "C"

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"math/rand"
	. "netease_control_center/globals"
	"netease_control_center/interfaces"
	"netease_control_center/interfaces/SRevent"
	"netease_control_center/interfaces/eventPoint"
	"netease_control_center/interfaces/http"
	"netease_control_center/interfaces/log"
	"netease_control_center/interfaces/schedule"
	. "netease_control_center/interfaces/vui"
	"netease_control_center/modules/hardware/led_mcu"
	"netease_control_center/modules/hardware/mcu_leds/atservice"
	"netease_control_center/modules/player"
	"netease_control_center/modules/wifi"
	"netease_control_center/utils/ihwsync"
	"netease_control_center/utils/ping"
	"strconv"
	"sync"
	"time"
)

const (
	HTTP_GET_UUID_TRY_TIMES = 3
	NETWORK_CHECK_TIMEOUT   = 10
)

var (
	Err_Interrupted     = errors.New("操作被取消")
	Err_HttpReqMaxTimes = errors.New("服务端多次请求失败")
	Err_Timeout         = errors.New("超时退出")
	lastAPWifiParingErr error

	networkonce sync.Once

	networkcheckchan = make(chan uint8, 1)

	Pingaddr2 = http.Server

	Pingaddrs = []string{
		"223.5.5.5",    //阿里 AliDNS
		"223.6.6.6",    //阿里 AliDNS
		"180.76.76.76", //百度 BaiduDNS
		"101.226.4.6",  //DNS 派
		"123.125.81.6", //DNS 派
	}
)

func GetRandomPingAddr() string {
	rand.Seed(time.Now().UnixNano())

	return Pingaddrs[rand.Intn(len(Pingaddrs))]
}

func SetInternetConnect(isConnected bool, desc string) {
	//fmt.Println("Internet IsConnected:", isConnected)
	//debug.PrintStack()
	//net disconnect
	tmp := InternetConnect
	if InternetConnect == true && isConnected == false {
		eventPoint.EpRecord("B232", map[string]interface{}{"isWifiConnected": CurWifiStatus == WifiStatuses_CONNECTED, "desc": desc})
	}
	InternetConnect = isConnected

	if tmp != isConnected {
		if isConnected == true {
			InternetConnectEvent()
		} else {
			InterneteDisconnectEvent()
		}
	}
}

func UpdateWifiStatus(tostatus WifiStatuses) {
	CurWifiStatus = tostatus
	UpdateMicLedsStauts()
}

func ClearWifiChan() {
	log.Println(log.DebugLog, "Clear cached wifi paring info")
loop:
	for {
		select {
		case <-ChanWifiParing:
		default:
			break loop
		}
	}
}

func ClearWifiChanAddWifi(config BtWifiParing) {
	ClearWifiChan()
	log.Printf(log.DebugLog, "Add to wifi chan:%+v\n", config)
	select {
	case ChanWifiParing <- config:
	default:
	}
}

func ShowWifiParingLeds(mode WifiParingMode) {
	switch mode {
	case WifiParingMode_BT:
		led_mcu.DirectLedControl(atservice.GUIDE_EFFECT_ID_NET_MODE, atservice.GUIDE_ACTION_START, 0, []byte{})
		IconLed_Wifiparing_Ble.Show()
	case WifiParingMode_AP:
		led_mcu.DirectLedControl(atservice.GUIDE_EFFECT_ID_NET_MODE, atservice.GUIDE_ACTION_START, 0, []byte{})
		IconLed_Wifiparing_Ap.Show()
	}
}

func OffWifiParingLeds() {
	IconLed_Clear.Show()
	led_mcu.DirectLedControl(atservice.GUIDE_EFFECT_ID_CLEAR, atservice.GUIDE_ACTION_STOP, 0, []byte{})
}

func JumpWifiParing(ctx context.Context, nextbootstage BootStage) (reterr error) {
	HasParedWifi = true
	var closeLedsTimer *time.Timer = time.NewTimer(0)
	var timeoutExitTimer *time.Timer = time.NewTimer(0)

	var tmp BtWifiParing
	if NewBoard == false && IsInBackupSystem() == false {
		timeoutExitTimer = ihwsync.TimerReset(timeoutExitTimer, AutoExitWifiParingTimeout.GetCfgListTime())
	} else {
		timeoutExitTimer = ihwsync.TimerReset(timeoutExitTimer, time.Hour*24*360)
	}

	BtOnline := CurMusicSrcOnOffSt.BtOn
	player.CloseBtOfBt()
	log.Println(log.DebugLog, "BT online status:", BtOnline)

	ledIsOn := true
	backupWifiParingMode := CurWifiParingMode

	defer func() {
		closeLedsTimer.Stop()
		timeoutExitTimer.Stop()
		//DisableBle()
		RequestWifiStatus()
		OffWifiParingLeds()
		//if curstage not change, go to next bootstage
		if nextbootstage != BootStage_DONOTHING {
			StartBoard(nextbootstage, false)
		}

		if wifi.GetAPstatus() == wifi.APSTATUS_ON {
			log.Println(log.DebugLog, "Exit wifiparing, in AP mode!")
			wifi.ShutDownAP()
			log.Println(log.DebugLog, "Exit wifi paring success!")
		}

		if backupWifiParingMode == WifiParingMode_AP {
			if reterr != nil && reterr != Err_Interrupted {
				log.Println(log.DebugLog, "wifi paring err:", reterr)
			}
		}

		if BtOnline == true {
			log.Println(log.DebugLog, "Make bt online!!")
			player.OpenBtOfBt()
		}
		StartBackground_wificheck()
		UpdateMicStatus(true)
	}()

	//in wifi paring mode, cancel mic disable
	if IsMicClose == true {
		SetMicClose(false, WhoCLoseMic_User, true)
	}

	//UpdateWifiStatus(WifiStatuses_PARING)
	APPWIFIPARINGSTAUTS = APPInitStatus_UNKNOW
	ClearWifiChan()

	if backupWifiParingMode == WifiParingMode_BT {
		EnableBle()
	}

	ShowWifiParingLeds(backupWifiParingMode)

	if backupWifiParingMode == WifiParingMode_BT {
		tmpctx, tmpctxcancel := context.WithTimeout(context.Background(), 30*time.Second)
		var welcompath string
		if NewBoard {
			welcompath = SoundNotice_WifiParing_Ble_Welecome.GetPath()
		} else {
			welcompath = SoundNotice_Alianwang003.GetPath()
		}
		schedule.PlayNoticeAsync(AdSrcType_FILE, welcompath, CONST_AUDIO_ID, false, tmpctx, func(id uint32, reason schedule.NoticeStopReason) {
			if reason.IsStart() {
				return
			}
			tmpctxcancel()
		})

		select {
		//case <-time.After(WifiParingFirstVuiKeepTime.GetCfgListTime()):
		case <-ctx.Done():
			log.Println(log.DebugLog, "Wifi paring cancel in play tts!")
			tmpctxcancel()
			return Err_Interrupted

		case tmp = <-ChanWifiParing:
			log.Println(log.DebugLog, "Recv wifi paring data:", tmp)
			APPWIFIPARINGSTAUTS = APPInitStatus_UNKNOW
			tmpctxcancel()
			goto _begin_connect_wifi

		case <-tmpctx.Done():
			log.Println(log.DebugLog, "Play finish, go next!")
		}
	}

	if backupWifiParingMode == WifiParingMode_AP {
		tmpctx, tmpctxfunc := context.WithCancel(context.Background())
		schedule.PlayNoticeAsync(AdSrcType_FILE, SoundNotice_WifiParing_AP_Welcome.GetPath(), CONST_AUDIO_ID, false, ctx, func(id uint32, reason schedule.NoticeStopReason) {
			if reason.IsStart() {
				return
			}
			tmpctxfunc()
		})
		select {
		case <-tmpctx.Done():

		case <-ctx.Done():
			log.Println(log.DebugLog, "Wifi paring cancel in play tts!")
			return Err_Interrupted

		case tmp = <-ChanWifiParing:
			log.Println(log.DebugLog, "Recv wifi paring data:", tmp)
			APPWIFIPARINGSTAUTS = APPInitStatus_UNKNOW

			goto _begin_connect_wifi
		}
	}

_begin:
	closeLedsTimer = ihwsync.TimerReset(closeLedsTimer, 30*time.Minute)
	if ledIsOn == false {
		ShowWifiParingLeds(backupWifiParingMode)
		ledIsOn = true
	}

	if backupWifiParingMode == WifiParingMode_AP {
		fmt.Println(log.DebugLog, "Current in ap mode, begin to start ap")
		wifi.StartAP()
	} else {
		fmt.Println(log.DebugLog, "Not in ap mode, shut ap!")
		wifi.ShutDownAP()
	}

	if NewBoard == false && IsInBackupSystem() == false {
		timeoutExitTimer = ihwsync.TimerReset(timeoutExitTimer, AutoExitWifiParingTimeout.GetCfgListTime())
	} else {
		timeoutExitTimer = ihwsync.TimerReset(timeoutExitTimer, time.Hour*24*360)
	}

	log.Println(log.DebugLog, "Begin to jump into wifi paring! mode:", backupWifiParingMode)
	//clear wifi paring status
	APPWIFIPARINGSTAUTS = APPInitStatus_UNKNOW

	for {
		toExit := false
		select {
		case <-timeoutExitTimer.C:
			if NewBoard == false && IsInBackupSystem() == false {
				log.Println(log.DebugLog, "Timeout exit wifi paring!!")
				return Err_Timeout
			}

		case <-ctx.Done():
			log.Println(log.DebugLog, "Wifi paring cancel in waiting wifi account!")
			return Err_Interrupted

		case tmp = <-ChanWifiParing:
			log.Println(log.ErrorLog, "Recv wifi paring data:", tmp)
			APPWIFIPARINGSTAUTS = APPInitStatus_UNKNOW
			toExit = true
			break

			//case <-time.After(20 * time.Second):
			//var showName string
			//if len(BTNAME) > 4 && BTNAME != DEFAULT_DEV_NAME {
			//	showName = BTNAME[len(BTNAME)-4: len(BTNAME)]
			//}
			//if backupWifiParingMode != WifiParingMode_AP {
			//	if CurBtstatus.Status == BtEvt_BLE_DISCONNECT {
			//		schedule.PlayNoticeAsync(AdSrcType_TTS, TTS_TEXT_NOTRECVINGWIFICONFIG+showName, CONST_AUDIO_ID, false, ctx, nil)
			//	} else {
			//		schedule.PlayNoticeAsync(AdSrcType_TTS, TTS_TEXT_BLE_SUUCCESS_WAIT_CONFIG, CONST_AUDIO_ID, false, ctx, nil)
			//	}
			//} else {
			//	schedule.PlayNoticeAsync(AdSrcType_TTS, TTS_TEXT_AP_WAIT_CONFIG, CONST_AUDIO_ID, false, ctx, nil)
			//}

			//case <-closeLedsTimer.C:
			//	if NewBoard == true {
			//		log.Println(log.DebugLog, "It is time to close leds")
			//		led_mcu.DirectLedControl(atservice.GUIDE_EFFECT_ID_CLEAR, atservice.GUIDE_ACTION_STOP, 0, []byte{})
			//		IconLed_Clear.Show()
			//		ledIsOn = false
			//	}
			//	closeLedsTimer = ihwsync.TimerReset(closeLedsTimer, 24*360*time.Hour)
		}
		if toExit == true {
			break
		}
	}

	if ledIsOn == false {
		ShowWifiParingLeds(backupWifiParingMode)
		ledIsOn = true
	}

_begin_connect_wifi:
	eventPoint.EpRecord("B218", map[string]interface{}{"type": backupWifiParingMode.String()})
	schedule.PlayNoticeAsync(AdSrcType_FILE, SoundNotice_Alianwang004.GetPath(), CONST_AUDIO_ID, false, ctx, nil)
	// prd:2017-01-17
	//led_mcu.DirectLedControl(atservice.GUIDE_EFFECT_ID_NETTING, atservice.GUIDE_ACTION_START, 0, []byte{})

	if backupWifiParingMode == WifiParingMode_AP {
		wifi.ShutDownAP()
		ClearModOnline(MODULE_WIFI)
		for {
			if IsModOnline(MODULE_WIFI) {
				break
			} else {
				RequestWifiStatus()
			}
			select {
			case <-ctx.Done():
				log.Println(log.DebugLog, "Wifi paring cancel in waiting wifi connected!")
				return Err_Interrupted

			case <-time.After(time.Second):
				log.Println(log.DebugLog, "Wait wifi online!")

			case <-timeoutExitTimer.C:
				if NewBoard == false && IsInBackupSystem() == false {
					log.Println(log.DebugLog, "Timeout exit wifi paring!!")
					return Err_Timeout
				}
			}
		}
	}

	wifiparingOrigData := []byte(tmp.OrigString)
	interfaces.SendCommand(MODULE_CONTROLLER, MODULE_WIFI, uint32(C.CMD_WIFI_CONF), &wifiparingOrigData)
	log.Printf(log.DebugLog, "Wifi paring data:%+v\n", tmp)
	try := 0
	for {
		isExit := false
		select {
		case <-timeoutExitTimer.C:
			if NewBoard == false && IsInBackupSystem() == false {
				log.Println(log.DebugLog, "Timeout exit wifi paring!!")
				if backupWifiParingMode == WifiParingMode_BT {
					SendBleStatus(APPInitStatus_NOT_IN_WIFIPARING)
				}
				return Err_Timeout
			}

		case <-ctx.Done():
			log.Println(log.DebugLog, "Wifi paring cancel in waiting wifi connected!")
			if backupWifiParingMode == WifiParingMode_BT {
				SendBleStatus(APPInitStatus_NOT_IN_WIFIPARING)
			}
			return Err_Interrupted

		case <-time.After(time.Second):
			try++
			if APPWIFIPARINGSTAUTS > APPInitStatus_SUCCESS && backupWifiParingMode == WifiParingMode_AP {
				lastAPWifiParingErr = errors.New(strconv.Itoa(int(APPWIFIPARINGSTAUTS)))
			}

			if APPWIFIPARINGSTAUTS > APPInitStatus_SUCCESS || try >= 60 {
				eventPoint.EpRecord("B231", map[string]interface{}{"type": backupWifiParingMode.String(), "error": APPWIFIPARINGSTAUTS, "timeout": try >= 60})
				CopyLogfiles()
				log.Println(log.DebugLog, "Wifi paring error! ", APPWIFIPARINGSTAUTS)
				// prd:2017-01-17
				//led_mcu.DirectLedControl(atservice.GUIDE_EFFECT_ID_NET_FAIL, atservice.GUIDE_ACTION_START, 0, []byte{})
				ledIsOn = false
				schedule.PlayNoticeSync(AdSrcType_FILE, SoundNotice_Slianwangshibai001.GetPath(), CONST_AUDIO_ID, false, nil)
			}

			switch APPWIFIPARINGSTAUTS {
			case APPInitStatus_UNKNOW:
				if try >= 60 {
					log.Println(log.DebugLog, APPWIFIPARINGSTAUTS, "Wait wifi timeout! re wifi paring!")
					if backupWifiParingMode == WifiParingMode_BT {
						SendBleStatus(APPInitStatus_OTHER_CONNECT_ERROR)
						schedule.PlayNoticeSync(AdSrcType_FILE, SoundNotice_Alianwang005.GetPath(), CONST_AUDIO_ID, false, ctx)
					} else {
						schedule.PlayNoticeSync(AdSrcType_FILE, SoundNotice_Alianwang009.GetPath(), CONST_AUDIO_ID, false, ctx)
					}
					goto _begin
				} else {
					log.Println(log.DebugLog, APPWIFIPARINGSTAUTS, "Wait wifi timeout! one second after retry: ", try)
				}
			case APPInitStatus_SUCCESS:
				log.Println(log.DebugLog, "Wifi connected!")
				isExit = true

			case APPInitStatus_PASSWD_ERROR, APPInitStatus_ARG_ERROR:
				log.Println(log.DebugLog, TTS_TEXT_WIFICONNECT_ERR_PASS)

				if backupWifiParingMode == WifiParingMode_AP {
					schedule.PlayNoticeSync(AdSrcType_FILE, SoundNotice_Alianwang007.GetPath(), CONST_AUDIO_ID, false, ctx)
				} else {
					schedule.PlayNoticeSync(AdSrcType_FILE, SoundNotice_Alianwang005.GetPath(), CONST_AUDIO_ID, false, ctx)
				}
				goto _begin

			case APPInitStatus_SSID_NOTFOUND:
				if backupWifiParingMode == WifiParingMode_AP {
					schedule.PlayNoticeSync(AdSrcType_FILE, SoundNotice_Alianwang014.GetPath(), CONST_AUDIO_ID, false, ctx)
				} else {
					schedule.PlayNoticeSync(AdSrcType_FILE, SoundNotice_Alianwang005.GetPath(), CONST_AUDIO_ID, false, ctx)
				}
				goto _begin

			default:
				if APPWIFIPARINGSTAUTS > APPInitStatus_SUCCESS {
					if backupWifiParingMode == WifiParingMode_AP {
						schedule.PlayNoticeSync(AdSrcType_FILE, SoundNotice_Alianwang009.GetPath(), CONST_AUDIO_ID, false, ctx)
					} else {
						schedule.PlayNoticeSync(AdSrcType_FILE, SoundNotice_Alianwang005.GetPath(), CONST_AUDIO_ID, false, ctx)
					}
					goto _begin
				}
			}
		}

		if isExit {
			break
		}
	}

	ClearWifiChan()

	//just test
	//go ping.RunCmd("ifconfig")
	//go ping.RunCmd("iwconfig")
	//go ping.RunPing(3, "192.168.1.1")
	//go ping.RunPing(3, "www.163.com")
	//go ping.RunCmdArg("curl", "--insecure", "https://vbox-server.3.163.com")
	//sync time from netease server
	calctx, _ := context.WithTimeout(ctx, 11*time.Second)
	calerr := http.HttpTestAndCalTime(calctx)
	if calerr != nil {
		log.Println(log.ErrorLog, "Caltime err:", calerr)
	} else {
		log.Println(log.DebugLog, "Caltime end, Cur time:", time.Now().Local())
	}

	oldbdusr := BindUser
	firstWifiParing := false
	if NewBoard {
		firstWifiParing = true
	}

	try = 0
	for {
		select {
		case <-ctx.Done():
			log.Println(log.DebugLog, "Http req interrupted!")
			if backupWifiParingMode == WifiParingMode_BT {
				SendBleStatus(APPInitStatus_NOT_IN_WIFIPARING)
			}
			return Err_Interrupted

		case <-time.After(200 * time.Millisecond):
			if err, code := FinalInitUUID(firstWifiParing, tmp.Account); err == nil && code == int(ERR_SUCCESS) {
				log.Println(log.DebugLog, "Wifi paring: get UUID success! Tell app through ble!")
				SetInternetConnect(true, "wifiParing")
				lastAPWifiParingErr = nil
				SendUUIDToVE()
				if backupWifiParingMode == WifiParingMode_BT {
					SendBleStatus(APPInitStatus_SUCCESS)
				}
				VolSet(DefaultVol.GetCfgListVui())
				IconLed_Clear.Show()
				if IsInBackupSystem() == false {
					schedule.PlayNoticeSync(AdSrcType_FILE, SoundNotice_Slianjiechenggong001.GetPath(), CONST_AUDIO_ID, false, ctx)
					waitsyncctx, waitsyncctxcancel := context.WithTimeout(context.Background(), 5*time.Second)
					backupnewboard := NewBoard

					if firstWifiParing == true {
						NewBoard = false
						var playpath string
						if len(SongUrl) > 0 {
							playpath = SongUrl
							SongUrl = ""
						} else {
							playpath = SoundNotice_S002.GetPath()
						}
						log.Println(log.DebugLog, "Get first wifi paring success:", playpath)
						BootDoneCb = func() {
							<-waitsyncctx.Done()
							log.Println(log.DebugLog, "Begin to exec bootdone cb!", playpath)
							MscToast(AdSrcType_FILE, playpath, CONST_AUDIO_ID, false, false, func(params *schedule.NoticePlayerParams) {
								tmpcb := params.BeforeExecFunc

								params.BeforeExecFunc = func() {
									if tmpcb != nil {
										tmpcb()
									}
								}
								params.BeforeSpecialFunc = func() {
									SendEventByYunxin(DeviceEventType_FirstWifiParing, DeviceEventAction_Start, map[string]interface{}{"timestamp": time.Now().UnixNano() / 1e6})
								}

								tmpaftercb := params.SpecialFunc
								params.SpecialFunc = func(arg schedule.TtsPlayerArg) {
									tmpaftercb(arg)
									isComplete := true
									var epComplete string
									if arg.StopReason == schedule.NoticeStopReason_Complete {
										isComplete = true
										epComplete = "complete"
									} else {
										isComplete = false
										epComplete = "interupt"
									}
									eventPoint.EpRecord("F666", map[string]interface{}{"status": epComplete})
									SendEventByYunxin(DeviceEventType_FirstWifiParing, DeviceEventAction_End, map[string]interface{}{"isComplete": isComplete, "timestamp": time.Now().UnixNano() / 1e6})
								}
							})
						}
						//sleep and ensuer ble send ok
						time.Sleep(1500 * time.Millisecond)
					} else {
						led_mcu.DirectLedControl(atservice.GUIDE_EFFECT_ID_EXEC_CMD, atservice.GUIDE_ACTION_START, 0, []byte{})
						schedule.PlayNoticeSync(AdSrcType_FILE, Vui_OldBoard_afterwificonnect(), CONST_AUDIO_ID, false, nil)
						led_mcu.DirectLedControl(atservice.GUIDE_EFFECT_ID_EXEC_CMD, atservice.GUIDE_ACTION_STOP, 0, []byte{})
					}

					go func() {
						DoOnceInit()
						if len(oldbdusr) > 0 && oldbdusr != BindUser && backupnewboard == false {
							AccountChange()
							waitsyncctxcancel()
						} else {
							waitsyncctxcancel()
							SyncAfterNetworkConnect()
						}
					}()
				} else {
					schedule.PlayNoticeSync(AdSrcType_TTS, VUI_BACKUPSYSTEM_WIFIPARING_SUCCESS, CONST_AUDIO_ID, false, nil)
				}

				eventPoint.EpRecord("B212", map[string]interface{}{"type": backupWifiParingMode.String(), "success": true, "rssi": GetWifiRssi()})

				return nil
			} else {
				log.Println(log.ErrorLog, "Init uuid fail:", err)

				if try++; try >= HTTP_GET_UUID_TRY_TIMES {
					log.Println(log.DebugLog, "Get UUID times out, re jump to wifi paring, tell app timeout!", code, err)
					if backupWifiParingMode == WifiParingMode_BT {
						switch ErrCode(code) {
						case ERR_SYSTEM:
							SendBleStatus(APPInitStatus_SERVER_SYSTEM_ERROR)
						case ERR_YUNXIN_FAIL:
							SendBleStatus(APPInitStatus_YUNXIN_FAIL)
						case ERR_ACCOUNT_NOT_EXIST:
							SendBleStatus(APPInitStatus_ACCOUNT_NOT_EXIST)
						case ERR_SERVER_ARGS_ERROR:
							SendBleStatus(APPInitStatus_SERVER_ARG_ERROR)
						case ERR_BIND_ACCOUNT_BEYOND_MAX:
							SendBleStatus(APPInitStatus_BINDNUM_BEYOND_MAX)
						case ERR_DEVICE_BOUNDED:
							SendBleStatus(APPInitStatus_SERVER_DEVICE_BOUNDED)
						case ERR_ACCOUNT_BOUNDED:
							SendBleStatus(APPInitStatus_SERVER_ACCOUNT_BOUNDED)
						default:
							SendBleStatus(APPInitStatus_REQUEST_SERVER_ERROR)
						}
					}

					// prd:2017-01-17
					schedule.PlayNoticeSync(AdSrcType_FILE, SoundNotice_Slianwangshibai001.GetPath(), CONST_AUDIO_ID, false, nil)
					if backupWifiParingMode == WifiParingMode_AP {
						schedule.PlayNoticeSync(AdSrcType_FILE, SoundNotice_Alianwang009.GetPath(), CONST_AUDIO_ID, false, ctx)
					} else {
						schedule.PlayNoticeSync(AdSrcType_FILE, SoundNotice_Alianwang005.GetPath(), CONST_AUDIO_ID, false, ctx)
					}
					//schedule.PlayNoticeSync(AdSrcType_TTS, TTS_TEXT_REQSERVER_ERROR+fmt.Sprintln(code), CONST_AUDIO_ID, false, nil)
					if backupWifiParingMode == WifiParingMode_AP {
						lastAPWifiParingErr = errors.New(strconv.Itoa(int(APPInitStatus_REQUEST_SERVER_ERROR)))
					}

					ledIsOn = false
					if code <= 0 || err != nil {
						eventPoint.EpRecord("B231", map[string]interface{}{"type": backupWifiParingMode.String(), "error": 0xf00d})
					} else {
						eventPoint.EpRecord("B231", map[string]interface{}{"type": backupWifiParingMode.String(), "error": code})
					}
					CopyLogfiles()
					goto _begin
				}
			}
		}
	}

	return nil
}

func CheckWifi(ctx context.Context) error {
	checktimeout := time.After(2 * time.Minute)
	CurWifiStatus = WifiStatuses_UNKNOW
	RequestWifiStatus()
	RequestWifiAutoConnect(false)
	for {
		select {
		case <-ctx.Done():
			return Err_Interrupted
		case <-checktimeout:
			log.Println(log.DebugLog, "Check wifi timeout, exit!")
			return Err_Timeout
		case <-time.After(5 * time.Second):
			if CurWifiStatus != WifiStatuses_CONNECTED || IsWifiAutoConnecting == true {
				log.Println(log.DebugLog, "Wait wifi connected...")
			} else {
				log.Println(log.DebugLog, "Wifi connected!")
				return http.HttpTestAndCalTime(ctx)
			}
		}
	}
}

func SendBleStatus(status APPInitStatus) error {
	ClearWifiChan()
	log.Println(log.DebugLog, "Send ble status:", status, " "+
		"Clean wifiparing chan, chan len:", len(ChanWifiParing))

	var tmp struct {
		ResCode APPInitStatus `json:"resCode"`
	}

	tmp.ResCode = status
	if by, err := json.Marshal(tmp); err == nil {
		interfaces.SendCommand(MODULE_CONTROLLER, MODULE_BT, uint32(C.CMD_WIFI_CONF_RESP), &by)
		return nil
	} else {
		return err
	}

	return nil
}

func SendWifiData(cmd uint32, data []byte) {
	interfaces.SendCommand(MODULE_CONTROLLER, MODULE_WIFI, cmd, &data)
}

func SendWifiCmd(cmd uint32) {
	interfaces.JustSendBusCmd(MODULE_CONTROLLER, MODULE_WIFI, cmd)
}

func EnableBle() {
	log.Println(log.DebugLog, "Enable ble!")
	interfaces.JustSendBusCmd(MODULE_CONTROLLER, MODULE_BT, uint32(C.CMD_BT_LE_ENABLE))
}

func DisableBle() {
	log.Println(log.DebugLog, "Disable ble!")
	interfaces.JustSendBusCmd(MODULE_CONTROLLER, MODULE_BT, uint32(C.CMD_BT_LE_DISABLE))
}

func DisableBT() {
	log.Println(log.DebugLog, "Disable bt!")
	interfaces.JustSendBusCmd(MODULE_CONTROLLER, MODULE_BT, uint32(C.CMD_BT_BREDR_DISABLE))
}

func RequestWifiAutoConnect(force bool) {
	if WifiSwitch != WifiSwitch_On {
		return
	}
	go func() {
		time.Sleep(500 * time.Millisecond)
		if IsWifiAutoConnecting == false || force == true {
			log.Println(log.DebugLog, "Wifi not auto connect, do auto connect!")
			SendWifiCmd(uint32(C.CMD_WIFI_AUTO_CONN))
		} else {
			log.Println(log.DebugLog, "Wifi is now auto connect, do not send auto conn cmd!")
		}
	}()
}

func RequestChangeWifi(force bool) {
	if WifiSwitch != WifiSwitch_On {
		return
	}
	go func() {
		time.Sleep(500 * time.Millisecond)
		if IsWifiChanging == false || force == true {
			SendWifiCmd(uint32(C.CMD_WIFI_CHANGE_VALID_AP))
		} else {
			log.Println(log.DebugLog, "Wifi is changing now, do not send cmd!")
		}
	}()
}

func RequestWifiStatus() {
	SendWifiCmd(uint32(C.CMD_WIFI_STATE_REQ))
}

func TellAppWifiParingSuccessByYunxin() {
	var tmp YunxinWifiParing
	tmp.Cmd = APP_CMD_WIFIPARING_SUCCESS
	tmp.Uuid = Uuid
	if data, err := json.Marshal(tmp); err == nil {
		SendYunxinGuarantee(data)
	} else {
		log.Println(log.DebugLog, "TellAppWifiParingSuccessByYunxin json err:", err)
	}
}

func StartBackground_wificheck() {
	networkonce.Do(func() {
		go Background_wificheck()
	})
}

func first2minWifiConnectFail() {
	log.Println(log.DebugLog, "First 2 mins wifi connect fail!")
	SetInternetConnect(false, "first2minF")
	if SilentBoot == false {
		ToastNetworkError(false)
	}
	eventPoint.EpRecord("B115", map[string]interface{}{"success": false, "restart": true, "error": "first 2 mins wifi connect timeout!"})
}

func ToastNetworkError(isJustPlayEror bool) {
	tmpconfig := player.GetDefaultNoticePlayerParams()
	tmpconfig.Keep = false
	tmpconfig.NeedRepeat = true
	tmpconfig.Level = TTS_LEVEL_1
	tmpconfig.SrcType = AdSrcType_FILE
	tmpconfig.Tts = SoundNotice_Alianwang010.GetPath()
	tmpconfig.LedOn = schedule.DirectLed{EffectId: atservice.GUIDE_EFFECT_ID_SYSTEM_ERR, Action: atservice.GUIDE_ACTION_START}
	tmpconfig.LedOff = schedule.DirectLed{EffectId: atservice.GUIDE_EFFECT_ID_SYSTEM_ERR, Action: atservice.GUIDE_ACTION_STOP}
	if isJustPlayEror == false {
		tmpconfig.BeforeExecFunc = func() {
			player.SoundEffectPlayCmd(CMD_SE_PLAY, SoundNotice_Slianwangshibai001.GetPath())
			time.Sleep(800 * time.Millisecond)
		}
	}
	player.NoticePlayerSendSCmd2(schedule.SCMD_SuperTask_Tts_Ctrl_Play, tmpconfig)
}

func Background_wificheck() {
	log.Println(log.DebugLog, "Background network connect check started!!")
	var calcoasttime time.Time
	timeout := time.After(WifiAutoConnectTimeout.GetCfgListTime())
	firstconnctx, _ := context.WithTimeout(context.Background(), WifiAutoConnectTimeout.GetCfgListTime())
	log.Println(log.DebugLog, "Wifi first startup timeout:", WifiAutoConnectTimeout.GetCfgListTime())

	wifiAutoConnectStart, _ := SRevent.GetSReventChan(SRevent.SRevent_type_AutoConnectStart)
	wifiAutoConnectFinish, _ := SRevent.GetSReventChan(SRevent.SRevent_type_AutoConnectEnd)

_start:
	for CurBootState < BootStage_BEFOREDONE {
		time.Sleep(time.Millisecond * 200)
		if IsInBackupSystem() {
			log.Println(log.DebugLog, "In backup system exit net check!")
			return
		}

		continue
	}

	if CurBootState == BootStage_WIFIPARING {
		goto _disconnectCheck
	}

	if HasParedWifi == true {
		log.Println(log.DebugLog, "Wifi pared, just do network check!")
		goto _disconnectCheck
	}

	RequestWifiAutoConnect(false)
loop_waitAutoConnectWifi:
	for {
		select {
		case <-wifiAutoConnectStart:
			log.Println(log.DebugLog, "Wifi auto connect has started, go next step!!")
			break loop_waitAutoConnectWifi

		case <-time.After(time.Second * 30):
			log.Println(log.DebugLog, "Wait wifi auto connect timeout, has waitted 30s, restart!")
			goto _start

		case <-timeout:
			first2minWifiConnectFail()
			goto _disconnectCheck
		}
	}

	eventPoint.EpRecord("B114", map[string]interface{}{})

loop_finishConnectWifi:
	for {
		select {
		case <-timeout:
			first2minWifiConnectFail()
			goto _disconnectCheck

		case <-wifiAutoConnectFinish:
			log.Println(log.DebugLog, "Auto connect finish, go next!")
			break loop_finishConnectWifi

		case <-time.After(time.Second * 60):
			log.Println(log.DebugLog, "Wait wifi auto connect wifi timeout, goto start!")
			goto _start
		}
	}

	log.Println(log.DebugLog, "wifi connected! Begin to cal time!")
	calcoasttime = time.Now()
	if err := http.HttpTestAndCalTime(firstconnctx); err == nil {
		log.Println(log.DebugLog, "wifi connected! And Internet ready! cal time:", time.Now().Sub(calcoasttime))
		HasBooted = true
		SetInternetConnect(true, "First2minS")
		if SilentBoot == false {
			tmpconfig := player.GetDefaultNoticePlayerParams()
			tmpconfig.NeedRepeat = false
			tmpconfig.SrcType = AdSrcType_FILE
			tmpconfig.Tts = Vui_OldBoard_afterwificonnect()

			tmpconfig.BeforeExecFunc = func() {
				player.SoundEffectPlayCmd(CMD_SE_PLAY, SoundNotice_Slianjiechenggong001.GetPath())
				time.Sleep(100 * time.Millisecond)
			}

			tmpconfig.BeforeSpecialFunc = func() {
				if IsMicClose == false {
					tmp := schedule.DirectLed{EffectId: atservice.GUIDE_EFFECT_ID_EXEC_CMD, Action: atservice.GUIDE_ACTION_START}
					led_mcu.DirectLedControl(tmp.EffectId, tmp.Action, tmp.StartLampNum, []byte{})
				}
			}

			tmpconfig.SpecialFunc = func(arg schedule.TtsPlayerArg) {
				if IsMicClose == false {
					tmp := schedule.DirectLed{EffectId: atservice.GUIDE_EFFECT_ID_EXEC_CMD, Action: atservice.GUIDE_ACTION_STOP}
					led_mcu.DirectLedControl(tmp.EffectId, tmp.Action, tmp.StartLampNum, []byte{})
				}
				DoOnceInit()
			}
			player.NoticePlayerSendSCmd2(schedule.SCMD_SuperTask_Tts_Ctrl_Play, tmpconfig)
		}
		eventPoint.EpRecord("B115", map[string]interface{}{"success": true, "rssi": GetWifiRssi(), "restart": true, "error": "", "ccboottime": time.Now().Sub(StartTime).Nanoseconds() / 1e6})

		SyncAfterNetworkConnect()
		time.Sleep(1 * time.Second)
	} else {
		log.Println(log.DebugLog, "wifi connected! And Internet not ready!", err)
		//todo
		first2minWifiConnectFail()
		eventPoint.EpRecord("B115", map[string]interface{}{"success": false, "restart": true, "error": "wifi connected! And Internet not ready!"})
	}

_disconnectCheck:
	log.Println(log.DebugLog, "Begin network disconnect check!")
	//go MultiPingCheckNetwork()
	for {
		select {
		case <-networkcheckchan:
			log.Println(log.DebugLog, "Begin to check network!!")
			network_check_daemon()

		case <-time.After(5 * time.Second):
			//log.Println(log.DebugLog, "Begin to check network by time!!")
			network_check_daemon()
		}
	}
}

func network_check_daemon() {
	try := 0
	wificonntimes := 0
	forceWifiAutoConnect := false
	defer func() {
		if err := recover(); err != nil {
			log.Println(log.ErrorLog, "network_check_daemon recover err:", err)
		}
	}()
_AllStart:
//log.Println(log.DebugLog, "Begin Check Network!")
	if CurBootState != BootStage_FINISH || WifiSwitch != WifiSwitch_On {
		//do nothing on boot
		fmt.Println("Not in net check daemon, wifiswitch:", WifiSwitch)
		return
	}

	if CurWifiStatus != WifiStatuses_CONNECTED {
		log.Println(log.DebugLog, "Network check daemon, wifi disconnect! reconnect!", wificonntimes)
		SetInternetConnect(false, "netCheck1")
		RequestWifiStatus()
		eventPoint.EpRecord("B241", map[string]interface{}{"desc": "wifi disconnect, reconnect!"})
		eventPoint.EpRecord("B114", map[string]interface{}{})
		RequestWifiAutoConnect(forceWifiAutoConnect)
		if isTimeout := SRevent.WaitSRevent(SRevent.SRevent_type_AutoConnectEnd, time.Second*50); isTimeout == true {
			log.Println(log.DebugLog, "Wait Wifi auto connect timeout! 50s")
			forceWifiAutoConnect = true
			wificonntimes = 0
			goto _AllStart
		} else {
			forceWifiAutoConnect = false
			time.Sleep(200 * time.Millisecond)
			if CurWifiStatus == WifiStatuses_CONNECTED {
				log.Println(log.DebugLog, "Get wifi connect event, go next!")
				eventPoint.EpRecord("B115", map[string]interface{}{"success": true, "rssi": GetWifiRssi(), "restart": false, "error": ""})
			} else {
				eventPoint.EpRecord("B115", map[string]interface{}{"success": false, "restart": false, "error": ""})
				log.Println(log.DebugLog, "Wifi not connect, redo!")
				time.Sleep(5 * time.Second)
				goto _AllStart
			}
			wificonntimes++
		}
	}

	jump := time.Now()
	tmpaddr := GetRandomPingAddr()
_reping:
	if statics, err := ping.GolangPingTimeout(10*time.Second, tmpaddr, 1); err != nil || statics == nil || statics.IsPublicIp() == false || statics.PacketsRecv <= 0 {
		DevNetworkStatus.Ping = ping.Statistics{}
		DevNetworkStatus.Time = time.Now()

		log.Printf(log.DebugLog, "Ping error! err:%+v, curpingaddr:%+v\n", err, tmpaddr)
		if statics != nil {
			log.Printf(log.DebugLog, "statics:%+v, publick ip:%+v\n", *statics, statics.IsPublicIp())
		}

		if tmpaddr != Pingaddr2 {
			tmpaddr = Pingaddr2
			log.Println(log.DebugLog, "change to addr:", tmpaddr, " reping!")
			goto _reping
		}

		log.Println(log.DebugLog, "Ping try error, do http test!")
		httpBeginTime := time.Now()
		var req http.HttpReqGetTimeStamp
		var rsp http.HttpRspGetTimeStamp
		if code, _, httperr := http.HttpPostAuto(req, &rsp); code == 1000 && httperr == nil {
			log.Println(log.DebugLog, "http req server success!")
			DevNetworkStatus.Ping.AvgRtt = time.Now().Sub(httpBeginTime)
			DevNetworkStatus.Ping.MaxRtt = DevNetworkStatus.Ping.AvgRtt
			DevNetworkStatus.Ping.MinRtt = DevNetworkStatus.Ping.AvgRtt
			DevNetworkStatus.Ping.Addr = "http get time"
			setPingSuccess(&DevNetworkStatus.Ping)
			return
		} else {
			log.Println(log.DebugLog, "http req fail, do internet disconnect!", code, httperr)
		}

		//network get fail
		log.Println(log.ErrorLog, "The network ping error:", err, "  try times:", try, time.Now().Sub(jump))
		if statics != nil {
			log.Println(log.ErrorLog, "The ip is public:", statics.IsPublicIp(), " IP:", statics.IPAddr)
			log.Printf(log.ErrorLog, "	statics:%+v\n", *statics)
		}

		SetInternetConnect(false, "pingError")

		//check wifi mode again!!
		if CurBootState != BootStage_FINISH {
			goto _AllStart
		}
		eventPoint.EpRecord("B241", map[string]interface{}{"desc": "ping error, change wifi!"})

		//do not care the wifi module
		RequestChangeWifi(true)

		//if timeout := SRevent.WaitSRevent(SRevent.SRevent_type_ChangeWifiStart, time.Minute); timeout == true {
		//	log.Println(log.ErrorLog, "Wait begin to changing wifi timeout! resent the change wifi cmd!")
		//	goto _AllStart
		//} else {
		//	log.Println(log.DebugLog, "Recv change wifi start event!")
		//}

		if timeout := SRevent.WaitSRevent(SRevent.SRevent_type_ChangeWifiEnd, 2*time.Minute); timeout == true {
			log.Println(log.ErrorLog, "Wait change wifi timeout!!")
		} else {
			log.Println(log.DebugLog, "Wait change wifi finish!")
		}

		try++
		goto _AllStart
	} else {
		setPingSuccess(statics)
	}
}

func setPingSuccess(statics *ping.Statistics) {
	//fmt.Println("ping throught, tmp addr:", tmpaddr, " ping1:", pingaddr, " ping2:", pingaddr2)
	//fmt.Printf("%+v\n", statics)
	var tmpaddr string
	if statics != nil {
		DevNetworkStatus.Ping = *statics
		DevNetworkStatus.Time = time.Now()
		tmpaddr = statics.Addr
	}
	if InternetConnect == false {
		epdata := map[string]interface{}{"success": true}
		if statics != nil {
			log.Printf(log.DebugLog, "Ping result:%+v\n", *statics)
			epdata["pingdelay"] = statics.AvgRtt.Nanoseconds() / 1e6
			epdata["pingaddr"] = tmpaddr
			epdata["checkway"] = "ping"
		} else {
			epdata["checkway"] = "http"
		}
		SetInternetConnect(true, "pingSuccess")
		IsWifiAutoConnecting = false
		//led_mcu.DirectLedControl(led_mcu.GUIDE_EFFECT_ID_SYSTEM_ERR, led_mcu.GUIDE_ACTION_START, 1, []byte{})
		log.Println(log.DebugLog, "Internet connect again!! Sync with server!")
		if CurBootState == BootStage_FINISH {
			SyncAfterNetworkConnect()
		}
		eventPoint.EpRecord("B242", epdata)
		DoOnceInit()
	}
	//log.Printf(log.DebugLog, "Network is ok, result:%+v\n", DevNetworkStatus)
}

func TrigeNetworkCheck() {
	select {
	case networkcheckchan <- 1:
	default:
	}
}

func MultiPingCheckNetwork() {
_begin:
	if mp, err := ping.NewMultiPinger([]string{http.Server, "8.8.8.8", "www.163.com"}); err == nil {
		mp.OnRecv = func(pkt *ping.Packet) {
			//fmt.Printf("%d bytes from %s: icmp_seq=%d time=%v id:%d\n",
			//	pkt.Nbytes, pkt.IPAddr, pkt.Seq, pkt.Rtt, pkt.Id)
		}

		mp.SetPrivileged(true)
		mp.Interval = 1000 * time.Millisecond
		mp.Run()
	} else {
		log.Println(log.DebugLog, "New multi ping err:", err)
		time.Sleep(time.Second)
		goto _begin
	}
}

func CheckWifiResCode(data string) APPInitStatus {
	var tmp struct {
		ResCode APPInitStatus `json:"resCode"`
	}

	err := json.Unmarshal([]byte(data), &tmp)
	if err == nil {
		return tmp.ResCode
	} else {
		return APPInitStatus_UNKNOW
	}
}
