using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Threading;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows;
using System.IO;

using sdkdef = YoseenPTZ.SDKDef;
using YoseenPTZ.ResourceDef;

namespace YoseenPTZ.CoreDef
{
    public class BaseCanvas : Canvas
    {
        protected readonly BaseCanvasD2D _D2D;
        protected readonly VisualCollection _visualList;
        protected readonly DrawingVisual _dvMain;

        public BaseCanvas()
        {
            _D2D = new BaseCanvasD2D();
            _visualList = new VisualCollection(this);
            _dvMain = new DrawingVisual();
            _visualList.Add(_dvMain);
        }

        #region canvas
        protected override int VisualChildrenCount
        {
            get
            {
                return _visualList.Count;
            }
        }

        protected override Visual GetVisualChild(int index)
        {
            return _visualList[index];
        }
        #endregion

        bool _isInited;
        public bool IsInited => _isInited;
        public int BeginUpdate(int width, int height)
        {
            if (_isInited) return sdkdef.EError.EError_InvalidState;
            bool needChange = !(_D2D.DataWidth == width && _D2D.DataHeight == height);
            if (needChange)
            {
                _D2D.Init(width, height);
                this.Width = _D2D.OutputWidth;
                this.Height = _D2D.OutputHeight;
            }
            _isInited = true;
            return 0;
        }

        public void EndUpdate()
        {
            if (!_isInited) return;

            _isInited = false;
            _D2D.PtrBfd = IntPtr.Zero;
            DrawingContext dc = _dvMain.RenderOpen();
            dc.Close();
        }

        public int UpdateData(IntPtr ptrBmp)
        {
            if (!_isInited) return sdkdef.EError.EError_InvalidState;
            _D2D.PtrBfd = ptrBmp;

            _D2D.wbImage.WritePixels(_D2D.wbSrcRect, _D2D.PtrBfd, _D2D.wbBufferSize, _D2D.wbStride);

            //
            DrawingContext dc = _dvMain.RenderOpen();
            dc.DrawImage(_D2D.wbImage, _D2D.RectImage);
            dc.Close();
            return 0;
        }

        #region save
        public unsafe int SaveFrame(string filename, bool withOverlay)
        {
            bool valid = _isInited && IntPtr.Zero != _D2D.PtrBfd && IntPtr.Zero != _D2D.saveBuffer;
            if (!valid) return sdkdef.EError.EError_InvalidState;
            string saveFilename = filename;
            bool saveWithOverlay = withOverlay;

            //coverImage
            uint coverImageSize = 0;
            try
            {
                JpegBitmapEncoder enc = new JpegBitmapEncoder();
                if (saveWithOverlay)
                {
                    RenderTargetBitmap rtb = _D2D.saveRtb;
                    rtb.Render(this);
                    enc.Frames.Add(BitmapFrame.Create(rtb));
                }
                else
                {
                    enc.Frames.Add(BitmapFrame.Create(_D2D.wbImage));
                }
                UnmanagedMemoryStream stream = new UnmanagedMemoryStream((byte*)_D2D.saveBuffer.ToPointer(),
                    _D2D.saveCoverSize, _D2D.saveCoverSize, FileAccess.Write);
                enc.Save(stream);
                coverImageSize = (uint)stream.Position;
                stream.Close();
                if (coverImageSize >= _D2D.saveCoverSize) coverImageSize = 0;
            }
            catch (Exception ex)
            {
                LogUtil.Error(ex.Message);
            }

            //
            IntPtr ptrFile = sdkdef.Win32Helper._wfopen(saveFilename, "wb");
            if (ptrFile == IntPtr.Zero) return sdkdef.EError.EError_FileOpen;
            sdkdef.Win32Helper.fwrite(_D2D.saveBuffer, coverImageSize, 1, ptrFile);
            sdkdef.Win32Helper.fclose(ptrFile);
            return 0;
        }

        #endregion
    }

    public class BaseCanvasD2D : IDisposable
    {
        //
        public int DataWidth;
        public int DataHeight;
        public int DataPixels;
        public ushort DataXMax;
        public ushort DataYMax;

        public WriteableBitmap wbImage;
        public Int32Rect wbSrcRect;
        public int wbBufferSize;
        public int wbStride;

        //
        public int OutputWidth;
        public int OutputHeight;
        public Rect RectImage;
        public double Scale;

        //
        public IntPtr PtrBfd;

        //save
        public IntPtr saveBuffer;
        public int saveCoverSize;
        public RenderTargetBitmap saveRtb;

        public BaseCanvasD2D()
        {
        }

        public void Dispose()
        {
            if (saveBuffer == IntPtr.Zero) return;
            sdkdef.Win32Helper.free(saveBuffer);
            saveBuffer = IntPtr.Zero;
        }

        public void Init(int dataWidth, int dataHeight)
        {
            bool needChange = !(DataWidth == dataWidth && DataHeight == dataHeight);
            if (!needChange) return;
            if (saveBuffer != IntPtr.Zero)
            {
                sdkdef.Win32Helper.free(saveBuffer);
                saveBuffer = IntPtr.Zero;
            }

            //
            DataWidth = dataWidth;
            DataHeight = dataHeight;
            DataPixels = dataWidth * dataHeight;
            DataXMax = (ushort)(dataWidth - 1); DataYMax = (ushort)(dataHeight - 1);

            wbImage = new WriteableBitmap(dataWidth, dataHeight, 96, 96, PixelFormats.Bgr32, null);
            wbSrcRect = new Int32Rect(0, 0, dataWidth, dataHeight);
            wbBufferSize = DataPixels * 4;
            wbStride = dataWidth * 4;

            //
            int dispWidth = dataWidth;
            int dispHeight = dataHeight;
            int dispPixels = dispWidth * dispHeight;
            OutputWidth = dispWidth; OutputHeight = dispHeight;
            Scale = 1.0;
            RectImage.X = 0; RectImage.Y = 0;
            RectImage.Width = dispWidth; RectImage.Height = dispHeight;

            //
            saveRtb = new RenderTargetBitmap(dispWidth, dispHeight, 96, 96, PixelFormats.Pbgra32);
            saveCoverSize = dispPixels * 4;
            int saveBufferSize = saveCoverSize;
            saveBuffer = sdkdef.Win32Helper.calloc(1, (uint)saveBufferSize);
            if (saveBuffer == IntPtr.Zero)
            {
                LogUtil.Error("no mem");
            }
        }
    }
}
