/* 
 * Copyright (c)2009-2025 DemiVision, LLC. All Rights Reserved. The information 
 * herein is the CONFIDENTIAL and PROPRIETARY information of DemiVision, LLC. 
 */

using DXLib.UI;
using SkiaSharp;

using Syncfusion.Pdf;
using Syncfusion.Pdf.Graphics;

using DXLib.Utils;

using PointF = Syncfusion.Drawing.PointF;

namespace iStatVball3;

/*
 * Provides static utility methods for drawing text, images, and shapes into a PDF document. Helper methods are also provided.
 */
public static class PDFUtils
{
	/* Constants */
	public const float Spacing = 2;

	// Available fonts
	public const string RegularFont = "Roboto-Regular";
	public const string BoldFont = "Roboto-Bold";
	public const string NumberFont = "Oswald-Regular";
	
	/* Get */

	// Returns width (excluding margins) of specified PDF page
	public static float GetWidth( PdfPage page )
	{
		return page.GetClientSize().Width;
	}

	// Converts from XF to PDF color
	public static PdfColor GetColor( Color color )
	{
		return new PdfColor( (byte)(color.Red * 255), (byte)(color.Green * 255), (byte)(color.Blue * 255) );
	}

	// Converts from SkiaSharp to PDF color
	public static PdfColor GetColor( SKColor color )
	{
		return GetColor( Color.FromArgb( color.ToString() ) );
	}

	// Create PDF solid color brush for drawing/filling
	public static PdfBrush GetBrush( Color color )
	{
		return new PdfSolidBrush( GetColor( color ) );
	}

	// Returns PDF font from specified file
	public static async Task<PdfFont> GetFont( string file, float size )
	{
		Stream stream = await DXResource.OpenFile( $"Fonts/{file}.ttf" );
		
		return new PdfTrueTypeFont( stream, size );
	}

	// Returns PDF bitmap from specified image file
	public static async Task<PdfBitmap> GetImage( string file )
	{
		Stream stream = await DXResource.OpenFile( $"Images/{file}.png" );
		
		return new PdfBitmap( stream );
	}
	
	/* Draw */

	// Draws specified text string into PDF page
	public static async Task<float> DrawText( PdfPage page, string text, string font, float size, PdfColor color, float x, float y )
	{
		// Define font/color
		PdfFont pdfFont = await GetFont( font, size );
		PdfBrush brush = new PdfSolidBrush( color );

		// Draw
		PdfTextElement element = new( text, pdfFont, brush );
		PdfLayoutResult result = element.Draw( page, new PointF( x, y ) );

		return result.Bounds.Bottom;
	}

	// Draws image from resource file into PDF page at specified location
	public static async Task<float> DrawImage( PdfPage page, string file, float x, float y )
	{
		// Load resource
		PdfBitmap image = await GetImage( file );

		// Draw
		PdfLayoutResult result = image.Draw( page, new PointF( x, y ) );

		return result.Bounds.Bottom;
	}

	// Draws outlined rectangle into PDF page at specified location
	public static float DrawRectangle( PdfPage page, PdfColor color, float weight, float x, float y, float wd, float ht )
	{
		// Define color
		PdfBrush brush = new PdfSolidBrush( color );

		// Draw vertical lines (left/right)
		PdfRectangle rectangle = new( brush, weight, ht );

		rectangle.Draw( page, x, y );
		rectangle.Draw( page, (x + wd), (y + 1) );

		// Draw horizontal lines (top/bottom)
		rectangle = new PdfRectangle( brush, (wd + weight), weight );

		rectangle.Draw( page, x, y );
		rectangle.Draw( page, x, (y + ht) );

		return (y + ht);
	}

	// Draws filled rectangle into PDF page at specified location
	public static float FillRectangle( PdfPage page, PdfColor color, float x, float y, float wd, float ht )
	{
		// Define color
		PdfSolidBrush brush = new( color );

		// Draw
		PdfRectangle rectangle = new( brush, wd, ht );
		PdfLayoutResult result = rectangle.Draw( page, x, y );

		return result.Bounds.Bottom;
	}

	// Draws filled circle into PDF page at specified location
	public static float FillCircle( PdfPage page, PdfColor color, float x, float y, float radius )
	{
		// Define color
		PdfSolidBrush brush = new( color );

		float diameter = (radius * 2);

		float x1 = (x - radius);
		float y1 = (y - radius);

		// Draw
		PdfEllipse circle = new( brush, diameter, diameter );
		PdfLayoutResult result = circle.Draw( page, x1, y1 );

		return result.Bounds.Bottom;
	}

	// Draws straight line into PDF page between specified endpoints
	public static void DrawLine( PdfPage page, PdfColor color, float weight, float x1, float y1, float x2, float y2 )
	{
		// Define color
		PdfSolidBrush brush = new( color );

		// Create stroke
		PdfPen pen = new( brush, weight );

		PointF pt1 = new( x1, y1 );
		PointF pt2 = new( x2, y2 );

		// Draw
		page.Graphics.DrawLine( pen, pt1, pt2 );
	}

	// PDF version of DXGraphics.DrawArrow(). Draws an arrow between any two points on a PDF page.
	//   page	  PDF page being drawing into
	//   color	  paint color
	//   weight	  arrow shaft thickness
	//   from	  x,y origin
	//   to		  x,y destination
	//   size	  arrow head length
	//   radius1  radius of src circle (0 if pt)
	//   radius2  radius of dest circle (0 if pt)
	//   gap	  (optional) space between arrow head and dest circle
	//   min	  (optional) minimum length to be drawn
	//
	public static void DrawArrow( PdfPage page, PdfColor color, float weight, float x1, float y1, float x2, float y2, float size, float radius1, float radius2, float gap = 0, float min = 0 )
	{
		// Connecting outside edges of two circles rather than from pt to pt
		if ( radius1 > 0 )
		{
			float dist1 = DXGraphics.Distance( x1, y1, x2, y2 );

			x1 += radius1 * ((x2 - x1) / dist1);
			y1 += radius1 * ((y2 - y1) / dist1);
		}

		if ( radius2 > 0 )
		{
			float dist2 = DXGraphics.Distance( x2, y2, x1, y1 );
			float gap2 = (gap * 2);

			x2 += (radius2 + gap2) * ((x1 - x2) / dist2);
			y2 += (radius2 + gap2) * ((y1 - y2) / dist2);
		}

		// Do not draw arrow if less than minimum length
		if ( min > 0 )
		{
			float dist = DXGraphics.Distance( x1, y1, x2, y2 );

			if ( dist < min )
			{
				return;
			}
		}

		// Arrow shaft
		DrawLine( page, color, weight, x1, y1, x2, y2 );

		PdfPath path = new();

		float angle = (float) Math.Atan2( (y1 - y2), (x1 - x2) );
		float theta = DXGraphics.Theta;

		// Arrowhead corner 1
		float ax1 = (float) (x2 + size * Math.Cos( angle + theta ));
		float ay1 = (float) (y2 + size * Math.Sin( angle + theta ));

		path.AddLine( x2, y2, ax1, ay1 );

		// Arrowhead corner 2
		float ax2 = (float) (x2 + size * Math.Cos( angle - theta ));
		float ay2 = (float) (y2 + size * Math.Sin( angle - theta ));

		path.AddLine( ax1, ay1, ax2, ay2 );

		// Close triangle
		path.CloseFigure();

		// Required for pointed tip
		PdfPen pen = new( color, weight )
		{
			LineJoin = PdfLineJoin.Miter
		};

		PdfSolidBrush brush = new( color );

		// Draw
		page.Graphics.DrawPath( pen, brush, path );
	}
}

//
