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

// MovieHeaderBox Movie Header Box struct
//
// 8.2.2 Movie Header Box
// Box Type: ‘mvhd’
// Container: Movie Box (‘moov’)
// Mandatory: Yes
// Quantity: Exactly one
//
// This box defines overall information which is media‐independent, and relevant to the entire
// presentation	considered as a whole.
type MovieHeaderBox struct {
	*FullBox
	// is an integer that declares the creation time of the presentation (in seconds
	// since midnight, Jan. 1, 1904, in UTC time)
	CreationTimeV0 time.Time
	// is an integer that declares the creation time of the presentation (in seconds
	// since midnight, Jan. 1, 1904, in UTC time)
	CreationTimeV1 time.Time
	// is an integer that declares the most recent time the presentation was
	// modified (in seconds since midnight, Jan. 1, 1904, in UTC time)
	ModificationTimeV0 time.Time
	// is an integer that declares the most recent time the presentation was
	// modified (in seconds since midnight, Jan. 1, 1904, in UTC time)
	ModificationTimeV1 time.Time
	// is an integer that specifies the time‐scale for the entire presentation; this is the
	// number of time units that pass in one second. For example, a time coordinate system that
	// measures time in sixtieths of a second has a time scale of 60.
	Timescale uint32
	// is an integer that declares length of the presentation (in the indicated timescale). This
	// property is derived from the presentation’s tracks: the value of this field corresponds to the
	// duration of the longest track in the presentation. If the duration cannot be determined then
	// duration is set to all 1s.
	DurationV0 uint32
	// is an integer that declares length of the presentation (in the indicated timescale). This
	// property is derived from the presentation’s tracks: the value of this field corresponds to the
	// duration of the longest track in the presentation. If the duration cannot be determined then
	// duration is set to all 1s.
	DurationV1 uint64
	// is a fixed point 16.16 number that indicates the preferred rate to play the presentation; 1.0
	// (0x00010000) is normal forward playback
	Rate Fixed1616
	// is a fixed point 8.8 number that indicates the preferred playback volume. 1.0 (0x0100) is
	// full volume.
	Volume Fixed88
	// provides a transformation matrix for the video; (u,v,w) are restricted here to (0,0,1), hex
	// values (0,0,0x40000000).
	Matrix []int32
	// is a non‐zero integer that indicates a value to use for the track ID of the next track
	// to be added to this presentation. Zero is not a valid track ID value. The value of
	// next_track_ID shall be larger than the largest track‐ID in use. If this value is equal to all 1s
	// (32‐bit maxint), and a new media track is to be added, then a search must be made in the file for
	// an unused track identifier.
	NextTrackID uint32
}

// BoxTypeMovieHeader Movie Header Box
const BoxTypeMovieHeader = "mvhd"

func init() {
	BoxDefinitions = append(BoxDefinitions, BoxDefinition{
		Type:        BoxTypeMovieHeader,
		ParentTypes: []string{BoxTypeMovie},
		Parser:      ParseMovieHeaderBox,
	})
}

// ParseMovieHeaderBox creates a new Movie Header Box struct
func ParseMovieHeaderBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) {
	box := &MovieHeaderBox{
		FullBox: newFullBox(&Box{filePosition, headerSize}, content[0:4]),
	}

	position := 4
	if box.Version == 1 {
		box.CreationTimeV1 = since1904ToTime(int64(binary.BigEndian.Uint64(content[4:12])))
		box.ModificationTimeV1 = since1904ToTime(int64(binary.BigEndian.Uint64(content[12:20])))
		box.Timescale = binary.BigEndian.Uint32(content[20:24])
		box.DurationV1 = binary.BigEndian.Uint64(content[24:32])
		position += 28
	} else { // version == 0
		box.CreationTimeV0 = since1904ToTime(int64(binary.BigEndian.Uint32(content[4:8])))
		box.ModificationTimeV0 = since1904ToTime(int64(binary.BigEndian.Uint32(content[8:12])))
		box.Timescale = binary.BigEndian.Uint32(content[12:16])
		box.DurationV0 = binary.BigEndian.Uint32(content[16:20])
		position += 16
	}

	box.Rate = NewFixed1616ByBytes(content[position : position+4])
	box.Volume = NewFixed88ByBytes(content[position+4 : position+6])
	position += 4 + 2 + 2 + 8 // 4 bytes for rate, 2 bytes for volume, 2 bytes reserved and 8 bytes reserved
	for i := 0; i < 9; i++ {
		box.Matrix = append(box.Matrix, int32(binary.BigEndian.Uint32(content[position:position+4])))
		position += 4
	}

	// skip 6*32 bit "pre-defined"
	position += 6 * 4
	box.NextTrackID = binary.BigEndian.Uint32(content[position : position+4])

	return box, nil
}
