#![allow(unused_imports, dead_code)]
use std::{fs::File, io::Write, process::Command, rc::Rc, time::Instant};

use anyhow::Result;
use clusterer::{Clusterer, Kmeans, KmeansStable};
use color::{color::Color, color_gray::ColorGray, color_oklab::ColorOklab, color_rgb::ColorRGB};
use ffmpeg_next::frame::Video;
use field::{Field, FieldView};
use fonts::{add_brailelike_chars, font_wrapper::FontWrapper, font_wrapper_zero::FontWrapperZero};
use preset_picture::PresetPicture;
use preset_video::PresetVideo;
use printer::{Chixel, Printer};

mod color;

mod clusterer;
mod field;
mod printer;
mod utils;
use video::{
    install_font, kitty_set_font,
    player::{self, PlayerDynamic, PlayerStatic},
    player_deep::PlayerStatic2,
};
mod preset_picture;
mod preset_video;
mod video;
use clap::{Parser, Subcommand};

mod fonts;

#[derive(Parser)]
struct Cli {
    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand)]
enum Commands {
    /// Print the image
    Print {
        /// Path to the image
        path: String,
    },

    /// Play the video
    Play {
        /// Path to the video
        path: String,
        /// Do not play audio
        #[arg(long)]
        no_audio: bool,
    },

    /// Remove installed fonts from the system
    Clean {},
}

fn screen_dim() -> [usize; 2] {
    let termsize::Size { rows, cols } = termsize::get().unwrap();
    [cols as usize, rows as usize]
}

fn main() -> Result<()> {
    let cli = Cli::parse();

    match cli.command {
        Commands::Clean {} => {
            // Remove installed font
            //Command::new("rm")
            //.arg("runtime/out.ttf")
            //.output()
            //.unwrap();
            //Command::new("rm")
            //.arg("runtime/out0.ttf")
            //.output()
            //.unwrap();
            //Command::new("rm")
            //.arg("runtime/in.ttf")
            //.output()
            //.unwrap();
            //Command::new("rm")
            //.arg("runtime/sound.mp3")
            //.output()
            //.unwrap();
        }
        Commands::Print { path } => {
            let dim = [80, 35];
            //PresetPicture::print_half_block::<ColorRGB>(path, dim)?;
            //PresetPicture::print_braile::<ColorRGB>(path, dim)?;
            PresetPicture::print_brailelike::<ColorRGB>(path, dim, [3, 5])?;
            //PresetPicture::funny_term_colors::<ColorRGB>(path, dim, [3, 5])?;
            //PresetPicture::print_icon::<ColorRGB>(path, dim)?;
        }
        Commands::Play { path, no_audio } => {
            if !no_audio {
                play_audio(path.as_str());
            }

            //static, brailelike
            //brailelike(path.as_str(), "../in.ttf", [1, 2])?;

            //static
            //let player = PlayerStatic::new(&frame_to_string);
            //player.play_video(path.as_str())?;

            //dynamic
            let player = PlayerDynamic::new(&frame_to_string_dyn, "../in.ttf");
            player.play_video(path.as_str())?;
            PresetVideo::dynamic(path)?;

            //player_deep
            //PresetVideo::deep_autoscale(path)?;

            //let pipeline_chixel = |f: &FieldView<ColorRGB>| {
            //let colors = f.to_vec();
            //let c = KmeansStable::new((5, 2)).cluster(&colors, 2);
            //let c = [c[0].clone(), c[1].clone()];
            //Printer::with_brailelike(f, c, 0)
            //};

            //let player = PlayerStatic2::new(&pipeline, &pipeline_chixel);
            //player.play_video(path.as_str())?;
        }
    }
    Ok(())
}

//fn brailelike(vid_name: &str, font_name: &str, char_dim: [usize; 2]) -> Result<()> {
//let font_data = std::fs::read(font_name).unwrap();
//let mut fw = FontWrapper::new(&font_data)?;
//let uni_start = fw.get_unused_unicode() + 1;
//add_brailelike_chars(char_dim, &mut fw, uni_start)?;
//fw.write_font("runtime/out.ttf", "matracut")?;
//install_font("runtime/out.ttf", "out0.ttf")?;
//kitty_set_font("matracut")?;

//let frame_to_string = |frame: &Video| -> String {
//type C = ColorOklab;
//let dim = [frame.width() as usize, frame.height() as usize];

//let field = Field::<C>::from_bytes(frame.data(0), dim).downscale([7, 7]);
//let clusterer = Kmeans::new((3, 1));
//let colors = field.cluster::<Vec<_>>(char_dim, clusterer, 2);
//let res = Printer::new(&field).with_brailelike(colors, char_dim, uni_start);

//res
//};

//let player = PlayerStatic::new(&frame_to_string);
//player.play_video(vid_name)?;
//Ok(())
//}

fn pipeline(rgb_frame: &Video) -> Field<ColorRGB> {
    type C = ColorRGB;
    //let mut pallete = vec![];
    //for i in 0..8 {
    //pallete.push(ColorRGB::new((i & 1) * 255, (i & 2) * 127, (i & 4) * 63));
    //
    //the nice pallete in vec
    let pallete = vec![
        ColorRGB::new(8, 0, 46),
        ColorRGB::new(100, 0, 44),
        ColorRGB::new(93, 115, 26),
        ColorRGB::new(205, 117, 28),
        ColorRGB::new(29, 109, 161),
        ColorRGB::new(183, 7, 126),
        ColorRGB::new(66, 163, 140),
        ColorRGB::new(243, 224, 184),
        ColorRGB::new(51, 30, 77),
        ColorRGB::new(208, 32, 99),
        ColorRGB::new(180, 206, 89),
        ColorRGB::new(250, 195, 87),
        ColorRGB::new(64, 164, 207),
        ColorRGB::new(241, 42, 174),
        ColorRGB::new(98, 202, 168),
        ColorRGB::new(255, 245, 219),
    ];
    let solarized = vec![
        ColorRGB::new(0, 43, 54),     // base03
        ColorRGB::new(7, 54, 66),     // base02
        ColorRGB::new(88, 110, 117),  // base01
        ColorRGB::new(101, 123, 131), // base00
        ColorRGB::new(131, 148, 150), // base0
        ColorRGB::new(147, 161, 161), // base1
        ColorRGB::new(238, 232, 213), // base2
        ColorRGB::new(253, 246, 227), // base3
        ColorRGB::new(181, 137, 0),   // yellow
        ColorRGB::new(203, 75, 22),   // orange
        ColorRGB::new(220, 50, 47),   // red
        ColorRGB::new(211, 54, 130),  // magenta
        ColorRGB::new(108, 113, 196), // violet
        ColorRGB::new(38, 139, 210),  // blue
        ColorRGB::new(42, 161, 152),  // cyan
        ColorRGB::new(133, 153, 0),   // green
    ];

    let dim = [rgb_frame.width() as usize, rgb_frame.height() as usize];
    let mut field = Field::<C>::from_bytes(rgb_frame.data(0), dim);
    let screen = screen_dim();
    let screen = [screen[0], screen[1]];
    //print!("screen: {:?}\n", screen);
    //field.scale_to_fit_ratio([screen[0] * 3, screen[1] * 5], 5.0 / 3.0 * 0.5);
    field = field.fit([screen[0] * 3, screen[1] * 5], 3.0 / 5.0 * 2.0);

    //let colors = Field::one(*field.get_dim(), pallete);
    //field.set_to_closest_with_multi_3(&pallete);
    ////field.discretize(2);
    //field.hard_shadow();
    field
}

//fn frame_to_string(frame: &Video) -> String {
//type C = ColorRGB;
//let dim = [frame.width() as usize, frame.height() as usize];

//let field = Field::<C>::from_bytes(frame.data(0), dim);
//let field = field.downscale([3, 4]);
//let clusterer = Kmeans::<C>::new((8, 3));
//let colors = field.cluster::<Vec<C>>([2, 3], clusterer, 2);
//let printer = Printer::<C>::new(&field);
//print!("\x1b[1;1H");
////let res = printer.with_braile(colors);
//let res = printer.with_braile(colors);

//res
//}

fn frame_to_string_dyn(frame: &Video, font_wrapper: &mut FontWrapper) -> String {
    type C = ColorRGB;
    let dim = [frame.width() as usize, frame.height() as usize];

    let mut field = Field::<C>::from_bytes(frame.data(0), dim).downscale([4, 4]);
    let dd = [3, 6];

    let colors = field.cluster::<Vec<C>>(dd, Kmeans::new((6, 3)), 2);
    let hcn = 16;
    //let mut fw = FontWrapperZero::new([[0, 0], [hcn, hcn * 2]]).unwrap();

    let action = |fv: &FieldView<C>, w: &mut FontWrapper| -> Chixel<C> {
        let colors = fv.to_vec();
        let c = KmeansStable::new((5, 2)).cluster(&colors, 2);
        let c = [c[0].clone(), c[1].clone()];

        let res = Printer::with_custom_font(fv, c, w);
        res
    };

    let res = field.print_for_each_block_font(dd, font_wrapper, action);

    //let res = Printer::new(&field)
    //.with_custom_font(&colors, dd, font_wrapper)
    //.unwrap();

    res.to_string()
}

///placesolder function that extracts mp3 from `file_name` and plays it using mpv
fn play_audio(file_name: &str) {
    const SOUND_PATH: &str = "runtime/sound.mp3";

    //Command::new("rm").arg(SOUND_PATH).output().unwrap();

    //Command::new("ffmpeg")
    //.arg("-i")
    //.arg(file_name)
    //.arg(SOUND_PATH)
    //.output()
    //.unwrap();

    //Command::new("mpv")
    //.arg(SOUND_PATH)
    //.arg("--no-audio-display")
    //.stdout(std::process::Stdio::null())
    //.stderr(std::process::Stdio::null())
    //.spawn()
    //.unwrap();

    Command::new("mpv")
        .arg(file_name)
        .arg("--no-video")
        .stdout(std::process::Stdio::null())
        .stderr(std::process::Stdio::null())
        .spawn()
        .unwrap();
}
