using DirectShowLib;
using System;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace VFWWinFormApp
{
    public partial class Form1 : Form
    {
        private IFilterGraph2 filterGraph;
        private IBaseFilter captureFilter;
        private IVideoWindow videoWindow;
        private string saveFolderPath = string.Empty;

        private int originalWidth = 0;
        private int originalHeight = 0;
        private float aspectRatio = 0f;

        public Form1()
        {
            InitializeComponent();
            UpdateCameraList();
        }

        private void UpdateCameraList()
        {
            cameraComboBox.Items.Clear();
            DsDevice[] devices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice);

            foreach (var device in devices)
            {
                cameraComboBox.Items.Add(device.Name);
            }

            if (cameraComboBox.Items.Count > 0)
            {
                cameraComboBox.SelectedIndex = 0;
            }
            else
            {
                MessageBox.Show("未检测到摄像头设备！");
            }
        }

        private void BtnActivateCamera_Click(object sender, EventArgs e)
        {
            try
            {
                if (cameraComboBox.SelectedIndex < 0)
                {
                    MessageBox.Show("请选择一个摄像头设备！");
                    return;
                }

                string selectedDevice = cameraComboBox.SelectedItem.ToString();
                ReleaseResources();
                filterGraph = (IFilterGraph2)new FilterGraph();

                var captureGraph = (ICaptureGraphBuilder2)new CaptureGraphBuilder2();
                captureGraph.SetFiltergraph(filterGraph);

                DsDevice[] devices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice);
                DsDevice device = Array.Find(devices, d => d.Name == selectedDevice);

                filterGraph.AddSourceFilterForMoniker(device.Mon, null, "Capture Filter", out captureFilter);

                captureGraph.FindInterface(PinCategory.Capture, Guid.Empty, captureFilter, typeof(IAMStreamConfig).GUID, out var streamConfigObj);

                if (streamConfigObj != null)
                {
                    var streamConfig = (IAMStreamConfig)streamConfigObj;
                    streamConfig.GetFormat(out var mediaType);

                    var vih = (VIDEOINFOHEADER)Marshal.PtrToStructure(mediaType.formatPtr, typeof(VIDEOINFOHEADER));
                    originalWidth = vih.bmiHeader.Width;
                    originalHeight = vih.bmiHeader.Height;
                    aspectRatio = (float)originalWidth / originalHeight;
                }

                var renderer = (IBaseFilter)new VideoRendererDefault();
                filterGraph.AddFilter(renderer, "Video Renderer");

                var captureOutputPin = GetPin(captureFilter, PinDirection.Output);
                var rendererInputPin = GetPin(renderer, PinDirection.Input);

                filterGraph.Connect(captureOutputPin, rendererInputPin);

                videoWindow = (IVideoWindow)filterGraph;
                videoWindow.put_Owner(previewBox.Handle);
                videoWindow.put_WindowStyle(WindowStyle.Child | WindowStyle.ClipChildren | WindowStyle.ClipSiblings);

                var mediaControl = (IMediaControl)filterGraph;
                mediaControl.Run();

                AdjustVideoWindow();
                MessageBox.Show("摄像头已激活！");
            }
            catch (Exception ex)
            {
                MessageBox.Show($"激活摄像头时发生错误: {ex.Message}");
            }
        }

        private IPin GetPin(IBaseFilter filter, PinDirection pinDirection)
        {
            filter.EnumPins(out var pinEnum);
            IPin[] pins = new IPin[1];

            while (pinEnum.Next(1, pins, IntPtr.Zero) == 0)
            {
                pins[0].QueryPinInfo(out PinInfo pinInfo);
                if (pinInfo.dir == pinDirection)
                {
                    return pins[0];
                }
                Marshal.ReleaseComObject(pins[0]);
            }
            return null;
        }

        private void BtnSelectFolder_Click(object sender, EventArgs e)
        {
            using (var dialog = new FolderBrowserDialog())
            {
                if (dialog.ShowDialog() == DialogResult.OK)
                {
                    saveFolderPath = dialog.SelectedPath;
                    lblFolderPath.Text = $"保存路径: {saveFolderPath}";
                }
            }
        }

        private void BtnCapture_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(saveFolderPath))
            {
                MessageBox.Show("请选择一个保存文件夹！");
                return;
            }

            try
            {
                Point screenPoint = previewBox.PointToScreen(Point.Empty);
                Bitmap bmp = new Bitmap(previewBox.Width, previewBox.Height);
                using (Graphics g = Graphics.FromImage(bmp))
                {
                    g.CopyFromScreen(screenPoint, Point.Empty, previewBox.Size);
                }

                string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
                string filename = Path.Combine(saveFolderPath, $"Capture_{timestamp}.bmp");
                bmp.Save(filename, System.Drawing.Imaging.ImageFormat.Bmp);
                MessageBox.Show($"截图已保存: {filename}");
            }
            catch (Exception ex)
            {
                MessageBox.Show($"保存截图时发生错误: {ex.Message}");
            }
        }

        private void ReleaseResources()
        {
            if (videoWindow != null)
            {
                videoWindow.put_Visible(OABool.False);
                videoWindow.put_Owner(IntPtr.Zero);
                videoWindow = null;
            }

            if (filterGraph != null)
            {
                Marshal.ReleaseComObject(filterGraph);
                filterGraph = null;
            }

            if (captureFilter != null)
            {
                Marshal.ReleaseComObject(captureFilter);
                captureFilter = null;
            }
        }

        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            ReleaseResources();
            base.OnFormClosing(e);
        }

        private void AdjustVideoWindow()
        {
            if (videoWindow == null || aspectRatio <= 0f)
                return;

            Point screenPosition = previewBox.PointToScreen(Point.Empty);
            int boxWidth = previewBox.Width;
            int boxHeight = previewBox.Height;

            float boxAspect = (float)boxWidth / boxHeight;
            int displayWidth, displayHeight;

            if (boxAspect > aspectRatio)
            {
                displayHeight = boxHeight;
                displayWidth = (int)(displayHeight * aspectRatio);
            }
            else
            {
                displayWidth = boxWidth;
                displayHeight = (int)(displayWidth / aspectRatio);
            }

            int offsetX = (boxWidth - displayWidth) / 2;
            int offsetY = (boxHeight - displayHeight) / 2;

            videoWindow.SetWindowPosition(
                screenPosition.X + offsetX,
                screenPosition.Y + offsetY,
                displayWidth,
                displayHeight);
            videoWindow.put_Visible(OABool.True);
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct VIDEOINFOHEADER
    {
        public RECT SrcRect;
        public RECT TargetRect;
        public int BitRate;
        public int BitErrorRate;
        public long AvgTimePerFrame;
        public BITMAPINFOHEADER bmiHeader;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct BITMAPINFOHEADER
    {
        public uint Size;
        public int Width;
        public int Height;
        public ushort Planes;
        public ushort BitCount;
        public uint Compression;
        public uint SizeImage;
        public int XPelsPerMeter;
        public int YPelsPerMeter;
        public uint ClrUsed;
        public uint ClrImportant;
    }
}