package common

import (
	"fmt"
	"strings"

	can "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor"
)

// SignalDesc is copied from project-ai-can-go and modified for local needs.
type SignalDesc struct {
	DBCHash   string `json:"dbc_hash" ch:"dbc_hash"`
	MessageID uint16 `json:"message_id" ch:"message_id"`
	// Description of the signal.
	Name string `json:"name" ch:"signal_name"`
	// Start bit.
	Start uint16 `json:"start" ch:"start"`
	// Length in bits.
	Length uint16 `json:"length" ch:"length"`
	// IsBigEndian is true if the signal is big-endian.
	IsBigEndian bool `json:"big_endian" ch:"big_endian"`
	// IsSigned is true if the signal uses raw signed values.
	IsSigned bool `json:"signed" ch:"signed"`
	// IsMultiplexer is true if the signal is the multiplexor of a multiplexed message.
	IsMultiplexer bool `json:"multiplexer" ch:"multiplexer"`
	// IsMultiplexed is true if the signal is multiplexed.
	IsMultiplexed bool `json:"multiplexed" ch:"multiplexed"`
	// MultiplexerValue is the value of the multiplexer when this signal is present.
	MultiplexerValue uint8 `json:"multiplexer_value" ch:"multiplexer_value"`
	// Offset for real-world transform.
	Offset float64 `json:"offset" ch:"offset"`
	// Scale for real-world transform.
	Scale float64 `json:"scale" ch:"scale"`
	// Min real-world value.
	Min float64 `json:"min" ch:"min"`
	// Max real-world value.
	Max float64 `json:"max" ch:"max"`
	// Unit of the signal.
	Unit string `json:"unit" ch:"unit"`
	// Description of the signal.
	Description string `json:"description" ch:"description"`
	// ValueDescriptions of the signal.
	ValueDescriptions []string `json:"value_descriptions" ch:"value_descriptions"`
	// ReceiverNodes is the list of names of the nodes receiving the signal.
	ReceiverNodes []string `json:"receiver_nodes" ch:"receiver_nodes"`
	// DefaultValue of the signal.
	DefaultValue int64 `json:"default_value" ch:"default_value"`
	// ECUName is a name of an ECU.
	ECUName string
}

func (s *SignalDesc) CopyFromCAN(in *can.Signal) {
	s.Name = in.Name
	s.Start = in.Start
	s.Length = in.Length
	s.IsBigEndian = in.IsBigEndian
	s.IsSigned = in.IsSigned
	s.IsMultiplexer = in.IsMultiplexer
	s.IsMultiplexed = in.IsMultiplexed
	s.MultiplexerValue = uint8(in.MultiplexerValue)
	s.Offset = in.Offset
	s.Scale = in.Scale
	s.Min = in.Min
	s.Max = in.Max
	s.Unit = in.Unit
	s.Description = in.Description
	s.ValueDescriptions = getDescriptions(in.ValueDescriptions)
	s.ReceiverNodes = in.ReceiverNodes
	s.DefaultValue = int64(in.DefaultValue)
}

func getDescriptions(ds []*can.ValueDescription) []string {
	res := make([]string, len(ds))
	for k, d := range ds {
		if d == nil {
			continue
		}

		res[k] = fmt.Sprintf("%d: %s", d.Value, d.Description)
	}

	return res
}

type SignalDescWithECU struct {
	SignalDesc
	ECUName string `json:"ecu_name" ch:"ecu_name"`
}

type MessageDesc struct {
	DBCHash string
	// Name of the message.
	Name string
	// ECUName is a name of an ECU.
	ECUName string
	// ID of the message.
	ID uint32
	// IsExtended is true if the message is an extended CAN message.
	IsExtended bool
	// SendType is the message's send type.
	SendType string
	// Length in bytes.
	Length uint16
	// Description of the message.
	Description string
	// SenderNode is the name of the node sending the message.
	SenderNode string
	// CycleTime is the cycle time (ns) of a cyclic message.
	CycleTime int64
	// DelayTime is the allowed delay (ns) between cyclic message sends.
	DelayTime int64
}

func (m *MessageDesc) CopyFromCAN(in *can.Message) {
	m.Name = in.Name
	m.ID = in.ID
	m.IsExtended = in.IsExtended
	m.SendType = in.SendType.String()
	m.Length = in.Length
	m.Description = in.Description
	m.SenderNode = in.SenderNode
	m.CycleTime = int64(in.CycleTime)
	m.DelayTime = int64(in.DelayTime)
	m.ECUName = extractECUName(in.Name)
}

func extractECUName(msgName string) string {
	names := strings.Split(msgName, "_")
	ecuName := names[0]
	if names[0] == "Diag" && len(names) > 1 {
		ecuName = names[1]
	}

	return ecuName
}

type DBCDesc struct {
	Hash string
	Name string
}
