package protocol

import (
	"encoding/json"
	"fmt"
	"strconv"
	"time"
)

type PayloadType uint8

const (
	PayloadTypeData                  PayloadType = 0
	PayloadTypeLive                  PayloadType = 2
	PayloadTypeDownload              PayloadType = 3
	PayloadTypePlayback              PayloadType = 4
	PayloadTypeCapturedPhotos        PayloadType = 6
	PayloadTypeParameterImport       PayloadType = 10
	PayloadTypeParameterExport       PayloadType = 11
	PayloadTypeTransmissionSubStream PayloadType = 15
	PayloadTypeRecordingSubStream    PayloadType = 16
	PayloadTypeBlackBox              PayloadType = 17
	PayloadTypeSpecial               PayloadType = 22
	PayloadTypeMaintainData          PayloadType = 30
)

type SpecialPayloadType uint16

const (
	SpecialPayloadTypeHeartbeat SpecialPayloadType = iota
	SpecialPayloadTypeHeartbeatWithoutBody
	SpecialPayloadTypeGPS
	SpecialPayloadTypeMileage
	SpecialPayloadTypeEnvironmentalQuality
	SpecialPayloadTypeDrivingPosture
	SpecialPayloadTypeScanningGun
	SpecialPayloadTypeOil
	SpecialPayloadTypeGDS
	SpecialPayloadTypeGPSToBWS
	SpecialPayloadTypeCANBOX
	SpecialPayloadTypeGSenor
	SpecialPayloadTypeAckGPS
)

type Message struct {
	Module    string      `json:"MODULE"`
	Session   string      `json:"SESSION"`
	Operation string      `json:"OPERATION"`
	Parameter interface{} `json:"PARAMETER,omitempty"`
	Response  interface{} `json:"RESPONSE,omitempty"`
}

// 3.4.5.27.1
type GPSData struct {
	GPSStatus uint8
	Expand    uint8
	Real      uint8
	Longitude float64
	Latitude  float64
	Speed     float64
	Direction float64
	Altitude  int32
	Time      time.Time
}

func (g *GPSData) MarshalJSON() ([]byte, error) {
	var alias struct {
		GPSStatus uint8  `json:"V"`
		Longitude string `json:"J"`
		Latitude  string `json:"W"`
		Speed     uint   `json:"S"`
		Direction uint   `json:"C"`
		Altitude  int32  `json:"H"`
		Time      string `json:"T"`
	}

	alias.GPSStatus = g.GPSStatus
	alias.Longitude = fmt.Sprintf("%.6f", g.Longitude)
	alias.Latitude = fmt.Sprintf("%.6f", g.Latitude)
	alias.Speed = uint(g.Speed * 100)
	alias.Direction = uint(g.Direction * 100)
	alias.Altitude = g.Altitude
	alias.Time = g.Time.Format("20060102150405")

	return json.Marshal(alias)
}

func (g *GPSData) UnmarshalJSON(data []byte) (err error) {
	var alias struct {
		GPSStatus uint8  `json:"V"`
		Longitude string `json:"J"`
		Latitude  string `json:"W"`
		Speed     uint   `json:"S"`
		Direction uint   `json:"C"`
		Altitude  int32  `json:"H"`
		Time      string `json:"T"`
	}

	if err = json.Unmarshal(data, &alias); err != nil {
		return
	}

	g.GPSStatus = alias.GPSStatus

	if g.Longitude, err = strconv.ParseFloat(alias.Longitude, 64); err != nil {
		return fmt.Errorf("invalid longitude: %w", err)
	}

	if g.Latitude, err = strconv.ParseFloat(alias.Latitude, 64); err != nil {
		return fmt.Errorf("invalid latitude: %w", err)
	}

	g.Speed = float64(alias.Speed) / 100.0
	g.Direction = float64(alias.Direction) / 100.0
	g.Altitude = alias.Altitude
	g.Time, _ = time.Parse("20060102150405", alias.Time)

	return nil
}

type Package struct {
	Version        uint8
	EncryptionFlag bool
	CompressFlag   bool
	CSRCCount      uint8

	PayloadType PayloadType
	SSRC        SpecialPayloadType
	Reserved    uint64
	CSRC        [16]uint64

	GPS GPSData

	payloadLen uint64
	Payload    Message
	RawPayload []byte

	Accum []byte
}