// Copyright 2024 Martin Riedl
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// 	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gomp4

import (
	"encoding/binary"
	"fmt"
)

// DataReferenceBox data reference box struct
//
// 8.7.2 Data Reference Box
//
// Box Types: ‘dref’
// Container: Data Information Box (‘dinf’)
// Mandatory: Yes
// Quantity: Exactly one
//
// The data reference object contains a table of data references (normally URLs) that declare the
// location(s) of the media data used within the presentation. The data reference index in the sample
// description ties entries in this table to the samples in the track. A track may be split over several
// sources in this way.
//
// If the flag is set indicating that the data is in the same file as this box, then no string (not even an empty
// one) shall be supplied in the entry field.
//
// The entry_count in the DataReferenceBox shall be 1 or greater; each DataEntryBox within the
// DataReferenceBox shall be either a DataEntryUrnBox or a DataEntryUrlBox.
//
// When a file that has data entries with the flag set indicating that the media data is in the same file, is
// split into segments for transport, the value of this flag does not change, as the file is (logically)
// reassembled after the transport operation.
type DataReferenceBox struct {
	*FullBox
	// EntryCount is an integer that counts the actual entries
	EntryCount uint32
	ChildBoxes []any
}

// BoxTypeDataReference Data Reference Box
const BoxTypeDataReference = "dref"

func init() {
	BoxDefinitions = append(BoxDefinitions, BoxDefinition{
		Type:        BoxTypeDataReference,
		ParentTypes: []string{BoxTypeDataInformation},
		Parser:      ParseDataReferenceBox,
	})
}

// ParseDataReferenceBox creates a new data reference box struct based on bytes
func ParseDataReferenceBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) {
	box := &DataReferenceBox{
		FullBox: newFullBox(&Box{filePosition, headerSize}, content[0:4]),
	}

	// parse entry counter
	box.EntryCount = binary.BigEndian.Uint32(content[4:8])

	// parse child boxes
	var err error
	box.ChildBoxes, err = box.parseChildBoxes(parser, BoxTypeDataReference, filePosition, content[8:])
	if err != nil {
		return box, err
	}

	// validate entries amount
	if len(box.ChildBoxes) != int(box.EntryCount) {
		return box, fmt.Errorf("invalid amount of boxes at %d", filePosition)
	}

	return box, err
}

// DataEntryUrlBox data entry URL box struct
//
// Box Types: ‘url ‘
// Container: Data Information Box (‘dref’)
// Mandatory: Yes (at least one of ‘url ‘ or ‘urn ‘ shall be present)
// Quantity: One or more
type DataEntryUrlBox struct {
	*FullBox
	// Location is a URL, and is required in a URL entry and optional in a URN entry, where it gives a location to find
	// the resource with the given name. If the
	// self‐contained flag is set, the URL form is used and no string is present; the box terminates with
	// the entry‐flags field. The URL type should be of a service that delivers a file (e.g. URLs of type file,
	// http, ftp etc.), and which services ideally also permit random access. Relative URLs are
	// permissible and are relative to the file containing the Movie Box that contains this data
	// reference.
	Location string
}

// BoxTypeDataEntryUrl Data Entry URL Box
const BoxTypeDataEntryUrl = "url "

func init() {
	BoxDefinitions = append(BoxDefinitions, BoxDefinition{
		Type:        BoxTypeDataEntryUrl,
		ParentTypes: []string{BoxTypeDataReference},
		Parser:      ParseDataEntryUrlBox,
	})
}

// ParseDataEntryUrlBox creates a data entry URL box struct based on bytes
func ParseDataEntryUrlBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) {
	box := &DataEntryUrlBox{
		FullBox: newFullBox(&Box{filePosition, headerSize}, content[0:4]),
	}

	box.Location, _ = ParseStringNullTerminated(content[4:])

	return box, nil
}

// DataEntryUrnBox data entry URN box struct
//
// Box Types: ‘urn ‘
// Container: Data Information Box (‘dref’)
// Mandatory: Yes (at least one of ‘url ‘ or ‘urn ‘ shall be present)
// Quantity: One or more
type DataEntryUrnBox struct {
	*FullBox
	// Name is a URN, and is required in a URN entry.
	Name string
	// Location is a URL, and is required in a URL entry and optional in a URN entry, where it gives a location to find
	// the resource with the given name. If the
	// self‐contained flag is set, the URL form is used and no string is present; the box terminates with
	// the entry‐flags field. The URL type should be of a service that delivers a file (e.g. URLs of type file,
	// http, ftp etc.), and which services ideally also permit random access. Relative URLs are
	// permissible and are relative to the file containing the Movie Box that contains this data
	// reference.
	Location string
}

// BoxTypeDataEntryUrn Data Entry URN Box
const BoxTypeDataEntryUrn = "urn "

func init() {
	BoxDefinitions = append(BoxDefinitions, BoxDefinition{
		Type:        BoxTypeDataEntryUrn,
		ParentTypes: []string{BoxTypeDataReference},
		Parser:      ParseDataEntryUrnBox,
	})
}

// ParseDataEntryUrnBox creates a data entry URN box struct based on bytes
func ParseDataEntryUrnBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) {
	box := &DataEntryUrnBox{
		FullBox: newFullBox(&Box{filePosition, headerSize}, content[0:4]),
	}

	name, endIndex := ParseStringNullTerminated(content[4:])
	box.Name = name
	box.Location, _ = ParseStringNullTerminated(content[4+endIndex:])

	return box, nil
}
