package main

import (
	"errors"
	"fmt"
	"gitea.unprism.ru/KRBL/n9m/v2/pkg/models"
	"gitea.unprism.ru/KRBL/n9m/v2/pkg/protocol"
	"gitea.unprism.ru/KRBL/n9m/v2/pkg/smart"
	"io"
	"net"
	"os"
	"syscall"
)

func main() {
	ln, err := net.Listen("tcp", "0.0.0.0:5556")

	if err != nil {
		panic(err)
	}

	for {
		var conn net.Conn

		conn, err = ln.Accept()

		if err != nil {
			continue
		}

		go handle(conn)
	}
}

func handleSpecialPackages(_ *smart.SmartPackage, pack protocol.Package) error {
	switch pack.SSRC {
	case protocol.SpecialPayloadTypeGPS:
		fmt.Printf("%+v\n", pack.GPS)
		return nil
	default:
		return fmt.Errorf("unhandled special operation: %d", pack.SSRC)
	}
}

func handleCertificateConnect(sPack *smart.SmartPackage, pack protocol.Package) (err error) {
	var params models.CertificateConnectRequest

	if err = pack.GetParametersAs(&params); err != nil {
		return fmt.Errorf("failed to get parameters: %w", err)
	}

	var response = models.CertificateConnectResponse{
		ErrorCode:   0,
		CommandMask: models.CommandMaskAll,
	}

	pack.SetResponse(response)

	if _, err = sPack.Write(pack.PackPackage()); err != nil {
		return fmt.Errorf("failed to write package: %w", err)
	}

	fmt.Println("Connected:", params.SerialNumber)
	sPack.Storage["serial"] = params.SerialNumber

	var request models.ConfigModelGetRequest
	request.MDVR = "?"

	pack.Payload.Module = "CONFIGMODEL"
	pack.Payload.Operation = "GET"
	pack.SetParameters(request)

	sPack.Write(pack.PackPackage())

	return
}

func handleKeepAlive(sPack *smart.SmartPackage, pack protocol.Package) (err error) {
	serial := sPack.Storage["serial"]
	fmt.Println(serial, "still alive!")

	pack.SetResponse(nil)
	sPack.Write(pack.PackPackage())

	return
}

func handleGetConfig(sPack *smart.SmartPackage, pack protocol.Package) (err error) {
	serial := sPack.Storage["serial"]

	os.WriteFile(fmt.Sprintf("./%s.json", serial), pack.RawPayload, 0644)

	var request models.ConfigModelSetRequest

	if err = pack.GetParametersAs(&request); err != nil {
		fmt.Println(err)
		return err
	}

	return
}

func handleUselessAlarms(sPack *smart.SmartPackage, pack protocol.Package, response models.SendAlarmInfoResponse) (err error) {
	return nil
}

func handleVideoLossAlarm(sPack *smart.SmartPackage, pack protocol.Package, response models.SendAlarmInfoResponse) (err error) {
	fmt.Println("Video loss alarm!")
	return nil
}

func handleCameraCoveredAlarm(sPack *smart.SmartPackage, pack protocol.Package, response models.SendAlarmInfoResponse) (err error) {
	fmt.Println("Camera covered alarm!")
	return nil
}

func handleSPI(_ *smart.SmartPackage, pack protocol.Package) (err error) {
	var params models.SpiParameters

	if err = pack.GetParametersAs(&params); err != nil {
		return
	}

	fmt.Printf("%+v\n", params)

	return
}

func createSmartPackage(conn net.Conn) (pack *smart.SmartPackage) {
	pack = smart.NewSmartPackage(conn)

	pack.AddPayloadHandler(protocol.PayloadTypeSpecial, handleSpecialPackages)

	pack.AddJSONHandler("CERTIFICATE", "CONNECT", handleCertificateConnect)
	pack.AddJSONHandler("CERTIFICATE", "KEEPALIVE", handleKeepAlive)
	pack.AddJSONHandler("CONFIGMODEL", "GET", handleGetConfig)
	pack.AddJSONHandler("DEVEMM", "SPI", handleSPI)

	pack.AddAlarmHandler(protocol.AlarmTypeMotionDetection, handleUselessAlarms)

	pack.AddAlarmHandler(protocol.AlarmTypeVideoLoss, handleVideoLossAlarm)
	pack.AddAlarmHandler(protocol.AlarmTypeCameraCovered, handleCameraCoveredAlarm)

	return
}

/*
	go func() {
		pack := packS
		pack.Payload.Module = "EVEM"
		pack.Payload.Operation = "GALARMING"

		ticker := time.NewTicker(5 * time.Second)
		defer ticker.Stop()

		for range ticker.C {
			fmt.Println("Sent!")
			if _, err := conn.Write(pack.PackPackage()); err != nil {
				fmt.Println("Failed to send GALARMING:", err)
				return
			}
		}
	}()
*/

func isNetConnClosedErr(err error) bool {
	switch {
	case
		errors.Is(err, net.ErrClosed),
		errors.Is(err, io.EOF),
		errors.Is(err, syscall.EPIPE):
		return true
	default:
		return false
	}
}

func handle(conn net.Conn) {
	pack := createSmartPackage(conn)

	var err error
	for {
		if err = pack.Handle(); err != nil {
			fmt.Println("Error:", err)

			if isNetConnClosedErr(err) {
				return
			}
		}
	}
}