package astiav

import (
	"testing"

	"github.com/stretchr/testify/require"
)

func TestFormatContext(t *testing.T) {
	fc1, err := globalHelper.inputFormatContext("video.mp4", nil)
	require.NoError(t, err)
	ss := fc1.Streams()
	require.Len(t, ss, 2)
	s1 := ss[0]

	require.Equal(t, int64(607664), fc1.BitRate())
	require.Equal(t, NewFormatContextCtxFlags(0), fc1.CtxFlags())
	require.Equal(t, int64(5013333), fc1.Duration())
	require.True(t, fc1.EventFlags().Has(FormatEventFlagMetadataUpdated))
	require.True(t, fc1.Flags().Has(FormatContextFlagAutoBsf))
	require.Equal(t, NewRational(24, 1), fc1.GuessFrameRate(s1, nil))
	require.Equal(t, NewRational(1, 1), fc1.GuessSampleAspectRatio(s1, nil))
	require.True(t, fc1.InputFormat().Flags().Has(IOFormatFlagNoByteSeek))
	require.Equal(t, IOContextFlags(0), fc1.IOFlags())
	require.Equal(t, int64(0), fc1.MaxAnalyzeDuration())
	require.Equal(t, "isom", fc1.Metadata().Get("major_brand", nil, NewDictionaryFlags()).Value())
	require.NotNil(t, fc1.PrivateData())
	require.Equal(t, int64(0), fc1.StartTime())
	require.Equal(t, 2, fc1.NbStreams())
	require.Len(t, fc1.Streams(), 2)
	cl := fc1.Class()
	require.NotNil(t, cl)
	require.Equal(t, "AVFormatContext", cl.Name())

	sdp, err := fc1.SDPCreate()
	require.NoError(t, err)
	require.Equal(t, "v=0\r\no=- 0 0 IN IP4 127.0.0.1\r\ns=Big Buck Bunny\r\nt=0 0\r\na=tool:libavformat 61.1.100\r\nm=video 0 RTP/AVP 96\r\nb=AS:441\r\na=rtpmap:96 H264/90000\r\na=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z0LADasgKDPz4CIAAAMAAgAAAwBhHihUkA==,aM48gA==; profile-level-id=42C00D\r\na=control:streamid=0\r\nm=audio 0 RTP/AVP 97\r\nb=AS:161\r\na=rtpmap:97 MPEG4-GENERIC/48000/2\r\na=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=1190\r\na=control:streamid=1\r\n", sdp)

	SetLogLevel(LogLevelInfo)
	var dump string
	SetLogCallback(func(c Classer, l LogLevel, f, msg string) { dump += msg })
	fc1.Dump(0, "video.mp4", false)
	require.Equal(t, "Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'video.mp4':\n  Metadata:\n    major_brand     : isom\n    minor_version   : 512\n    compatible_brands: isomiso2avc1mp41\n    title           : Big Buck Bunny\n    artist          : Blender Foundation\n    composer        : Blender Foundation\n    date            : 2008\n    encoder         : Lavf58.12.100\n  Duration: 00:00:05.01, start: 0.000000, bitrate: 607 kb/s\n  Stream #0:0[0x1](und): Video: h264 (Extended) (avc1 / 0x31637661), yuv420p(progressive), 320x180 [SAR 1:1 DAR 16:9], 441 kb/s, 24 fps, 24 tbr, 12288 tbn (default)\n      Metadata:\n        handler_name    : VideoHandler\n        vendor_id       : [0][0][0][0]\n  Stream #0:1[0x2](und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 161 kb/s (default)\n      Metadata:\n        handler_name    : SoundHandler\n        vendor_id       : [0][0][0][0]\n", dump)
	ResetLogCallback()

	_, _, err = fc1.FindBestStream(MediaTypeUnknown, -1, -1)
	require.Error(t, err)
	s2, c1, err := fc1.FindBestStream(MediaTypeVideo, -1, -1)
	require.NoError(t, err)
	require.Equal(t, s1.Index(), s2.Index())
	require.Equal(t, s1.CodecParameters().CodecID(), c1.ID())
	s2, c1, err = fc1.FindBestStream(MediaTypeAudio, 1, 0)
	require.NoError(t, err)
	require.Equal(t, 1, s2.Index())

	fc2, err := AllocOutputFormatContext(nil, "mp4", "")
	require.NoError(t, err)
	defer fc2.Free()
	require.True(t, fc2.OutputFormat().Flags().Has(IOFormatFlagGlobalheader))

	fc3 := AllocFormatContext()
	require.NotNil(t, fc3)
	defer fc3.Free()
	io, err := OpenIOContext("testdata/video.mp4", NewIOContextFlags(IOContextFlagRead), nil, nil)
	require.NoError(t, err)
	defer io.Close() //nolint:errcheck
	fc3.SetPb(io)
	fc3.SetStrictStdCompliance(StrictStdComplianceExperimental)
	fc3.SetFlags(NewFormatContextFlags(FormatContextFlagAutoBsf))
	require.NotNil(t, fc3.Pb())
	require.Equal(t, StrictStdComplianceExperimental, fc3.StrictStdCompliance())
	require.True(t, fc3.Flags().Has(FormatContextFlagAutoBsf))
	s3 := fc3.NewStream(nil)
	require.NotNil(t, s3)
	s4 := fc3.NewStream(nil)
	require.NotNil(t, s4)
	require.Equal(t, 1, s4.Index())

	d := NewDictionary()
	d.Set("k", "v", 0)
	fc3.SetMetadata(d)
	e := fc3.Metadata().Get("k", nil, 0)
	require.NotNil(t, e)
	require.Equal(t, "v", e.Value())

	fc3.SetMetadata(nil)
	require.Nil(t, fc3.Metadata())

	fc4 := AllocFormatContext()
	require.NotNil(t, fc4)
	defer fc4.Free()
	ii1 := NewIOInterrupter()
	defer ii1.Free()
	fc4.SetIOInterrupter(ii1)
	ii1.Interrupt()
	require.ErrorIs(t, fc4.OpenInput("testdata/video.mp4", nil, nil), ErrExit)

	fc9 := AllocFormatContext()
	require.NotNil(t, fc9)
	defer fc9.Free()
	ii2 := NewIOInterrupter()
	defer ii2.Free()
	fc9.SetIOInterrupter(ii2)
	fc9.SetIOInterrupter(nil)
	require.NoError(t, fc4.OpenInput("testdata/video.mp4", nil, nil))

	fc5 := AllocFormatContext()
	require.NotNil(t, fc5)
	defer fc5.Free()
	require.NotNil(t, fc5.NewProgram(1))
	require.Equal(t, 1, fc5.NbPrograms())
	ps := fc5.Programs()
	require.Equal(t, 1, len(ps))
	require.Equal(t, 1, ps[0].ID())

	fc6 := AllocFormatContext()
	require.NotNil(t, fc6)
	defer fc6.Free()
	require.NoError(t, fc6.OpenInput("testdata/video.mp4", nil, nil))
	require.NoError(t, fc6.FindStreamInfo(nil))
	require.Equal(t, 2, fc6.NbStreams())
	pkt1 := AllocPacket()
	require.NotNil(t, pkt1)
	defer pkt1.Free()
	require.NoError(t, fc6.ReadFrame(pkt1))
	require.Equal(t, int64(48), pkt1.Pos())
	pkt2 := AllocPacket()
	require.NotNil(t, pkt2)
	defer pkt2.Free()
	require.NoError(t, fc6.ReadFrame(pkt2))
	require.Equal(t, int64(261), pkt2.Pos())
	require.NoError(t, fc6.SeekFrame(0, 0, NewSeekFlags().Add(SeekFlagBackward)))
	require.NoError(t, fc6.ReadFrame(pkt1))
	require.Equal(t, int64(48), pkt1.Pos())

	const outputPath = "tmp/test-format-context-output.mp4"
	fc7, err := AllocOutputFormatContext(nil, "", outputPath)
	require.NoError(t, err)
	defer fc7.Free()
	for _, is := range fc6.Streams() {
		os := fc7.NewStream(nil)
		require.NotNil(t, os)
		require.NoError(t, is.CodecParameters().Copy(os.CodecParameters()))
	}
	ic, err := OpenIOContext(outputPath, NewIOContextFlags(IOContextFlagWrite), nil, nil)
	require.NoError(t, err)
	fc7.SetPb(ic)
	require.NoError(t, fc7.WriteHeader(nil))
	require.NoError(t, fc7.WriteFrame(pkt1))
	require.NoError(t, fc7.WriteInterleavedFrame(pkt2))
	require.NoError(t, fc7.WriteTrailer())
	require.NoError(t, fc7.Flush())
	fc8 := AllocFormatContext()
	require.NotNil(t, fc8)
	defer fc8.Free()
	require.NoError(t, fc8.OpenInput(outputPath, nil, nil))
	require.NoError(t, fc8.FindStreamInfo(nil))
	require.Equal(t, 2, fc8.NbStreams())
	pkt3 := AllocPacket()
	require.NotNil(t, pkt3)
	defer pkt3.Free()
	require.NoError(t, fc8.ReadFrame(pkt3))
	require.Equal(t, int64(48), pkt3.Pos())
	pkt4 := AllocPacket()
	require.NotNil(t, pkt4)
	defer pkt4.Free()
	require.NoError(t, fc8.ReadFrame(pkt4))
	require.Equal(t, int64(261), pkt4.Pos())
}
