/*
 * This file is part of Afterwarp Framework (http://afterwarp.io).
 * Copyright (c) 2015 - 2025 Dr. Yuriy Kotsarenko. All rights reserved.
 *
 * This example source code is 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.
 */

// This example illustrates the following features:
//  * Drawing images and portions of thereof with 2D canvas, using multiple techniques, interpolation
//    filters and effects.

namespace ImageBasics
{
  using System.Numerics;
  using System.Windows.Forms;
  using Afterwarp;
  using TextRenderer = Afterwarp.TextRenderer;
  using Application = System.Windows.Forms.Application;

  public partial class MainForm : Form
  {
    private Device device;
    private SwapChain swapChain;
    private Canvas canvas;
    private TextRenderer textRenderer;
    private Texture sampleImage;
    private Vector2 displaySize;

    public MainForm()
    {
      InitializeComponent();
    }

    private void MainForm_Load(object sender, EventArgs e)
    {
      displaySize = new Vector2(ClientSize.Width, ClientSize.Height);
      try
      {
        device = new Device(DeviceTechnology.Direct3D);
        swapChain = new SwapChain(device, Handle, null, PixelFormat.Unknown, PixelFormat.Unknown, 4);
        canvas = new Canvas(device);
        textRenderer = new TextRenderer(canvas, new Point(512, 512), PixelFormat.LA8);
        sampleImage = new Texture(device, "../../../../Media/ForkLift.png");
      }
      catch (Exception ex)
      {
        MessageBox.Show(ex.Message, "Initialization failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
        Application.Exit();
      }
      SetStyle(ControlStyles.Opaque, true);
      SetStyle(ControlStyles.UserPaint, true);
      SetStyle(ControlStyles.AllPaintingInWmPaint, true);
      SetStyle(ControlStyles.DoubleBuffer, false);
    }

    private void MainForm_FormClosed(object sender, FormClosedEventArgs e)
    {
      sampleImage?.Dispose();
      textRenderer?.Dispose();
      canvas?.Dispose();
      swapChain?.Dispose();
      device?.Dispose();
    }

    private void MainForm_Resize(object sender, EventArgs e)
    {
      if (WindowState != FormWindowState.Minimized && device is not null)
      {
        displaySize = new Vector2(ClientSize.Width, ClientSize.Height);
        swapChain.Resize(new Point(displaySize));
        Invalidate();
      }
    }

    private void MainForm_Paint(object sender, PaintEventArgs e)
    {
      float scale = DeviceDpi / 96.0f;

      swapChain.Begin();
      try
      {
        device.Clear(DeviceClear.Color, new FloatColor(0xFFE1E2E6u));

        canvas.Begin();
        try
        {
          canvas.ContextState = CanvasContextState.FlatScene;

          // Enable cubic interpolation for drawing image at its full size.
          canvas.Attributes = Canvas.Attribute.Cubic;
          canvas.SamplerState = new CanvasSamplerState(TextureFilter.Linear, TextureFilter.Linear);

          Vector2 imageSize, position;
          Quad quad;
          { // Draw image at its full size.
            TextureParameters parameters = sampleImage.Parameters;

            imageSize = new Vector2(parameters.Width * scale, parameters.Height * scale);
            position = (displaySize - imageSize) * 0.5f;
            quad = new Quad(new RectF(position, imageSize));

            canvas.Quad(sampleImage, quad, null, null, BlendingEffect.Copy);
          }
          canvas.Attributes = 0; // Disable cubic interpolation.
          { // Magnification samples.
            RectF subRect = RectF.Bounds(201.0f, 184.0f, 235.0f, 210.0f);
            float magnification = 8.0f * scale;
            float angle = MathF.PI * -0.15f;
            Quad texCoords = new(subRect);

            // Draw rectangle to show which portion of parrot is going to be magnified.
            canvas.FrameRect(position.X + subRect.Left * scale, position.Y + subRect.Top * scale,
              subRect.Width * scale, subRect.Height * scale, ColorRect.White, 2.0f * scale,
              BlendingEffect.Copy);

            // Nearest filtering...
            canvas.SamplerState = new CanvasSamplerState(TextureFilter.Nearest, TextureFilter.Nearest);

            position = new Vector2(displaySize.X * 0.85f, displaySize.Y * 0.2f);
            quad = Quad.Rotated(position, subRect.Size, angle, magnification);

            canvas.QuadRegion(sampleImage, quad, null, texCoords, BlendingEffect.Copy);

            // Linear filtering...
            canvas.SamplerState = new CanvasSamplerState(TextureFilter.Linear, TextureFilter.Linear);

            position = new Vector2(displaySize.X * 0.85f, displaySize.Y * 0.5f);
            quad = Quad.Rotated(position, subRect.Size, angle, magnification);

            canvas.QuadRegion(sampleImage, quad, null, texCoords, BlendingEffect.Copy);

            // Linear + cubic filtering...
            canvas.Attributes = Canvas.Attribute.Cubic;

            position = new Vector2(displaySize.X * 0.85f, displaySize.Y * 0.8f);
            quad = Quad.Rotated(position, subRect.Size, angle, magnification);

            canvas.QuadRegion(sampleImage, quad, null, texCoords, BlendingEffect.Copy);
          }
          canvas.Attributes = Canvas.Attribute.ColorAdjust;
          canvas.SamplerState = new CanvasSamplerState(TextureFilter.Linear, TextureFilter.Linear);
          { // Color manipulation samples.
            RectF subRect = RectF.Bounds(71.0f, 49.0f, 265.0f, 275.0f);
            float magnification = 1.4f * scale;
            float angle = MathF.PI * 0.1f;
            Quad texCoords = new(subRect);

            position = new Vector2(displaySize.X * 0.15f, displaySize.Y * 0.17f);
            quad = Quad.Rotated(position, subRect.Size, angle, magnification);

            canvas.QuadRegion(sampleImage, quad, new ColorRect(0xFFAF7F7Fu), texCoords,
              BlendingEffect.Copy);

            position = new Vector2(displaySize.X * 0.15f, displaySize.Y * 0.5f);
            quad = Quad.Rotated(position, subRect.Size, angle, magnification);

            canvas.QuadRegion(sampleImage, quad, new ColorRect(0xFF7F107Fu), texCoords,
              BlendingEffect.Copy);

            position = new Vector2(displaySize.X * 0.15f, displaySize.Y * 0.83f);
            quad = Quad.Rotated(position, subRect.Size, angle, magnification);

            canvas.QuadRegion(sampleImage, quad, new ColorRect(0xFF3F7F7Fu), texCoords,
              BlendingEffect.Copy);
          }
          canvas.Attributes = 0;
          canvas.SamplerState = new CanvasSamplerState(TextureFilter.Nearest, TextureFilter.Nearest);
          canvas.ContextState = CanvasContextState.FlatText;
          {
            FontParameters parameters = new("Segoe UI", 16.0f * scale, FontWeight.Bold);
            parameters.Effect.BorderType = FontBorder.SemiHeavy;
            parameters.Effect.ShadowOpacity = 1.0f;
            textRenderer.Parameters = parameters;

            Vector2 offset = (new Vector2(-50.0f, -90.0f)) * scale;

            position = (new Vector2(displaySize.X * 0.93f, displaySize.Y * 0.2f)) + offset;
            textRenderer.DrawAligned(position, "Nearest", new ColorPair(0xFF325DFFu, 0xFFC5DCFFu));

            position = (new Vector2(displaySize.X * 0.93f, displaySize.Y * 0.5f)) + offset;
            textRenderer.DrawAligned(position, "Linear", new ColorPair(0xFF51F700u, 0xFFA7FFC5u));

            position = (new Vector2(displaySize.X * 0.93f, displaySize.Y * 0.8f)) + offset;
            textRenderer.DrawAligned(position, "Cubic", new ColorPair(0xFF34B9FFu, 0xFF93FFF6u));

            offset = (new Vector2(70.0f, -110.0f)) * scale;

            position = (new Vector2(displaySize.X * 0.1f, displaySize.Y * 0.17f)) + offset;
            textRenderer.DrawAligned(position, "Positive hue shift",
              new ColorPair(0xFF75C916u, 0xFF9CFADCu));

            position = (new Vector2(displaySize.X * 0.1f, displaySize.Y * 0.5f)) + offset;
            textRenderer.DrawAligned(position, "Chroma reduction",
              new ColorPair(0xFF26A1FFu, 0xFF62EAFFu));

            position = (new Vector2(displaySize.X * 0.1f, displaySize.Y * 0.83f)) + offset;
            textRenderer.DrawAligned(position, "Negative hue shift",
              new ColorPair(0xFF3318D6u, 0xFFFDC3E7u));
          }
          { // Draw technology string.
            FontParameters parameters = new("Segoe UI", 16.0f * scale, FontWeight.SemiBold);
            parameters.Effect.BorderType = FontBorder.SemiHeavy;
            parameters.Effect.BorderBrightness = 1.0f;
            parameters.Effect.FillBrightness = 0.0f;
            parameters.Effect.ShadowBrightness = 0.75f;
            parameters.Effect.ShadowOpacity = 1.0f;
            textRenderer.Parameters = parameters;

            textRenderer.DrawAligned(new Vector2(displaySize.X * 0.5f, displaySize.Y -
              2.0f * scale), string.Format("Direct3D ({0}.{1}), features: {2}.{3}",
              device.TechVersion >> 8, (device.TechVersion >> 4) & 0xF,
              device.TechFeatureVersion >> 8, (device.TechFeatureVersion >> 4) & 0xF),
              new ColorPair(0xA0FFFFFFu), TextAlignment.Middle, TextAlignment.End);
          }
        }
        finally
        {
          canvas.End();
        }
      }
      finally
      {
        swapChain.End();
      }
    }
  }
}