use crate::export::SymbolicColor;
use crate::utils;

use gtk::prelude::*;
use gtk::{gdk, gio, glib};

pub struct Config {
    pub size: u32,
    pub border_radius: u32,
    pub symbolic: Option<glib::Bytes>,
    pub symbolic_color: SymbolicColor,
    pub gradient_start: gdk::RGBA,
    pub gradient_end: gdk::RGBA,
}

pub async fn save_svg(config: &Config, dest: &gio::File) -> anyhow::Result<()> {
    dest.replace_contents_future(
        generate_svg(config),
        None,
        false,
        gio::FileCreateFlags::REPLACE_DESTINATION,
    )
    .await
    .map_err(|e| e.1)?;

    Ok(())
}

pub async fn save_png(config: &Config, dest: &gio::File) -> anyhow::Result<()> {
    dest.replace_contents_future(
        generate_png(config).await?,
        None,
        false,
        gio::FileCreateFlags::REPLACE_DESTINATION,
    )
    .await
    .map_err(|e| e.1)?;

    Ok(())
}

pub fn generate_svg(config: &Config) -> Vec<u8> {
    let (mask, width, height) = {
        if let Some(symbolic) = config.symbolic.as_ref() {
            let svg = std::str::from_utf8(symbolic).unwrap_or("");
            let (width, height) = utils::svg_size(symbolic).unwrap_or((16.0, 16.0));
            (
                utils::sanitize(svg, width, height)
                    .unwrap_or_else(|_| utils::strip_svg_header(svg)),
                width,
                height,
            )
        } else {
            ("".to_string(), 16.0, 16.0)
        }
    };

    let border_radius = config.border_radius;
    let start_color = &config.gradient_start;
    let end_color = &config.gradient_end;
    let size = config.size;
    let tx = config.size / 4;
    let scale_x = (config.size / 2) / width as u32;
    let scale_y = (config.size / 2) / height as u32;

    let start_color_hex = utils::rgba_to_hex(start_color);
    let end_color_hex = utils::rgba_to_hex(end_color);

    let dark_icon = match config.symbolic_color {
        SymbolicColor::Auto => {
            let avg_color = utils::interpolate(start_color, end_color);
            utils::black_has_better_contrast(&avg_color)
        }
        SymbolicColor::Light => false,
        SymbolicColor::Dark => true,
    };

    // We intentionally don't recolor symbolics for now.
    let matrix_values = if dark_icon {
        let black = gdk::RGBA::new(0.0, 0.0, 0.0, 0.8);
        utils::fe_color_matrix_values(Some(black), None, None, None)
    } else {
        let white = gdk::RGBA::WHITE;
        utils::fe_color_matrix_values(Some(white), None, None, None)
    };
    format!(
        r##"<?xml version="1.0" encoding="UTF-8"?>
<svg width="{size}" height="{size}" version="1.1"
     xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink">
  <defs>
    <linearGradient id="Gradient" x1="0" x2="0" y1="0" y2="1">
      <stop offset="0%" style="stop-color:{start_color_hex};stop-opacity:1" />
      <stop offset="100%" style="stop-color:{end_color_hex};stop-opacity:1" />
    </linearGradient>
    <filter id="alpha-to-white">
      <feColorMatrix in="SourceGraphic" type="matrix"
                     values="{matrix_values}"/>
    </filter>
    <g id="child-svg">{mask}</g>
  </defs>
  <rect
      width="{size}"
      height="{size}"
      fill="url(#Gradient)"
      ry="{border_radius}"
      x="0"
      y="0" />
  <use xlink:href="#child-svg" filter="url(#alpha-to-white)"
       transform="matrix({scale_x},0,0,{scale_y},{tx},{tx})" />
</svg>"##
    )
    .into_bytes()
}

async fn generate_png(config: &Config) -> anyhow::Result<glib::Bytes> {
    let bytes = glib::Bytes::from_owned(generate_svg(config));
    let texture = utils::load_texture_from_bytes(&bytes).await?;
    Ok(texture.save_to_png_bytes())
}
