
// SPDX-License-Identifier: CC-BY-NC-SA-4.0
//
// Copyright (C) 2026 Bit by Bit Signal Processing LLC (https://bxbsp.com)
//
// This work is placed under the "Creative Commons Attribution
// NonCommercial ShareAlike 4.0 International" license, known
// by the shortened acronym "CC-BY-NC-SA-4.0".
//
// This work is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// A CC-BY-NC-SA-4.0 license allows you to use this work for
// noncommercial purposes so long as attribution is made to the
// original author.  Modified versions of this work may be distributed,
// but only under the same license.  For further details, see the
// Creative Commons License "CC-BY-NC-SA-4.0".
//
// You should have received a copy of the CC-BY-NC-SA-4.0 license
// along with this work. If not, see
// <https://creativecommons.org/licenses/by-nc-sa/4.0/>.
//

#include "svg.hh"
#include "panel.hh"
#include <glib.h>
#include <librsvg/rsvg.h>

bool global_svg_dimensions_from_file(const char* filename, int& width, int& height)
{
  //RsvgDimensionData  dim;
  GError * gerror = 0;

  RsvgHandle *svgh = rsvg_handle_new_from_file(filename, &gerror); 

  if(!svgh)
    {
      printf("Couldn't create RSVG handle in global_svg_dimensions() from file.\n");
      return false;
    }
    
  //rsvg_handle_get_dimensions(svgh, &dim);
  //
  //width = dim.width;
  //height = dim.height;

  double dwidth, dheight;
  rsvg_handle_get_intrinsic_size_in_pixels(svgh, &dwidth, &dheight);

  width = ceil(dwidth);
  height = ceil(dheight);
    
  g_object_unref(svgh);

  return true;
}


bool global_draw_svg_from_file(panel* p, const char* filename, int offset_x, int offset_y, int draw_width, int draw_height)
{
  //RsvgDimensionData  dim;
  GError * gerror = 0;

  //RsvgHandle *svgh = rsvg_handle_new_from_data(svg_data, svg_data_length, &gerror);
  RsvgHandle *svgh = rsvg_handle_new_from_file(filename, &gerror); 

  if(!svgh)
    {
      printf("Couldn't create RSVG handle in global_draw_svg() from file.\n");
      return false;
    }
    
  //rsvg_handle_get_dimensions(svgh, &dim);

  double dwidth, dheight;
  int dim_width, dim_height;
  rsvg_handle_get_intrinsic_size_in_pixels(svgh, &dwidth, &dheight);

  dim_width = ceil(dwidth);
  dim_height = ceil(dheight);


  if(draw_width==0 && draw_height==0)
    {
      // Keep aspect ratio, but size as big as possible to fit the screen
      if(float(dim_width)/dim_height > float(p->width)/p->height)
	{
	  // Object is wider than panel in aspect.  Scale object to match width.

	  draw_width  = p->width;
	  draw_height = dim_height * p->width / dim_width;
	  offset_y += (p->height-draw_height)/2;
	}
      else
	{
	  // Object is narrower than panel in aspect.  Scale object to match height.
	  
	  draw_width  = dim_width * p->height / dim_height;
	  draw_height = p->height;
	  offset_x += (p->width-draw_width)/2;
	}
    }
  else if(draw_width==0)
    {
      // Keep aspect ratio, sizing width to match height
      draw_width = dim_width * p->height / dim_height;
      offset_x += (p->width-draw_width)/2;
    }
  else if(draw_height==0)
    {
      // Keep aspect ratio, sizing height to match width
      draw_height = dim_height * p->width / dim_width;
      offset_y += (p->height-draw_height)/2;
    }

  
  //  printf("SVG Dimensions are %dx%d\n", dim_width, dim_height);

  // CAIRO_FORMAT_RGB16_565
  // CAIRO_FORMAT_ARGB32
  cairo_surface_t* cs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, draw_width, draw_height);
  
  if(!cs)
    {
      printf("Couldn't create cairo surface.\n");
      g_object_unref(svgh);
      return false;
    }

  cairo_t *cr = cairo_create(cs);

  if(!cr)
    {
      printf("Couldn't create cairo renderer.\n");
      cairo_surface_finish(cs);
      cairo_surface_destroy(cs);
      g_object_unref(svgh);
      return false;
    }

  //cairo_translate(cr, 1, 1);
  cairo_scale(cr, float(draw_width)/dim_width, float(draw_height)/dim_height);
  
  RsvgRectangle viewport;
  viewport.x      = 0;
  viewport.y      = 0;
  viewport.width  = dim_width;
  viewport.height = dim_height;

  
  //rsvg_handle_render_cairo(svgh, cr);

  rsvg_handle_render_document(svgh, cr, &viewport, 0);

  
  cairo_surface_flush(cs);

  uint32_t* data = (uint32_t*) cairo_image_surface_get_data(cs);


  for(int x=0; x<draw_width; x++)
    for(int y=0; y<draw_height; y++)
      {
	uint32_t pixel_data = data[x+y*draw_width];
	uint32_t alpha = (pixel_data>>24) & 0xff;
	uint32_t red   = (pixel_data>>16) & 0xff;
	uint32_t green = (pixel_data>> 8) & 0xff;
	uint32_t blue  = (pixel_data>> 0) & 0xff;

	color c = RGB(red, green, blue);
	point pt(x+offset_x, y+offset_y);
	float frac = float(alpha)/255;
	
	p->draw_point_antialiased(pt, c, frac);
      }

  //cairo_surface_mark_dirty(cs);  // For if we modify the image data ourselves
  //int cerror = cairo_surface_write_to_png(cs, "out.png");  // To write to a PNG file
  //  if(cerror==CAIRO_STATUS_SUCCESS)
  //    printf("PNG write reports success.\n");
  //  else
  //    printf("PNG write reports failure.\n");

  cairo_destroy(cr);
  cairo_surface_finish(cs);
  cairo_surface_destroy(cs);
  g_object_unref(svgh);

  return true;
}
















void global_draw_svg_from_data(panel* p, uint8_t* svg_data, int svg_data_length, int offset_x, int offset_y, int draw_width, int draw_height)
{
  //RsvgDimensionData  dim;
  GError * gerror = 0;

  //printf("Data is %p, first byte %d ('%c'), length is %d\n", svg_data, svg_data[0], svg_data[0], svg_data_length);
  
  RsvgHandle *svgh = rsvg_handle_new_from_data(svg_data, svg_data_length, &gerror);

  if(!svgh)
    {
      printf("Couldn't create RSVG handle in global_draw_svg() from data.\n");
      exit(20);
    }
    
  //rsvg_handle_get_dimensions(svgh, &dim);
  double dwidth, dheight;
  int dim_width, dim_height;
  rsvg_handle_get_intrinsic_size_in_pixels(svgh, &dwidth, &dheight);

  dim_width = ceil(dwidth);
  dim_height = ceil(dheight);


  if(draw_width==0 && draw_height==0)
    {
      // Keep aspect ratio, but size as big as possible to fit the screen
      if(float(dim_width)/dim_height > float(p->width)/p->height)
	{
	  // Object is wider than panel in aspect.  Scale object to match width.

	  draw_width  = p->width;
	  draw_height = dim_height * p->width / dim_width;
	  offset_y += (p->height-draw_height)/2;
	}
      else
	{
	  // Object is narrower than panel in aspect.  Scale object to match height.
	  
	  draw_width  = dim_width * p->height / dim_height;
	  draw_height = p->height;
	  offset_x += (p->width-draw_width)/2;
	}
    }
  else if(draw_width==0)
    {
      // Keep aspect ratio, sizing width to match height
      draw_width = dim_width * p->height / dim_height;
      offset_x += (p->width-draw_width)/2;
    }
  else if(draw_height==0)
    {
      // Keep aspect ratio, sizing height to match width
      draw_height = dim_height * p->width / dim_width;
      offset_y += (p->height-draw_height)/2;
    }

  
  //  printf("SVG Dimensions are %dx%d\n", dim_width, dim_height);

  // CAIRO_FORMAT_RGB16_565
  // CAIRO_FORMAT_ARGB32
  cairo_surface_t* cs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, draw_width, draw_height);
  
  if(!cs)
    {
      printf("Couldn't create cairo surface.\n");
      exit(20);
    }

  cairo_t *cr = cairo_create(cs);

  if(!cr)
    {
      printf("Couldn't create cairo renderer.\n");
      cairo_surface_finish(cs);
      cairo_surface_destroy(cs);
      g_object_unref(svgh);
      exit(20);
    }

  //cairo_translate(cr, 1, 1);
  cairo_scale(cr, float(draw_width)/dim_width, float(draw_height)/dim_height);

  RsvgRectangle viewport;
  viewport.x      = 0;
  viewport.y      = 0;
  viewport.width  = dim_width;
  viewport.height = dim_height;

  
  //rsvg_handle_render_cairo(svgh, cr);

  rsvg_handle_render_document(svgh, cr, &viewport, 0);
 
  cairo_surface_flush(cs);

  uint32_t* data = (uint32_t*) cairo_image_surface_get_data(cs);


  for(int x=0; x<draw_width; x++)
    for(int y=0; y<draw_height; y++)
      {
	uint32_t pixel_data = data[x+y*draw_width];
	uint32_t alpha = (pixel_data>>24) & 0xff;
	uint32_t red   = (pixel_data>>16) & 0xff;
	uint32_t green = (pixel_data>> 8) & 0xff;
	uint32_t blue  = (pixel_data>> 0) & 0xff;

	color c = RGB(red, green, blue);
	point pt(x+offset_x, y+offset_y);
	float frac = float(alpha)/255;
	
	p->draw_point_antialiased(pt, c, frac);
      }

  //cairo_surface_mark_dirty(cs);  // For if we modify the image data ourselves
  //int cerror = cairo_surface_write_to_png(cs, "out.png");  // To write to a PNG file
  //  if(cerror==CAIRO_STATUS_SUCCESS)
  //    printf("PNG write reports success.\n");
  //  else
  //    printf("PNG write reports failure.\n");
    
  cairo_destroy(cr);
  cairo_surface_finish(cs);
  cairo_surface_destroy(cs);
  g_object_unref(svgh);
}
