//! The module creates a VecRenderer per icon
//! and passes it to icon_data for rendering.

use super::render_c::CRenderer;
use super::render_png::PngRenderer;
use super::render_svg::VecRenderer;
use crate::model::icon::IconSource;
use std::fs;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;

/// creates a parent directory and a file in it
///
/// # Arguments
///
/// * `out_dir` - The directory where files shall be written to
/// * `file_name` - The filename of the file to be created
///
/// # Panics
///
/// This function panics if the the file cannot be created.
///
fn open_file_to_write(out_dir: &str, file_name: &str) -> File {
    /* create the output directory, panic in case an error occured */
    fs::create_dir_all(out_dir).unwrap();

    /* create the file, panic in case an error occured */
    let mut file_to_use = PathBuf::from(out_dir);
    file_to_use.push(file_name);
    File::create(&file_to_use).unwrap()
}

/// The function writes a starting para tag to the docbook out file.
///
/// # Panics
///
/// This function panics if the vector graphics cannot be written to a file.
///
fn write_db_header(out: &mut File) -> () {
    write!(out, "<para>\n").expect("Error at writing file");
    write!(out, "<!-- this file is generated by icon_artist -->\n").expect("Error at writing file");
    write!(out, "  <variablelist>\n").expect("Error at writing file");
    write!(out, "    <title>Stereotype images</title>\n").expect("Error at writing file");
}

/// The function writes a ending para tag to the docbook out file.
///
/// # Panics
///
/// This function panics if the vector graphics cannot be written to a file.
///
fn write_db_footer(out: &mut File) -> () {
    write!(out, "  </variablelist>\n").expect("Error at writing file");
    write!(out, "</para>\n").expect("Error at writing file");
}

/// The function writes a icon entry to the docbook out file.
///
/// # Panics
///
/// This function panics if the vector graphics cannot be written to a file.
///
fn write_db_icon_entry(icon: &IconSource, out: &mut File) -> () {
    write!(out, "    <varlistentry>\n").expect("Error at writing file");
    write!(out, "      <term>{}</term>\n", icon.name).expect("Error at writing file");
    write!(out, "      <listitem><para>\n        ").expect("Error at writing file");
    write!(
        out,
        "\
        <inlinemediaobject><imageobject>\
        <imagedata fileref=\"{}.svg\" width=\"1cm\"/>\
        </imageobject></inlinemediaobject>\
        ",
        icon.name
    )
    .expect("Error at writing file");
    write!(out, "\n      </para><para>\n").expect("Error at writing file");
    write!(out, "        <command><![CDATA[").expect("Error at writing file");
    let mut db_render = VecRenderer {
        output_file: out,
        force_colors: false,
    };
    (icon.generate)(&mut db_render);
    write!(out, "]]></command>\n").expect("Error at writing file");
    write!(out, "      </para></listitem>\n").expect("Error at writing file");
    write!(out, "    </varlistentry>\n").expect("Error at writing file");
}

/// Defines the possible filetypes to generate
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum FileType {
    /// A set of standalone svg images.
    Svg,
    /// An index file in docbook format.
    IndexOfSvg,
    /// A c struct containig pixbuf data.
    PixBuf,
    /// A png image.
    Png,
    /// A scaled png image.
    ScaledPng {
        /// Width and height of the png image.
        size: i32,
    },
}

/// The function creates a file for each icon and triggers the rendering
/// of the icon as vector graphics drawing directives to the file.
///
/// # Arguments
///
/// * `icons` - The list of images to be rendered
/// * `out_type` - The output format
/// * `out_dir` - The output directory where to export the generated images to
///
/// # Panics
///
/// This function panics if the vector graphics cannot be written to a file.
///
pub fn generate_files(icons: &[IconSource], out_type: FileType, out_dir: &str) -> () {
    match out_type {
        FileType::IndexOfSvg => {
            let mut db_file = open_file_to_write(out_dir, &"stereotype_images.xml");
            write_db_header(&mut db_file);

            for icon in icons {
                /* write an image reference to a docbook file */
                write_db_icon_entry(icon, &mut db_file);
            }

            write_db_footer(&mut db_file);
        }

        FileType::Svg => {
            for icon in icons {
                /* render an svg file */
                let file_name: String = icon.name.to_owned() + ".svg";
                let mut svg_file = open_file_to_write(out_dir, &file_name);
                let mut v_render = VecRenderer {
                    output_file: &mut svg_file,
                    force_colors: true,
                };
                v_render.header(&icon.viewport);
                (icon.generate)(&mut v_render);
                v_render.footer();
            }
        }

        FileType::PixBuf => {
            /* write some c files */
            for icon in icons {
                /* render a c file */
                let file_name: String = icon.name.to_owned() + ".c";
                let mut c_file = open_file_to_write(out_dir, &file_name);
                let mut c_render = CRenderer::new(&mut c_file, &icon.name, &icon.viewport);
                (icon.generate)(&mut c_render);
                c_render.write_cimpl();
            }
        }

        FileType::Png => {
            /* write some png files */
            for icon in icons {
                /* render a png file */
                let file_name: String = out_dir.to_owned() + "/" + icon.name + ".png";
                let mut png_render = PngRenderer::new(
                    &file_name,
                    icon.viewport.width as i32,
                    icon.viewport.height as i32,
                    0.0,
                    0.0,
                    1.0,
                    1.0,
                );
                (icon.generate)(&mut png_render);
                png_render.write_png();
            }
        }

        FileType::ScaledPng { size } => {
            /* write some png files */
            for icon in icons {
                /* render a png file */
                let size_num: &str = &(size.to_string());
                let file_name: String =
                    out_dir.to_owned() + "/" + icon.name + "_" + size_num + "x" + size_num + ".png";
                let scale: f32 = size as f32 / f32::max(icon.viewport.width, icon.viewport.height);
                let mut png_render =
                    PngRenderer::new(&file_name, size, size, 0.0, 0.0, scale, scale);
                (icon.generate)(&mut png_render);
                png_render.write_png();
            }
        }
    }
}

/*
Copyright 2023-2026 Andreas Warnke

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.
*/
