package xmpp_manager

import (
	"github.com/astaxie/beego"
	"github.com/astaxie/beego/logs"

	"git.kingpenguin.tk/chteufleur/datahouse.git/models"
	"git.kingpenguin.tk/chteufleur/datahouse.git/models/relay"
	"git.kingpenguin.tk/chteufleur/datahouse.git/models/variables"
	"git.kingpenguin.tk/chteufleur/go-xmpp.git/src/xmpp"
)

const (
	Status_online         = ""
	Status_offline        = ""
	Status_away           = "away"
	Status_chat           = "chat"
	Status_do_not_disturb = "dnd"
	Status_extended_away  = "xa"

	Type_available    = ""
	Type_unavailable  = "unavailable"
	Type_subscribe    = "subscribe"
	Type_subscribed   = "subscribed"
	Type_unsubscribe  = "unsubscribe"
	Type_unsubscribed = "unsubscribed"
	Type_probe        = "probe"
	Type_error        = "error"

	EndRun = "EndRunXMPP"
)

var (
	log = logs.NewLogger(10000)

	JidStr     = beego.AppConfig.String("JID")
	passwdStr  = beego.AppConfig.String("PWD")
	serverPort = beego.AppConfig.String("PORT")

	jid    xmpp.JID
	stream = new(xmpp.Stream)
	client = new(xmpp.XMPP)

	Debug = false
)

func init() {
	log.SetLogger(variables.LogType, variables.LogParams)
	Debug = beego.AppConfig.String("XMPP_DEBUG") == "true"
}

func must(v interface{}, err error) interface{} {
	if err != nil {
		log.Error("Must error : ", err)
	}
	return v
}

func Run() {
	// Create stream and configure it as a component connection.
	log.Info("XMPP Run()")
	jid = must(xmpp.ParseJID(JidStr)).(xmpp.JID)
	addrs, err := xmpp.HomeServerAddrs(jid)
	if err == nil && len(addrs) > 0 {
		stream = must(xmpp.NewStream(addrs[0], &xmpp.StreamConfig{LogStanzas: Debug})).(*xmpp.Stream)
		client = must(xmpp.NewClientXMPP(stream, jid, passwdStr, &xmpp.ClientConfig{InsecureSkipVerify: true})).(*xmpp.XMPP)

		mainXMPP()
	}
	log.Debug("xmpp.Run Ended")
	models.ChanRuns <- EndRun
}

func mainXMPP() {
	SendPresence(Status_online, Type_available, "")

	for x := range client.In {
		switch v := x.(type) {
		case *xmpp.Iq:
			// TODO check if the caller has privilege to ask
			switch v.PayloadName().Space {
			case xmpp.NSDiscoItems:
				execDiscoCommand(v)

			case xmpp.NodeAdHocCommand:
				execCommandAdHoc(v)
			}

		default:
			log.Info("recv: %v", x)
		}
	}

	// Send deconnexion
	SendPresence(Status_offline, Type_unavailable, "")
	log.Debug("mainXMPP Ended")
}

func SendPresence(status, tpye, message string) {
	client.Out <- xmpp.Presence{From: jid.Domain, Show: status, Type: tpye, Status: message}
}

func SendMessage(to, subject, message string) {
	m := xmpp.Message{From: jid.Domain, To: to, Type: "chat"}
	mBody := xmpp.MessageBody{Value: message}
	m.Body = append(m.Body, mBody)

	if subject != "" {
		m.Subject = subject
	}

	client.Out <- m
}

func execDiscoCommand(iq *xmpp.Iq) {
	log.Info("Discovery item iq received")
	reply := iq.Response(xmpp.IQTypeResult)
	discoItem := &xmpp.DiscoItems{Node: xmpp.NodeAdHocCommand}

	relays := relay.GetAllRelay()
	for _, r := range relays {
		discoI := &xmpp.DiscoItem{JID: client.JID.Full(), Node: r.Mac, Name: "Relay : "}
		if r.Description == "" {
			discoI.Name += r.Mac
		} else {
			discoI.Name += r.Description
		}
		discoItem.Item = append(discoItem.Item, *discoI)
	}

	reply.PayloadEncode(discoItem)
	client.Out <- reply
}

func execCommandAdHoc(iq *xmpp.Iq) {
	adHoc := &xmpp.AdHocCommand{}
	iq.PayloadDecode(adHoc)
	log.Info("Commande Ad-Hoc reçut. Node : %s", adHoc.Node)

	// For now, we know that the node is the Mac address for a relay
	relais := relay.GetRelayByMac(adHoc.Node)
	descriptionRelais := relais.Description
	if descriptionRelais == "" {
		descriptionRelais = relais.Mac
	}
	if adHoc.SessionID == "" && adHoc.Action == xmpp.ActionAdHocExecute {
		// First step in the command
		if relais.Id != 0 {
			reply := iq.Response(xmpp.IQTypeResult)
			cmd := &xmpp.AdHocCommand{Node: adHoc.Node, Status: xmpp.StatusAdHocExecute, SessionID: xmpp.SessionID() /*+";"+relais.Mac*/}

			cmdXForm := &xmpp.AdHocXForm{Type: xmpp.TypeAdHocForm, Title: "Commande relais " + descriptionRelais}
			field := &xmpp.AdHocField{Var: "command", Label: "Commande a executer", Type: "list-single"}
			fieldOption := &xmpp.AdHocFieldOption{Value: "toggle"}
			field.Options = append(field.Options, *fieldOption)
			cmdXForm.Fields = append(cmdXForm.Fields, *field)
			// TODO, ajouter des commandes pour allumer X seconde (par exemple)

			cmd.XForm = *cmdXForm

			reply.PayloadEncode(cmd)
			client.Out <- reply
		}
	} else if adHoc.Action == xmpp.ActionAdHocExecute {
		// Last step in the command

		err := relais.Toggle()

		reply := iq.Response(xmpp.IQTypeResult)
		cmd := &xmpp.AdHocCommand{Node: adHoc.Node, Status: xmpp.StatusAdHocCompleted, SessionID: adHoc.SessionID}

		cmdXForm := &xmpp.AdHocXForm{Type: xmpp.TypeAdHocResult, Title: "Commande relais " + descriptionRelais}
		cmd.XForm = *cmdXForm

		note := &xmpp.AdHocNote{Type: xmpp.TypeAdHocNoteInfo}
		if err == nil {
			note.Value = "Commande effectuée avec succes !"
		} else {
			note.Value = "Une erreur c'est produite à l'exécution de la commande…"
		}
		cmd.Note = *note

		reply.PayloadEncode(cmd)
		client.Out <- reply
	} else if adHoc.Action == xmpp.ActionAdHocCancel {
		// command canceled
		reply := iq.Response(xmpp.IQTypeResult)
		cmd := &xmpp.AdHocCommand{Node: adHoc.Node, Status: xmpp.StatusAdHocCanceled, SessionID: adHoc.SessionID}
		reply.PayloadEncode(cmd)
		client.Out <- reply
	}
}
