// 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"

// FileTypeBox file type box struct
//
// 4.3 File Type Box
// Box Type: `ftyp’
// Container: File
// Mandatory: Yes
// Quantity: Exactly one (but see below)
//
// Files written to this version of this specification must contain a file‐type box. For compatibility with an
// earlier version of this specification, files may be conformant to this specification and not contain a file‐
// type box. Files with no file‐type box should be read as if they contained an FTYP box with
// Major_brand='mp41', minor_version=0, and the single compatible brand 'mp41'.
//
// A media‐file structured to this part of this specification may be compatible with more than one detailed
// specification, and it is therefore not always possible to speak of a single ‘type’ or ‘brand’ for the file. This
// means that the utility of the file name extension and Multipurpose Internet Mail Extension (MIME) type
// are somewhat reduced.
//
// This box must be placed as early as possible in the file (e.g. after any obligatory signature, but before
// any significant variable‐size boxes such as a Movie Box, Media Data Box, or Free Space). It identifies
// which specification is the ‘best use’ of the file, and a minor version of that specification; and also a set of
// other specifications to which the file complies. Readers implementing this format should attempt to
// read files that are marked as compatible with any of the specifications that the reader implements. Any
// incompatible change in a specification should therefore register a new ‘brand’ identifier to identify files
// conformant to the new specification.
//
// The minor version is informative only. It does not appear for compatible‐brands, and must not be used
// to determine the conformance of a file to a standard. It may allow more precise identification of the
// major specification, for inspection, debugging, or improved decoding.
//
// Files would normally be externally identified (e.g. with a file extension or mime type) that identifies the
// ‘best use’ (major brand), or the brand that the author believes will provide the greatest compatibility.
//
// This section of this specification does not define any brands. However, see subclause 6.3 below for brands for
// files conformant to the whole specification and not just this section. All file format brands
// defined in this specification are included in Annex E with a summary of which features they require.
type FileTypeBox struct {
	*Box
	// is a brand identifier
	MajorBrand string
	// is an informative integer for the minor version of the major brand
	MinorBrand uint32
	// is a list, to the end of the box, of brands
	CompatibleBrands []string
}

// BoxTypeFileType File Type Box
const BoxTypeFileType = "ftyp"

func init() {
	BoxDefinitions = append(BoxDefinitions, BoxDefinition{
		Type:        BoxTypeFileType,
		ParentTypes: []string{boxTypeParentFile},
		Parser:      ParseFileTypeBox,
	})
}

// ParseFileTypeBox creates new file type box based on bytes
func ParseFileTypeBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) {
	// create new box
	box := &FileTypeBox{
		Box: &Box{filePosition, headerSize},
	}

	// parse major brand
	majorBrandBytes := content[0:4]
	box.MajorBrand = string(majorBrandBytes)

	// parse minor brand
	box.MinorBrand = binary.BigEndian.Uint32(content[4:8])

	// parse brands
	for i := 8; i < len(content); i = i + 4 {
		data := content[i : i+4]
		brand := string(data)
		box.CompatibleBrands = append(box.CompatibleBrands, brand)
	}

	return box, nil
}
