// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT

package webrtc

import (
	

	
)

// ICECandidate represents a ice candidate.
type ICECandidate struct {
	statsID        string
	Foundation     string           `json:"foundation"`
	Priority       uint32           `json:"priority"`
	Address        string           `json:"address"`
	Protocol       ICEProtocol      `json:"protocol"`
	Port           uint16           `json:"port"`
	Typ            ICECandidateType `json:"type"`
	Component      uint16           `json:"component"`
	RelatedAddress string           `json:"relatedAddress"`
	RelatedPort    uint16           `json:"relatedPort"`
	TCPType        string           `json:"tcpType"`
	SDPMid         string           `json:"sdpMid"`
	SDPMLineIndex  uint16           `json:"sdpMLineIndex"`
	extensions     string
}

// Conversion for package ice.
func newICECandidatesFromICE(
	 []ice.Candidate,
	 string,
	 uint16,
) ([]ICECandidate, error) {
	 := []ICECandidate{}

	for ,  := range  {
		,  := newICECandidateFromICE(, , )
		if  != nil {
			return nil, 
		}
		 = append(, )
	}

	return , nil
}

func newICECandidateFromICE( ice.Candidate,  string,  uint16) (ICECandidate, error) {
	,  := convertTypeFromICE(.Type())
	if  != nil {
		return ICECandidate{}, 
	}
	,  := NewICEProtocol(.NetworkType().NetworkShort())
	if  != nil {
		return ICECandidate{}, 
	}

	 := ICECandidate{
		statsID:       .ID(),
		Foundation:    .Foundation(),
		Priority:      .Priority(),
		Address:       .Address(),
		Protocol:      ,
		Port:          uint16(.Port()), //nolint:gosec // G115
		Component:     .Component(),
		Typ:           ,
		TCPType:       .TCPType().String(),
		SDPMid:        ,
		SDPMLineIndex: ,
	}

	.setExtensions(.Extensions())

	if .RelatedAddress() != nil {
		.RelatedAddress = .RelatedAddress().Address
		.RelatedPort = uint16(.RelatedAddress().Port) //nolint:gosec // G115
	}

	return , nil
}

// ToICE converts ICECandidate to ice.Candidate.
func ( ICECandidate) () ( ice.Candidate,  error) {
	 := .statsID
	switch .Typ {
	case ICECandidateTypeHost:
		 := ice.CandidateHostConfig{
			CandidateID: ,
			Network:     .Protocol.String(),
			Address:     .Address,
			Port:        int(.Port),
			Component:   .Component,
			TCPType:     ice.NewTCPType(.TCPType),
			Foundation:  .Foundation,
			Priority:    .Priority,
		}

		,  = ice.NewCandidateHost(&)
	case ICECandidateTypeSrflx:
		 := ice.CandidateServerReflexiveConfig{
			CandidateID: ,
			Network:     .Protocol.String(),
			Address:     .Address,
			Port:        int(.Port),
			Component:   .Component,
			Foundation:  .Foundation,
			Priority:    .Priority,
			RelAddr:     .RelatedAddress,
			RelPort:     int(.RelatedPort),
		}

		,  = ice.NewCandidateServerReflexive(&)
	case ICECandidateTypePrflx:
		 := ice.CandidatePeerReflexiveConfig{
			CandidateID: ,
			Network:     .Protocol.String(),
			Address:     .Address,
			Port:        int(.Port),
			Component:   .Component,
			Foundation:  .Foundation,
			Priority:    .Priority,
			RelAddr:     .RelatedAddress,
			RelPort:     int(.RelatedPort),
		}

		,  = ice.NewCandidatePeerReflexive(&)
	case ICECandidateTypeRelay:
		 := ice.CandidateRelayConfig{
			CandidateID: ,
			Network:     .Protocol.String(),
			Address:     .Address,
			Port:        int(.Port),
			Component:   .Component,
			Foundation:  .Foundation,
			Priority:    .Priority,
			RelAddr:     .RelatedAddress,
			RelPort:     int(.RelatedPort),
		}

		,  = ice.NewCandidateRelay(&)
	default:
		return nil, fmt.Errorf("%w: %s", errICECandidateTypeUnknown, .Typ)
	}

	if  != nil &&  == nil {
		 = .exportExtensions()
	}

	return , 
}

func ( *ICECandidate) ( []ice.CandidateExtension) {
	var  string

	for  := range  {
		if  > 0 {
			 += " "
		}

		 += [].Key + " " + [].Value
	}

	.extensions = 
}

func ( *ICECandidate) ( ice.Candidate) error {
	 := .extensions
	var  ice.CandidateExtension
	var  string

	for ,  := 0, 0;  < len(); ++ {
		switch {
		case [] == ' ':
			 = [:]
			 =  + 1
		case  == len()-1:
			 = [:]
		default:
			continue
		}

		// Extension keys can't be empty
		 := .Key != ""
		if ! {
			.Key = 
		} else {
			.Value = 
		}

		// Extension value can be empty
		if  ||  == len()-1 {
			if  := .AddExtension();  != nil {
				return 
			}

			 = ice.CandidateExtension{}
		}
	}

	return nil
}

func convertTypeFromICE( ice.CandidateType) (ICECandidateType, error) {
	switch  {
	case ice.CandidateTypeHost:
		return ICECandidateTypeHost, nil
	case ice.CandidateTypeServerReflexive:
		return ICECandidateTypeSrflx, nil
	case ice.CandidateTypePeerReflexive:
		return ICECandidateTypePrflx, nil
	case ice.CandidateTypeRelay:
		return ICECandidateTypeRelay, nil
	default:
		return ICECandidateType(), fmt.Errorf("%w: %s", errICECandidateTypeUnknown, )
	}
}

func ( ICECandidate) () string {
	,  := .ToICE()
	if  != nil {
		return fmt.Sprintf("%#v failed to convert to ICE: %s", , )
	}

	return .String()
}

// ToJSON returns an ICECandidateInit
// as indicated by the spec https://w3c.github.io/webrtc-pc/#dom-rtcicecandidate-tojson
func ( ICECandidate) () ICECandidateInit {
	 := ""

	,  := .ToICE()
	if  == nil {
		 = .Marshal()
	}

	return ICECandidateInit{
		Candidate:     fmt.Sprintf("candidate:%s", ),
		SDPMid:        &.SDPMid,
		SDPMLineIndex: &.SDPMLineIndex,
	}
}