using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using SharpDX;
using SharpDX.DXGI;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using SharpDX.Direct2D1;
using SharpDX.Windows;
using Device = SharpDX.Direct3D11.Device;
using FactoryD2D = SharpDX.Direct2D1.Factory;
using FactoryDXGI = SharpDX.DXGI.Factory1;

namespace Direct2DTest
{
   
    public partial class Form1 : Form
    {
        int testcounter = 0;
        int testbcounter = 0;
        int c,d;

        int[,] cloudcoord = new int [11,2];
        RenderTarget renderTarget;
        Random rnd;
   
        public Form1()
        {
            InitializeComponent();
            //Enable Timer
            timer1.Interval = 20; //50 FPS
            timer1.Enabled = false;           
            rnd = new Random();            
        }

        public void IdentityMatrix(ref SharpDX.Mathematics.Interop.RawMatrix3x2 b)
        {
            var a = new SharpDX.Mathematics.Interop.RawMatrix3x2();
            a.M11 = 1; a.M12 = 0; a.M21 = 0; a.M22 = 1; a.M31 = 0; a.M32 = 0;
            b = a;
        }

        public static SharpDX.Direct2D1.Bitmap LoadFromFileTransparent(RenderTarget renderTarget, string file, System.Drawing.Color cmatch)
        {
            // Loads from file using System.Drawing.Image
            using (var bitmap = (System.Drawing.Bitmap)System.Drawing.Image.FromFile(file))
            {
                var sourceArea = new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height);
                var bitmapProperties = new SharpDX.Direct2D1.BitmapProperties(new SharpDX.Direct2D1.PixelFormat(SharpDX.DXGI.Format.R8G8B8A8_UNorm, SharpDX.Direct2D1.AlphaMode.Premultiplied));
                var size = new Size2(bitmap.Width, bitmap.Height);

                // Transform pixels from BGRA to RGBA
                int stride = bitmap.Width * sizeof(int);
                using (var tempStream = new DataStream(bitmap.Height * stride, true, true))
                {
                    // Lock System.Drawing.Bitmap
                    var bitmapData = bitmap.LockBits(sourceArea, System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);

                    // Convert all pixels 
                    for (int y = 0; y < bitmap.Height; y++)
                    {
                        int offset = bitmapData.Stride * y;
                        for (int x = 0; x < bitmap.Width; x++)
                        {
                            // Not optimized 
                            byte B = Marshal.ReadByte(bitmapData.Scan0, offset++);
                            byte G = Marshal.ReadByte(bitmapData.Scan0, offset++);
                            byte R = Marshal.ReadByte(bitmapData.Scan0, offset++);
                            byte A = Marshal.ReadByte(bitmapData.Scan0, offset++);
                            if ((cmatch.R == R) && (cmatch.B == B) && (cmatch.G == G))
                            {
                                //Matched our color, make transparent.
                                R = 0; G = 0; B = 0; A = 0;
                            }

                            
                            int rgba = R | (G << 8) | (B << 16) | (A << 24);
                            tempStream.Write(rgba);
                        }

                    }
                    bitmap.UnlockBits(bitmapData);
                    tempStream.Position = 0;

                    return new SharpDX.Direct2D1.Bitmap(renderTarget, size, tempStream, stride, bitmapProperties);
                }
            }
        }

        private void button1_Click(object sender, EventArgs e)
        { //click
            //Blank out the clouds
            
            for (testcounter = 0; testcounter <= 10; testcounter++)
            {
                cloudcoord[testcounter, 0] = 0; cloudcoord[testcounter, 1] = 0;
            }
            


            // Create render target window
            var form = new RenderForm("Direct2D Demo");
            
            // Create swap chain description
            var swapChainDesc = new SwapChainDescription()
            {
                BufferCount = 2,
                Usage = Usage.RenderTargetOutput,
                OutputHandle = form.Handle,
                IsWindowed = true,
                ModeDescription = new ModeDescription(260, 260, new Rational(60, 1), Format.R8G8B8A8_UNorm),
                SampleDescription = new SampleDescription(1, 0),
                Flags = SwapChainFlags.AllowModeSwitch,  //SwapChainFlags.FrameLatencyWaitAbleObject,
                SwapEffect = SwapEffect.Discard
            };

            // Create swap chain and Direct3D device
            // The BgraSupport flag is needed for Direct2D compatibility otherwise RenderTarget.FromDXGI will fail!
            Device device;
            SwapChain swapChain;
            Device.CreateWithSwapChain(DriverType.Hardware, DeviceCreationFlags.BgraSupport, swapChainDesc, out device, out swapChain);

            // Get back buffer in a Direct2D-compatible format (DXGI surface)
            Surface backBuffer = Surface.FromSwapChain(swapChain, 0);          

            // Create Direct2D factory
            using (var factory = new FactoryD2D())
            {
                // Get desktop DPI
                var dpi = factory.DesktopDpi;                
                // Create bitmap render target from DXGI surface
                renderTarget = new RenderTarget(factory, backBuffer, new RenderTargetProperties()
                { //RenderTarget
                    DpiX = dpi.Width,
                    DpiY = dpi.Height,
                    MinLevel = SharpDX.Direct2D1.FeatureLevel.Level_DEFAULT,
                    PixelFormat = new SharpDX.Direct2D1.PixelFormat(SharpDX.DXGI.Format.R8G8B8A8_UNorm, SharpDX.Direct2D1.AlphaMode.Premultiplied),
                    Type = RenderTargetType.Default, 
                    Usage = RenderTargetUsage.None}); //RenderTarget              
                
            } //using factory

            renderTarget.AntialiasMode = SharpDX.Direct2D1.AntialiasMode.PerPrimitive; /* Not that this appears to effect anything. */
            // Disable automatic ALT+Enter processing because it doesn't work properly with WinForms
            using (var factory = swapChain.GetParent())
            {
                factory.MakeWindowAssociation(form.Handle, WindowAssociationFlags.IgnoreAltEnter);
            }
            // Add event handler for ALT+Enter
            form.KeyDown += (o, f) =>
            {
                if (f.Alt && f.KeyCode == Keys.Enter)
                    swapChain.IsFullScreen = !swapChain.IsFullScreen;
            };

            // Set window size
            form.Size = new System.Drawing.Size(260,260);

            // Prevent window from being re-sized
            form.AutoSizeMode = AutoSizeMode.GrowAndShrink;

            //Load our Bitmaps
            
            SharpDX.WIC.ImagingFactory imagingFactory = new SharpDX.WIC.ImagingFactory();
            SharpDX.IO.NativeFileStream fileStream = new SharpDX.IO.NativeFileStream("background.png",
                SharpDX.IO.NativeFileMode.Open, SharpDX.IO.NativeFileAccess.Read);

            SharpDX.WIC.BitmapDecoder BMDecode = new SharpDX.WIC.BitmapDecoder(imagingFactory, fileStream, SharpDX.WIC.DecodeOptions.CacheOnDemand);
            SharpDX.WIC.BitmapFrameDecode frame = BMDecode.GetFrame(0);

            SharpDX.WIC.FormatConverter converter = new SharpDX.WIC.FormatConverter(imagingFactory);
            converter.Initialize(frame, SharpDX.WIC.PixelFormat.Format32bppPRGBA);

            SharpDX.Direct2D1.Bitmap backBitmap = SharpDX.Direct2D1.Bitmap1.FromWicBitmap(renderTarget, converter);

            fileStream.Dispose(); imagingFactory.Dispose(); BMDecode.Dispose(); frame.Dispose(); converter.Dispose();
                       
            SharpDX.Direct2D1.Bitmap cloudBitmap = LoadFromFileTransparent(renderTarget, "cloud.png",Color.Magenta);
            c = (int)renderTarget.Size.Height - 50;
            d = (int)renderTarget.Size.Width;
            timer1.Enabled = true; //Now, we can turn on the timer.
            
            // Rendering function
            RenderLoop.Run(form, () =>
            {
                int a;
                var junkmatrix = new SharpDX.Mathematics.Interop.RawMatrix3x2();
                IdentityMatrix(ref junkmatrix);
                renderTarget.BeginDraw();
                renderTarget.Transform = junkmatrix;   //Matrix3x2.Identity;
                renderTarget.Clear(new SharpDX.Mathematics.Interop.RawColor4(128,128,128,255));

                renderTarget.DrawBitmap(
                   backBitmap,
                   new SharpDX.Mathematics.Interop.RawRectangleF(0,0,renderTarget.Size.Width,renderTarget.Size.Height),
                  1.0f,
                   SharpDX.Direct2D1.BitmapInterpolationMode.Linear,
                   new SharpDX.Mathematics.Interop.RawRectangleF(0, 0, 259,259)
                   );

                // put drawing code here (see below)

                /* Old demo code.
                var v1 = new SharpDX.Mathematics.Interop.RawVector2();
                var v2 = new SharpDX.Mathematics.Interop.RawVector2();

                using (var brush = new SolidColorBrush(renderTarget, new SharpDX.Mathematics.Interop.RawColor4(100, 120, 140, 255)))
                {
                  for (int x = 0; x < renderTarget.Size.Width; x += 10)
                    {
                        v1.X = x; v1.Y = 0;
                        v2.X = x; v2.Y = renderTarget.Size.Height;
                        renderTarget.DrawLine(v1, v2, brush, 0.5f);
                    } 
                    for (int y = 0; y < renderTarget.Size.Height; y += 10)
                    {
                        v1.X = 0; v1.Y = y;
                        v2.X = renderTarget.Size.Width; v2.Y = y;
                        renderTarget.DrawLine(v1, v2, brush, 0.5f);
                    } 
                    renderTarget.FillRectangle(new SharpDX.Mathematics.Interop.RawRectangleF(50,150, 100, 200), brush);
                
                    var MyText = new SharpDX.DirectWrite.Factory();                    
                    var MyTextFormat = new SharpDX.DirectWrite.TextFormat(MyText,"Arial",22);


                     renderTarget.DrawText("This is a test" + testcounter.ToString(), MyTextFormat, new SharpDX.Mathematics.Interop.RawRectangleF(150, 150, 350, 200), brush, 0, 0);                   
                } //using brush */
                              

                for (a = 0; a <= 10; a++)
                { // cloud loop
                    if (cloudcoord[a,0] > 0)
                    {
                        renderTarget.DrawBitmap(
                            cloudBitmap,
                            new SharpDX.Mathematics.Interop.RawRectangleF(cloudcoord[a,0],cloudcoord[a,1],cloudcoord[a,0] +38,cloudcoord[a,1]+28),
                            0.5f,
                            SharpDX.Direct2D1.BitmapInterpolationMode.Linear,
                            new SharpDX.Mathematics.Interop.RawRectangleF(0, 0, 38,28)
                            );
                    }

                }

                /*
                renderTarget.DrawRectangle(
                    new SharpDX.Mathematics.Interop.RawRectangleF(0,0, 200, 200),
                    new SolidColorBrush(renderTarget, new SharpDX.Mathematics.Interop.RawColor4(100, 160, 140, 0))); */


                renderTarget.EndDraw();
               
                swapChain.Present(0, PresentFlags.None);
            });
          

            //clean up
            renderTarget.Dispose();
            swapChain.Dispose();
            device.Dispose();
            
        }

        private void button2_Click(object sender, EventArgs e)
        {
            //testcounter = testcounter + 1;
            label1.Text = c.ToString() + "," + d.ToString(); // testbcounter.ToString();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            int a, b;
            
            if (testbcounter > 300) { testbcounter = 0; } else { testbcounter++; }
            /* Manipulate clouds here */

           

            for (a = 0; a <= 10; a++)
            {
                if (cloudcoord[a, 0] > d)
                { //Off the edge
                    cloudcoord[a, 0] = 0;
                }
                else
                {
                    if (cloudcoord[a, 0] != 0)
                    { //exists, just move it along
                        cloudcoord[a, 0]++;
                    }
                    else
                    {
                        //It does = 0
                        b = rnd.Next(1, 500);
                        if (b == 25)
                        { //spawn a new cloud
                            cloudcoord[a, 1] = rnd.Next(1, c);
                            cloudcoord[a, 0] = 1;
                        }
                    }

                } //else
            } //for

        } //click
    } //Form 1
} //Direct2DTest