package manufacture

/*
#include <uvdbus/smart_sound.h>
#include <uvdbus/ota.h>
*/
import "C"
import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"github.com/godbus/dbus"
	"netease_control_center/globals"
	"netease_control_center/interfaces/log"
	. "netease_control_center/modules/hardware/mcu_leds/atservice"
	"netease_control_center/nembd"
	"os"
)

const (
	NETEASE_DBUS_BUS_NAME = "netease.ihw.manufacture_controller"

	NETEASE_DBUS_INTERFACE_COMMONNAME = "netease.ihw.SmartAudio"

	NETEASE_DBUS_SIGNAL_NAME = "Notify"
	NETEASE_DBUS_METHOD_NAME = "API"
)

var (
	Conn   *dbus.Conn      = nil
	busobj *dbus.BusObject = nil
)
var (
	Methodtable = map[string]interface{}{
		"GOAPI": goapi,
		"API":   capi,
	}
	SigChan chan *dbus.Signal
)

type testCmdRespInfoT struct {
	testId uint8
	ret    uint8
}

type mcuTestGuideRGB struct {
	Red   int
	Green int
	Blue  int
}

type mcuTestMatrixLevel struct {
	Level int
}

type LampTestId uint8

const (
	MCU_TEST_GUIDE  LampTestId = 1
	MCU_TEST_MATRIX LampTestId = 2
)

var mcuAtIsReady bool = false

func CheckSignal() {
	for sig := range SigChan {
		fmt.Printf("Get a signal:%+v\n", sig)
	}
}

func capi(modname uint32, msgmsk uint32, buscmd uint32, dataLen uint32, data string) *dbus.Error {
	return goapi(modname, msgmsk, 0, 0, buscmd, uint32(len(data)), []byte(data))
}

func goapi(modId uint32, msgmsk uint32, sec uint32, usec uint32, buscmd uint32, msglen uint32, data []byte) *dbus.Error {

	switch buscmd {
	//Voice status: wake up. data: {"angle":93,"score":53,"beam":2,"power":332032770048.000000}
	case uint32(C.CMD_MSC_MSG_CONTROLLER_WAKEUP):
	case uint32(C.CMD_TEST_GET_VER_REQ):
		var tmp struct {
			R16version string `json:"appVer"`
			HwVer      string `json:"hwVer"`
			CpuSerial  string `json:"cpuSerial"`
			ChipId     string `json:"chipId"`
		}
		tmp.R16version = *globals.GitTag
		_, _, tmp.CpuSerial, tmp.ChipId, _, _ = nembd.BoardInfo()
		tmp.HwVer = *globals.Hv
		if data, err := json.Marshal(tmp); err == nil {
			SendCommand(globals.MODULE_MANUFACTURE_CONTROLLER, globals.MODULE_MANUFACTURE, uint32(C.CMD_TEST_GET_VER_RESP), &data)
		}
	case uint32(C.CMD_TEST_MCU_LED_GUIDE):
		log.Printf(log.DebugLog, "rev mcu led guide test cmd: %s\n", data)

		if mcuAtIsReady {
			var t mcuTestGuideRGB
			if err := json.Unmarshal(data, &t); err == nil {
				log.Println(log.DebugLog, "r:", t.Red, ",g:", t.Green, ",b:", t.Blue)

				buf := new(bytes.Buffer)
				binary.Write(buf, binary.LittleEndian, MCU_TEST_GUIDE)
				binary.Write(buf, binary.LittleEndian, uint8(t.Red))
				binary.Write(buf, binary.LittleEndian, uint8(t.Green))
				binary.Write(buf, binary.LittleEndian, uint8(t.Blue))
				log.Println(log.DebugLog, "guide test data:", buf.Bytes())

				SendAtCmd(AT_CMD_LAMP_TEST, AT_CMD_FWD_PRI_NORMAL, buf.Bytes(), atCmdLampTestCallback)
			}
		} else {
			log.Printf(log.ErrorLog, "MCU is not ready\n")
		}
	case uint32(C.CMD_TEST_MCU_LED_MATRIX):
		log.Printf(log.DebugLog, "rev mcu led matrix test cmd: %s\n", data)

		if mcuAtIsReady {
			var t mcuTestMatrixLevel
			if err := json.Unmarshal(data, &t); err == nil {
				log.Println(log.DebugLog, "level:", t.Level)

				buf := new(bytes.Buffer)
				binary.Write(buf, binary.LittleEndian, MCU_TEST_MATRIX)
				binary.Write(buf, binary.LittleEndian, uint8(t.Level))
				log.Println(log.DebugLog, "mrtrix test data:", buf.Bytes())

				SendAtCmd(AT_CMD_LAMP_TEST, AT_CMD_FWD_PRI_NORMAL, buf.Bytes(), atCmdLampTestCallback)
			}
		} else {
			log.Printf(log.ErrorLog, "MCU is not ready\n")
		}
	case uint32(C.CMD_TEST_GET_MCU_VER_REQ):
		log.Println(log.DebugLog, "rev mcu led get version test cmd")
		if mcuAtIsReady {
			SendAtCmd(AT_CMD_VERSION, AT_CMD_FWD_PRI_NORMAL, []byte{}, atCmdVersionCallback)
		} else {
			log.Printf(log.ErrorLog, "MCU is not ready\n")
		}

	default:
		fmt.Printf("Unknow bus command: %d\n", buscmd)
	}

	return nil
}

func atStateCallback(mcuStatus McuStatusT) {
	switch mcuStatus {
	case MCU_STATUS_AT_NOT_READY:
		mcuAtIsReady = false
		log.Println(log.DebugLog, "mcu AT not ready!!!")
	case MCU_STATUS_AT_READY:
		mcuAtIsReady = true
		log.Println(log.DebugLog, "mcu AT ready.")
	default:
		log.Println(log.DebugLog, "err mcu status:", mcuStatus)
	}
}

func atCmdLampTestCallback(cmdId uint32, status AT_CMD_RSP, buf []byte) {
	if status == AT_CMD_RSP_OK {
		log.Println(log.DebugLog, "LampTest:", buf)
		var resp testCmdRespInfoT
		if len(buf) >= 2 { //int(unsafe.Sizeof(resp))
			resp.testId = buf[0]
			resp.ret = buf[1]
			switch LampTestId(resp.testId) {
			case MCU_TEST_GUIDE:
				if resp.ret == 1 {
					log.Println(log.DebugLog, "test guide successful")
				} else {
					log.Printf(log.DebugLog, "test guide err, ret = %d\n", resp.ret)
				}
				SendCommand(globals.MODULE_MANUFACTURE_CONTROLLER, globals.MODULE_MANUFACTURE, uint32(C.CMD_TEST_MCU_LED_GUIDE_RSP), nil)
			case MCU_TEST_MATRIX:
				if resp.ret == 1 {
					log.Println(log.DebugLog, "test matrix successful")
				} else {
					log.Printf(log.DebugLog, "test matrix err, ret = %d\n", resp.ret)
				}
				SendCommand(globals.MODULE_MANUFACTURE_CONTROLLER, globals.MODULE_MANUFACTURE, uint32(C.CMD_TEST_MCU_LED_MATRIX_RSP), nil)
			}
		}

	} else {
		log.Println(log.DebugLog, "atCmdLampTestCallback err:", status, buf)
	}
}

func atCmdVersionCallback(cmdId uint32, status AT_CMD_RSP, buf []byte) {
	if status == AT_CMD_RSP_OK {
		log.Printf(log.DebugLog, "ver:%s\n", buf)

		var tmp struct {
			McuVer string `json:"McuVer"`
		}
		tmp.McuVer = string(buf)
		if data, err := json.Marshal(tmp); err == nil {
			SendCommand(globals.MODULE_MANUFACTURE_CONTROLLER, globals.MODULE_MANUFACTURE, uint32(C.CMD_TEST_GET_MCU_VER_RESP), &data)
		}
	} else {
		log.Println(log.DebugLog, "AtCmdVersionCallback err:", status, buf)
	}
}

func DbusInit() {
	log.Init()
	fmt.Println("Init dbus interface")
	Conn, _ = dbus.SessionBus()

	replay, err := Conn.RequestName(NETEASE_DBUS_BUS_NAME, dbus.NameFlagDoNotQueue)

	if err != nil {
		fmt.Println("dbus request error ", err)
		panic(err)
	}

	if replay != dbus.RequestNameReplyPrimaryOwner {
		fmt.Println("Name has already been taken, exit!s")
		fmt.Println(replay)
		os.Exit(1)
	}

	if v := GetAtGpioState(); v == true {
		AtCmdInit(atStateCallback)
	}

	//Signal
	//controller chan
	Conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
		`type='signal',interface='`+NETEASE_DBUS_INTERFACE_COMMONNAME+`'`)
	Conn.Signal(SigChan)
	Conn.ExportMethodTable(Methodtable, dbus.ObjectPath("/netease/ihw/manufacture_controller"), NETEASE_DBUS_INTERFACE_COMMONNAME)
}

func SendCommand(fromId uint32, destId uint32, cmdId uint32, data *[]byte) {
	var msk uint32
	if destId == 0 {
		msk = 0xffffffff
	} else {
		msk = 1 << destId
	}

	var tmp string
	if data != nil {
		tmp = string(*data)
	} else {
		tmp = ""
	}
	Conn.Object(globals.Modules[destId].Aliase, dbus.ObjectPath(globals.Modules[destId].Path)).
		Go(NETEASE_DBUS_INTERFACE_COMMONNAME+"."+NETEASE_DBUS_METHOD_NAME, dbus.FlagNoReplyExpected, nil,
			fromId,
			uint32(msk),
			cmdId,
			uint32(len(tmp)),
			tmp)
	fmt.Printf("cmdId:0x%x send:%+v  len:%d\n", cmdId, tmp, len(tmp))
}

func JustSendBusCmd(fromId uint32, destId uint32, cmdId uint32) {
	var msk uint32
	if destId == 0 {
		msk = 0xffffffff
	} else {
		msk = 1 << destId
	}

	var tmp string = ""

	Conn.Object(globals.Modules[destId].Aliase, dbus.ObjectPath(globals.Modules[destId].Path)).
		Go(NETEASE_DBUS_INTERFACE_COMMONNAME+"."+NETEASE_DBUS_METHOD_NAME, dbus.FlagNoReplyExpected, nil,
			fromId,
			uint32(msk),
			cmdId,
			uint32(len(tmp)),
			tmp)

	fmt.Printf("sendCmdId: 0x%x\n", cmdId)
}

func BroadSignalAll(srcId uint32, buscmd uint32, data string) error {
	return BroadSignal(srcId, uint32(0xffffffff), buscmd, NETEASE_DBUS_INTERFACE_COMMONNAME, NETEASE_DBUS_SIGNAL_NAME, data)
}

func BroadSignal(srcId uint32, toMask uint32, buscmd uint32, interfaceName string, memberName string, data string) error {
	return Conn.Emit("/netease/ihw/manufacture_controller", NETEASE_DBUS_INTERFACE_COMMONNAME+"."+memberName, srcId, toMask, buscmd, data)
}

func SeqTest2(no int) {
	var tmp struct {
		Id int `json:"id"`
	}
	tmp.Id = no
	if data, err := json.Marshal(tmp); err == nil {
		SendCommand(globals.MODULE_MANUFACTURE_CONTROLLER, globals.MODULE_CONTROLLER, uint32(C.CMD_MISC_PING), &data)
	}
}

func SeqTest() {
	for i := 0; i < 1000; i++ {
		fmt.Println("Send:", i)
		SeqTest2(i)
	}
}
