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

// SampleToChunkBox sample to chunk box struct
//
// 8.7.4 Sample To Chunk Box
//
// Box Type: ‘stsc’
// Container: Sample Table Box (‘stbl’)
// Mandatory: Yes
// Quantity: Exactly one
//
// Samples within the media data are grouped into chunks. Chunks can be of different sizes, and the
// samples within a chunk can have different sizes. This table can be used to find the chunk that contains a
// sample, its position, and the associated sample description.
//
// The table is compactly coded. Each entry gives the index of the first chunk of a run of chunks with the
// same characteristics. By subtracting one entry here from the previous one, you can compute how many
// chunks are in this run. You can convert this to a sample count by multiplying by the appropriate
// samples‐per‐chunk.
type SampleToChunkBox struct {
	*FullBox
	// is an integer that gives the number of entries in the following table
	EntryCount uint32
	Entries    []SampleToChunkBoxEntry
}

type SampleToChunkBoxEntry struct {
	// is an integer that gives the index of the first chunk in this run of chunks that share
	// the same samples‐per‐chunk and sample‐description‐index; the index of the first chunk in a
	// track has the value 1 (the first_chunk field in the first record of this box has the value 1,
	// identifying that the first sample maps to the first chunk).
	FirstChunk uint32
	// is an integer that gives the number of samples in each of these chunks
	SamplesPerChunk uint32
	// is an integer that gives the index of the sample entry that
	// describes the samples in this chunk. The index ranges from 1 to the number of sample entries in
	// the Sample Description Box
	SampleDescriptionIndex uint32
}

// BoxTypeSampleToChunk Sample To Chunk Box
const BoxTypeSampleToChunk = "stsc"

func init() {
	BoxDefinitions = append(BoxDefinitions, BoxDefinition{
		Type:        BoxTypeSampleToChunk,
		ParentTypes: []string{BoxTypeSampleTable},
		Parser:      ParseSampleToChunkBox,
	})
}

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

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

	// parse entries
	position := 8
	for i := 0; i < int(box.EntryCount); i++ {
		newSampleEntry := SampleToChunkBoxEntry{
			FirstChunk:             binary.BigEndian.Uint32(content[position+(i*12) : position+4+(i*12)]),
			SamplesPerChunk:        binary.BigEndian.Uint32(content[position+4+(i*12) : position+8+(i*12)]),
			SampleDescriptionIndex: binary.BigEndian.Uint32(content[position+8+(i*12) : position+12+(i*12)]),
		}
		box.Entries = append(box.Entries, newSampleEntry)
	}

	// validate entries amount
	if len(box.Entries) != int(box.EntryCount) {
		return box, fmt.Errorf("invalid amount of entries at %d; got %d but expected %d", filePosition, len(box.Entries), box.EntryCount)
	}

	return box, nil
}
