use rand::Rng;
use winit::{
    event::*,
    event_loop::{ControlFlow, EventLoop},
    window::WindowBuilder,
};

use bufro::Color;
use cgmath::VectorSpace;

fn main() {
    env_logger::init();
    let event_loop = EventLoop::new();
    let window = WindowBuilder::new()
        .with_inner_size(winit::dpi::PhysicalSize::new(500, 500))
        .build(&event_loop)
        .unwrap();

    // Since main can't be async, we're going to need to block
    let mut painter = pollster::block_on(bufro::Painter::new_from_window(&window, (500, 500)));
    let font = bufro::Font::new(include_bytes!("FiraMono-Regular.ttf")).unwrap();
    let mut cursor_position = cgmath::vec2(0.0, 0.0);
    let mut mouse_down = false;

    let mut canvas_translation = cgmath::vec2(0.0, 0.0);
    let mut canvas_translation_lerped = cgmath::vec2(0.0, 0.0);

    let mut canvas_scale = cgmath::vec1(1.);
    let mut canvas_scale_lerped = cgmath::vec1(1.);

    let mut frame = 0;

    let mut circles = std::collections::HashMap::new();

    let mut rng = rand::thread_rng();

    event_loop.run(move |event, _, control_flow| {
        let text = "Bufro v0.1.7";
        match event {
            Event::WindowEvent {
                ref event,
                window_id,
            } if window_id == window.id() => {
                match event {
                    WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
                    WindowEvent::CursorMoved { position, .. } => {
                        let rel_x = cursor_position.x - position.x;
                        let rel_y = cursor_position.y - position.y;
                        if mouse_down {
                            canvas_translation.x -= rel_x / canvas_scale_lerped.x as f64;
                            canvas_translation.y -= rel_y / canvas_scale_lerped.x as f64;
                        }
                        cursor_position = cgmath::vec2(position.x, position.y)
                    }
                    WindowEvent::MouseInput { state, .. } => match state {
                        winit::event::ElementState::Pressed => mouse_down = true,
                        winit::event::ElementState::Released => mouse_down = false,
                    },
                    WindowEvent::MouseWheel { delta, .. } => match delta {
                        winit::event::MouseScrollDelta::LineDelta(x, y) => {
                            canvas_scale.x += y / 10.;
                        }
                        _ => {}
                    },
                    WindowEvent::KeyboardInput {
                        input:
                            winit::event::KeyboardInput {
                                virtual_keycode: Some(keycode),
                                ..
                            },
                        ..
                    } => match keycode {
                        VirtualKeyCode::Escape => *control_flow = ControlFlow::Exit,
                        VirtualKeyCode::Space => {}
                        _ => {}
                    },
                    WindowEvent::Resized(physical_size) => {
                        painter.resize((physical_size.width, physical_size.height));
                    }
                    WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
                        // new_inner_size is &mut so w have to dereference it twice
                        painter.resize((new_inner_size.width, new_inner_size.height));
                    }
                    _ => {}
                }
            }
            Event::RedrawRequested(_) => {
                frame += 1;

                let size = window.inner_size();
                painter.rectangle(
                    0.,
                    0.,
                    size.width as f32,
                    size.height as f32,
                    Color::from_f(0.2, 0.2, 0.2, 1.0),
                );
                canvas_translation_lerped = canvas_translation_lerped.lerp(canvas_translation, 0.2);
                canvas_scale_lerped = canvas_scale_lerped.lerp(canvas_scale, 0.2);
                painter.translate(size.width as f32 / 2., size.height as f32 / 2.);
                painter.scale(canvas_scale_lerped.x, canvas_scale_lerped.x);
                painter.translate(
                    -(size.width as f32) / 2. + canvas_translation_lerped.x as f32,
                    -(size.height as f32) / 2. + canvas_translation_lerped.y as f32,
                );
                /*painter.translate(
                    canvas_translation_lerped.x as f32 - (cursor_position.x / 2.) as f32,
                    canvas_translation_lerped.y as f32 - (cursor_position.y / 2.) as f32,
                );
                painter.scale(canvas_scale_lerped.x, canvas_scale_lerped.x);
                painter.translate(
                    -((cursor_position.x / 2.) as f32),
                    -((cursor_position.y / 2.) as f32)
                );*/
                painter.rectangle(50., 50., 100., 100., Color::from_8(220, 220, 40, 100));
                painter.rectangle(75., 75., 100., 100., Color::from_8(30, 90, 200, 100));

                painter.rectangle(225., 225., 100., 100., Color::from_8(30, 90, 200, 100));
                painter.rectangle(200., 200., 100., 100., Color::from_8(220, 220, 40, 100));

                painter.save();
                painter.translate(500., 500.);
                painter.save();
                painter.translate(-75., 0.);
                painter.circle(0., 0., 100., Color::from_8(0, 0, 255, 100));
                painter.restore();

                painter.save();
                painter.translate(75., 0.);
                painter.circle(0., 0., 100., Color::from_8(0, 255, 0, 100));
                painter.restore();

                painter.save();
                painter.translate(0., -75.);
                painter.circle(0., 0., 100., Color::from_8(255, 0, 0, 100));
                painter.restore();
                painter.restore();

                painter.save();
                painter.translate(700., 100.);
                let mut path = bufro::PathBuilder::new();
                path.move_to(0., 0.);
                path.quad_to(100., 100., 200., 0.);
                path.quad_to(100., 100., 200., 200.);
                path.quad_to(100., 100., 0., 200.);
                path.quad_to(100., 100., 0., 0.);
                path.close();
                let path = path.build();
                painter.fill_path(&path, Color::from_8(200, 200, 200, 255));
                painter.stroke_path(
                    &path,
                    Color::from_8(255, 255, 255, 255),
                    bufro::StrokeOptions::default()
                        .with_line_width(10.)
                        .with_line_join(bufro::LineJoin::Round),
                );
                painter.translate(300., 0.);
                let mut path = bufro::PathBuilder::new();
                path.move_to(0., 0.);
                path.curve_to(100., 100., 100., -100., 200., 0.);
                path.curve_to(100., 100., 300., 100., 200., 200.);
                path.curve_to(100., 100., 100., 300., 0., 200.);
                path.curve_to(100., 100., -100., 100., 0., 0.);
                path.close();
                let path = path.build();
                painter.fill_path(&path, Color::from_8(200, 200, 200, 255));
                painter.stroke_path(
                    &path,
                    Color::from_8(255, 255, 255, 255),
                    bufro::StrokeOptions::default()
                        .with_line_width(10.)
                        .with_line_join(bufro::LineJoin::Round),
                );

                painter.restore();

                if circles.len() < 10000 {
                    circles.insert(
                        frame,
                        (
                            rng.gen_range(0.0..10000.0),
                            rng.gen_range(0.0..10000.0),
                            rng.gen_range(10.0..50.0),
                        ),
                    );
                }

                let time_start = std::time::Instant::now();
                for (_, circle) in circles.iter_mut() {
                    painter.save();
                    painter.translate(circle.0, circle.1);
                    //painter.circle(0.0, 0.0, circle.2 + 5.0, Color::from_8(174, 63, 0, 255));
                    painter.circle(0.0, 0.0, circle.2, Color::from_8(214, 73, 5, 255));
                    painter.restore();
                }
                println!("Elapsed: {:?}", time_start.elapsed());

                painter.circle(
                    0.,
                    0.,
                    (frame % 60 + 1) as f32,
                    Color::from_8(255, 255, 255, 255),
                );

                painter.reset();
                painter.fill_text(
                    &font,
                    &painter.get_buffer_info(),
                    0.,
                    0.,
                    15.5,
                    Color::from_8(0xFF, 0xFF, 0xFF, 0xFF),
                    Some(150),
                );

                match painter.flush() {
                    Ok(_) => {}
                    // Recreate the swap_chain if lost
                    Err(wgpu::SurfaceError::Lost) => {
                        painter.clear();
                        painter.regen()
                    }
                    // The system is out of memory, we should probably quit
                    Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
                    // All other errors (Outdated, Timeout) should be resolved by the next frame
                    Err(e) => {
                        painter.clear();
                        eprintln!("{:?}", e)
                    }
                }
            }
            Event::MainEventsCleared => {
                // RedrawRequested will only trigger once, unless we manually
                // request it.
                window.request_redraw();
            }
            _ => {}
        }
    });
}
