use {
    crate::{
        makepad_platform::*,
        cx_2d::{Cx2d},
    }
};
#[derive(Copy, Clone, Debug, Live, LiveHook)]
#[live_ignore]
pub struct Layout {
    #[live] pub scroll: DVec2,
    #[live(true)] pub clip_x: bool,
    #[live(true)] pub clip_y: bool,
    #[live] pub padding: Padding,
    #[live] pub align: Align,
    #[live] pub flow: Flow,
    #[live] pub spacing: f64
}
impl Default for Layout{
    fn default()->Self{
        Self{
            scroll: dvec2(0.0,0.0),
            clip_x: true,
            clip_y: true,
            padding: Padding::default(),
            align: Align{x:0.0,y:0.0},
            flow: Flow::Right,
            spacing: 0.0
        }
    }
}
#[derive(Copy, Clone, Default, Debug, Live, LiveHook)]
#[live_ignore]
pub struct Walk {
    #[live] pub abs_pos: Option<DVec2>,
    #[live] pub margin: Margin,
    #[live] pub width: Size,
    #[live] pub height: Size,
}
#[derive(Clone, Copy, Default, Debug, Live, LiveHook)]
#[live_ignore]
pub struct Align {
    #[live] pub x: f64,
    #[live] pub y: f64
}
#[derive(Clone, Copy, Default, Debug, Live)]
#[live_ignore]
pub struct Padding {
    #[live] pub left: f64,
    #[live] pub top: f64,
    #[live] pub right: f64,
    #[live] pub bottom: f64
}
#[derive(Copy, Clone, Debug, Live, LiveHook)]
#[live_ignore]
pub enum Axis {
    #[pick] Horizontal,
    Vertical
}
impl Default for Axis {
    fn default() -> Self {
        Axis::Horizontal
    }
}
#[derive(Copy, Clone, Debug, Live, LiveHook)]
#[live_ignore]
pub enum Flow {
    #[pick] Right,
    Down,
    Overlay
}
#[derive(Copy, Clone, Debug, Live)]
#[live_ignore]
pub enum Size {
    #[pick] Fill,
    #[live(200.0)] Fixed(f64),
    Fit,
    All
}
#[derive(Clone, Debug)]
pub enum DeferWalk{
    Unresolved{
        defer_index: usize,
        margin: Margin,
        other_axis: Size,
        pos: DVec2
    },
    Resolved(Walk)
}
#[derive(Debug)]
pub enum AlignEntry{
    Unset,
    Area(Area),
    ShiftTurtle{area:Area, shift:DVec2, skip:usize},
    SkipTurtle{skip:usize},
    BeginTurtle(DVec2,DVec2),
    EndTurtle
}
#[derive(Clone, Default, Debug)]
pub struct TurtleWalk {
    align_start: usize,
    defer_index: usize,
    rect: Rect,
}
#[derive(Clone, Default, Debug)]
pub struct Turtle {
    walk: Walk,
    layout: Layout,
    align_start: usize,
    turtle_walks_start: usize,
    defer_count: usize,
    shift: DVec2,
    pos: DVec2,
    origin: DVec2,
    width: f64,
    height: f64,
    width_used: f64,
    height_used: f64,
    guard_area: Area
}
impl<'a> Cx2d<'a> {
    pub fn turtle(&self) -> &Turtle {
        self.turtles.last().unwrap()
    }
    
    pub fn turtle_mut(&mut self) -> &mut Turtle {
        self.turtles.last_mut().unwrap()
    }
    
    pub fn begin_turtle(&mut self, walk: Walk, layout: Layout) {
        self.begin_turtle_with_guard(walk, layout, Area::Empty)
    }
    
    pub fn defer_walk(&mut self, walk: Walk) -> Option<DeferWalk> {
        if walk.abs_pos.is_some(){
            return None
        }
        let turtle = self.turtles.last_mut().unwrap();
        let defer_index = turtle.defer_count;
        let pos = turtle.pos;
        let size = dvec2(
            turtle.eval_width(walk.width, walk.margin, turtle.layout.flow),
            turtle.eval_height(walk.height, walk.margin, turtle.layout.flow)
        );
        let margin_size = walk.margin.size();
        match turtle.layout.flow {
            Flow::Right if walk.width.is_fill() => {
                let spacing = turtle.child_spacing(self.turtle_walks.len());
                turtle.pos.x += margin_size.x + spacing.x;
                turtle.update_width_max(turtle.pos.x, 0.0);
                turtle.update_height_max(turtle.pos.y, size.y + margin_size.y);
                turtle.defer_count += 1;
                Some(DeferWalk::Unresolved{
                    defer_index,
                    margin: walk.margin,
                    other_axis: walk.height,
                    pos: pos + spacing
                })
            },
            Flow::Down if walk.height.is_fill() => {
                let spacing = turtle.child_spacing(self.turtle_walks.len());
                turtle.pos.y += margin_size.y + spacing.y;
                turtle.update_width_max(turtle.pos.x, size.x + margin_size.x);
                turtle.update_height_max(turtle.pos.y, 0.0);
                turtle.defer_count += 1;
                Some(DeferWalk::Unresolved {
                    defer_index,
                    margin: walk.margin,
                    other_axis: walk.width,
                    pos: pos + spacing
                })
            },
            _ => {
                None
            }
        }
    }
    
    pub fn begin_pass_sized_turtle(&mut self, layout: Layout) {
        let pass_size = self.current_pass_size();
        self.align_list.push(AlignEntry::BeginTurtle(dvec2(0.0,0.0),pass_size));
        let turtle = Turtle {
            walk: Walk::fill(),
            layout,
            align_start: self.align_list.len() - 1,
            turtle_walks_start: self.turtle_walks.len(),
            defer_count: 0,
            pos: DVec2 {
                x: layout.padding.left,
                y: layout.padding.top
            },
            origin: dvec2(0.0,0.0),
            width: pass_size.x,
            height: pass_size.y,
            shift: dvec2(0.0,0.0),
            width_used: layout.padding.left,
            height_used: layout.padding.top,
            guard_area: Area::Empty,
        };
        self.turtles.push(turtle);
    }
    
    pub fn end_pass_sized_turtle(&mut self){
        let turtle = self.turtles.pop().unwrap();
        self.align_list.push(AlignEntry::EndTurtle);
        self.perform_nested_clipping_on_align_list_and_shift(turtle.align_start, self.align_list.len());
        self.align_list[turtle.align_start] = AlignEntry::SkipTurtle{skip:self.align_list.len()};
        self.turtle_walks.truncate(turtle.turtle_walks_start);
    }
    pub fn end_pass_sized_turtle_with_shift(&mut self, area:Area, shift:DVec2){
        let turtle = self.turtles.pop().unwrap();
        self.align_list.push(AlignEntry::EndTurtle);
        self.perform_nested_clipping_on_align_list_and_shift(turtle.align_start, self.align_list.len());
        self.align_list[turtle.align_start] = AlignEntry::ShiftTurtle{
            area,
            shift, 
            skip: self.align_list.len()
        };
        self.turtle_walks.truncate(turtle.turtle_walks_start);
    }
    
    pub fn begin_turtle_with_guard(&mut self, walk: Walk, layout: Layout, guard_area: Area) {
        let (origin, width, height, draw_clip) = if let Some(parent) = self.turtles.last() {
            
            let o = walk.margin.left_top() + if let Some(pos) = walk.abs_pos {pos} else {
                parent.pos + parent.child_spacing(self.turtle_walks.len()) 
            };
            
            let w = parent.eval_width(walk.width, walk.margin, parent.layout.flow);
            let h = parent.eval_height(walk.height, walk.margin, parent.layout.flow);
            
            let (x0, x1) = if layout.clip_x {
                (o.x, if w.is_nan() {
                    f64::NAN
                    } else {
                    o.x + w})
            } else {
                (f64::NAN, f64::NAN)};
            
            let (y0, y1) = if layout.clip_y {
                (o.y, if h.is_nan() {
                    f64::NAN
                } else {
                    o.y + h})
            }else {(f64::NAN,f64::NAN)};(o - layout.scroll, w, h, (dvec2(x0, y0), (dvec2(x1, y1))))
        }
        else {
            let o = DVec2 {x: walk.margin.left, y: walk.margin.top};
            let w = walk.width.fixed_or_nan();
            let h = walk.height.fixed_or_nan();
            
            (o, w, h, (dvec2(o.x, o.y), dvec2(o.x + w, o.y + h)))
        };
        self.align_list.push(AlignEntry::BeginTurtle(draw_clip.0,draw_clip.1));
        let turtle = Turtle {
            walk,
            layout,
            align_start: self.align_list.len()-1,
            turtle_walks_start: self.turtle_walks.len(),
            defer_count: 0,
            pos: DVec2 {
                x: origin.x + layout.padding.left,
                y: origin.y + layout.padding.top
            },
            origin,
            width,
            height,
            shift: dvec2(0.0,0.0),
            width_used: layout.padding.left,
            height_used: layout.padding.top,
            guard_area,
        };
        
        self.turtles.push(turtle);
    }
    
    pub fn turtle_has_align_items(&mut self)->bool{
        self.align_list.len() != self.turtle().align_start + 1
    }
    
    pub fn end_turtle(&mut self) -> Rect {
        self.end_turtle_with_guard(Area::Empty)
    }
    
    pub fn end_turtle_with_area(&mut self, area: &mut Area) {
        let rect = self.end_turtle_with_guard(Area::Empty);
        self.add_aligned_rect_area(area, rect)
    }
    
    pub fn end_turtle_with_guard(&mut self, guard_area: Area) -> Rect {
        let turtle = self.turtles.pop().unwrap();
        if guard_area != turtle.guard_area {
            panic!("End turtle guard area misaligned!, begin/end pair not matched begin {:?} end {:?}", turtle.guard_area, guard_area)
        }
        
        let w = if turtle.width.is_nan() {
            Size::Fixed(turtle.width_used + turtle.layout.padding.right - turtle.layout.scroll.x)
        }
        else {
            Size::Fixed(turtle.width)
        };
        
        let h = if turtle.height.is_nan() {
            Size::Fixed(turtle.height_used + turtle.layout.padding.bottom - turtle.layout.scroll.y)
        }
        else {
            Size::Fixed(turtle.height)
        };
        
        match turtle.layout.flow {
            Flow::Right => {
                if turtle.defer_count > 0 {
                    let left = turtle.width_left();
                    let part = left / turtle.defer_count as f64;
                    for i in turtle.turtle_walks_start..self.turtle_walks.len() {
                        let walk = &self.turtle_walks[i];
                        let shift_x = walk.defer_index as f64 * part;
                        let shift_y = turtle.layout.align.y * (turtle.padded_height_or_used() - walk.rect.size.y);
                        let align_start = walk.align_start;
                        let align_end = self.get_turtle_walk_align_end(i);
                        self.move_align_list(shift_x, shift_y, align_start, align_end, false, turtle.shift);
                    }
                }
                else {
                    for i in turtle.turtle_walks_start..self.turtle_walks.len() {
                        let walk = &self.turtle_walks[i];
                        let shift_x = turtle.layout.align.x * turtle.width_left();
                        let shift_y = turtle.layout.align.y * (turtle.padded_height_or_used() - walk.rect.size.y);
                        let align_start = walk.align_start;
                        let align_end = self.get_turtle_walk_align_end(i);
                        self.move_align_list(shift_x, shift_y, align_start, align_end, false, turtle.shift);
                    }
                }
            },
            Flow::Down => {
                if turtle.defer_count > 0 {
                    let left = turtle.height_left();
                    let part = left / turtle.defer_count as f64;
                    for i in turtle.turtle_walks_start..self.turtle_walks.len() {
                        let walk = &self.turtle_walks[i];
                        let shift_x = turtle.layout.align.x * (turtle.padded_width_or_used() - walk.rect.size.x);
                        let shift_y = walk.defer_index as f64 * part;
                        let align_start = walk.align_start;
                        let align_end = self.get_turtle_walk_align_end(i);
                        self.move_align_list(shift_x, shift_y, align_start, align_end, false, turtle.shift);
                    }
                }
                else {
                    for i in turtle.turtle_walks_start..self.turtle_walks.len() {
                        let walk = &self.turtle_walks[i];
                        let shift_x = turtle.layout.align.x * (turtle.padded_width_or_used() - walk.rect.size.x);
                        let shift_y = turtle.layout.align.y * turtle.height_left();
                        let align_start = walk.align_start;
                        let align_end = self.get_turtle_walk_align_end(i);
                        self.move_align_list(shift_x, shift_y, align_start, align_end, false, turtle.shift);
                    }
                }
            },
            Flow::Overlay => {
                for i in turtle.turtle_walks_start..self.turtle_walks.len() {
                    let walk = &self.turtle_walks[i];
                    let shift_x = turtle.layout.align.x * (turtle.padded_width_or_used() - walk.rect.size.x);
                    let shift_y = turtle.layout.align.y * (turtle.padded_height_or_used() - walk.rect.size.y);
                    let align_start = walk.align_start;
                    let align_end = self.get_turtle_walk_align_end(i);
                    self.move_align_list(shift_x, shift_y, align_start, align_end, false, turtle.shift);
                }
            }
        }
        self.turtle_walks.truncate(turtle.turtle_walks_start);
        self.align_list.push(AlignEntry::EndTurtle);
        if self.turtles.len() == 0 {
            return Rect {
                pos: dvec2(0.0, 0.0),
                size: dvec2(w.fixed_or_zero(), h.fixed_or_zero())
            }
        }
        let rect = self.walk_turtle_move(Walk {width: w, height: h, ..turtle.walk}, turtle.align_start);
        rect
    }
    
    pub fn walk_turtle(&mut self, walk: Walk) -> Rect {
        self.walk_turtle_move(walk, self.align_list.len())
    }
    
    pub fn walk_turtle_with_area(&mut self, area: &mut Area, walk: Walk) -> Rect {
        let rect = self.walk_turtle_move(walk, self.align_list.len());
        self.add_aligned_rect_area(area, rect);
        rect
    }
    
    pub fn walk_turtle_with_align(&mut self, walk: Walk, align_start: usize) -> Rect {
        self.walk_turtle_move(walk, align_start)
    }
    
    pub fn peek_walk_turtle(&self, walk: Walk) -> Rect {
        self.walk_turtle_peek(walk)
    }
    
    pub fn walk_turtle_would_be_visible(&mut self, walk: Walk) -> bool {
        let rect = self.walk_turtle_peek(walk);
        self.turtle().rect_is_visible(rect)
    }
    
    pub fn peek_walk_pos(&self, walk: Walk) -> DVec2 {
        if let Some(pos) = walk.abs_pos {
            pos + walk.margin.left_top()
        }
        else {
            let turtle = self.turtles.last().unwrap();
            turtle.pos + walk.margin.left_top()
        }
    }
    
     fn walk_turtle_move(&mut self, walk: Walk, align_start: usize) -> Rect {
        
        let turtle = self.turtles.last_mut().unwrap();
        let size = dvec2(
            turtle.eval_width(walk.width, walk.margin, turtle.layout.flow),
            turtle.eval_height(walk.height, walk.margin, turtle.layout.flow)
        );
        
        if let Some(pos) = walk.abs_pos {
            self.turtle_walks.push(TurtleWalk {
                align_start,
                defer_index: 0,
                rect: Rect {pos, size: size + walk.margin.size()}
            });
            
            match turtle.layout.flow {
                Flow::Right=>turtle.update_height_max(pos.y, size.y + walk.margin.size().y),
                Flow::Down=>turtle.update_width_max(pos.x, size.x + walk.margin.size().x),
                _=>()
            }
            Rect {pos: pos + walk.margin.left_top(), size}
        }
        else {
            let spacing = turtle.child_spacing(self.turtle_walks.len());
            let pos = turtle.pos;
        
            let margin_size = walk.margin.size();
            match turtle.layout.flow {
                Flow::Right => {
                    turtle.pos.x = pos.x + size.x + margin_size.x + spacing.x;
                    if size.x < 0.0 {
                        turtle.update_width_min(turtle.pos.x, 0.0);
                        turtle.update_height_max(turtle.pos.y,size.y + margin_size.y);
                    }
                    else {
                        turtle.update_width_max(turtle.pos.x, 0.0);
                        turtle.update_height_max(turtle.pos.y,size.y + margin_size.y);
                    }
                },
                Flow::Down => {
                    turtle.pos.y = pos.y + size.y + margin_size.y + spacing.y;
                    if size.y < 0.0 {
                        turtle.update_width_max(turtle.pos.x, size.x + margin_size.x);
                        turtle.update_height_min(turtle.pos.y,0.0);
                    }
                    else {
                        turtle.update_width_max(turtle.pos.x, size.x + margin_size.x);
                        turtle.update_height_max(turtle.pos.y,0.0);
                    }
                },
                Flow::Overlay => { turtle.update_width_max(turtle.pos.x, size.x);
                    turtle.update_height_max(turtle.pos.y,size.y);
                }
            };
            
            self.turtle_walks.push(TurtleWalk {
                align_start,
                defer_index: turtle.defer_count,
                rect: Rect {pos, size: size + margin_size}
            });
            Rect {pos: pos + walk.margin.left_top() + spacing, size}
        }
    }
    
    fn walk_turtle_peek(&self, walk: Walk) -> Rect {
        if self.turtles.len() == 0{
            return Rect::default()
        }
        let turtle = self.turtles.last().unwrap();
        let size = dvec2(
            turtle.eval_width(walk.width, walk.margin, turtle.layout.flow),
            turtle.eval_height(walk.height, walk.margin, turtle.layout.flow)
        );
        
        if let Some(pos) = walk.abs_pos {
            Rect {pos: pos + walk.margin.left_top(), size}
        }
        else {
            let spacing = turtle.child_spacing(self.turtle_walks.len());
            let pos = turtle.pos;
            Rect {pos: pos + walk.margin.left_top() + spacing, size}
        }
    }
    
    fn move_align_list(&mut self, dx: f64, dy: f64, align_start: usize, align_end: usize, shift_clip: bool, turtle_shift:DVec2) {
        let dx = if dx.is_nan() {0.0}else {dx} + turtle_shift.x;
        let dy = if dy.is_nan() {0.0}else {dy} + turtle_shift.y;
        if dx.abs() <  0.000000001 && dy.abs() <  0.000000001{
            return 
        }
        let d = dvec2(dx, dy);
        let mut c = align_start;
        while c < align_end {
            let align_item = &mut self.align_list[c];
            match align_item {
                AlignEntry::Area(Area::Instance(inst)) => {
                    let draw_list = &mut self.cx.draw_lists[inst.draw_list_id];
                    let draw_item = &mut draw_list.draw_items[inst.draw_item_id];
                    let draw_call = draw_item.draw_call().unwrap();
                    let sh = &self.cx.draw_shaders[draw_call.draw_shader.draw_shader_id];
                    let inst_buf = draw_item.instances.as_mut().unwrap();
                    for i in 0..inst.instance_count {
                        if let Some(rect_pos) = sh.mapping.rect_pos {
                            inst_buf[inst.instance_offset + rect_pos + 0 + i * sh.mapping.instances.total_slots] += dx as f32;
                            inst_buf[inst.instance_offset + rect_pos + 1 + i * sh.mapping.instances.total_slots] += dy as f32;
                            if shift_clip{
                                if let Some(draw_clip) = sh.mapping.draw_clip {
                                    inst_buf[inst.instance_offset + draw_clip + 0 + i * sh.mapping.instances.total_slots] += dx as f32;
                                    inst_buf[inst.instance_offset + draw_clip + 1 + i * sh.mapping.instances.total_slots] += dy as f32;
                                    inst_buf[inst.instance_offset + draw_clip + 2 + i * sh.mapping.instances.total_slots] += dx as f32;
                                    inst_buf[inst.instance_offset + draw_clip + 3 + i * sh.mapping.instances.total_slots] += dy as f32;
                                }
                            }
                        }
                    }
                },
                AlignEntry::Area(Area::Rect(ra)) => {
                    let draw_list = &mut self.cx.draw_lists[ra.draw_list_id];
                    let rect_area = &mut draw_list.rect_areas[ra.rect_id];
                    rect_area.rect.pos += d;
                    if shift_clip{
                        rect_area.draw_clip.0 += d;
                        rect_area.draw_clip.1 += d;
                    }
                }
                AlignEntry::BeginTurtle(clip0, clip1)=>{
                    *clip0 += d;
                    *clip1 += d;
                }
                AlignEntry::SkipTurtle{skip} | AlignEntry::ShiftTurtle{skip,..} =>{
                    c = *skip;
                    continue;
                }
                _ => (),
            }
            c += 1;
        }
    }
    
    fn perform_nested_clipping_on_align_list_and_shift(&mut self, align_start:usize, align_end:usize){
        self.turtle_clips.clear();
        let mut i = align_start;
        while i < align_end{
            let align_item = &self.align_list[i];
            match align_item {
                AlignEntry::SkipTurtle{skip} =>{
                    i = *skip;
                    continue;
                }
                AlignEntry::ShiftTurtle{area, shift, skip} =>{
                    let rect = area.get_rect(self);
                    let skip = *skip;
                    self.move_align_list(rect.pos.x+shift.x, rect.pos.y+shift.y, i + 1, skip, true, dvec2(0.0,0.0));
                    i = skip;
                    continue;
                }
                AlignEntry::BeginTurtle(clip0, clip1)=>{
                    if let Some((tclip0, tclip1)) = self.turtle_clips.last(){
                        self.turtle_clips.push((
                            dvec2(clip0.x.max(tclip0.x),clip0.y.max(tclip0.y)),
                            dvec2(clip1.x.min(tclip1.x),clip1.y.min(tclip1.y)),
                        ));
                    }
                    else{
                        self.turtle_clips.push((*clip0, *clip1));
                    }
                }
                AlignEntry::EndTurtle=>{
                    self.turtle_clips.pop().unwrap();
                }
                AlignEntry::Area(Area::Instance(inst)) => if let Some((clip0, clip1)) = self.turtle_clips.last(){
                    let draw_list = &mut self.cx.draw_lists[inst.draw_list_id];
                    let draw_item = &mut draw_list.draw_items[inst.draw_item_id];
                    let draw_call = draw_item.draw_call().unwrap();
                    let sh = &self.cx.draw_shaders[draw_call.draw_shader.draw_shader_id];
                    let inst_buf = draw_item.instances.as_mut().unwrap();
                    for i in 0..inst.instance_count {
                        if let Some(draw_clip) = sh.mapping.draw_clip {
                            inst_buf[inst.instance_offset + draw_clip + 0 + i * sh.mapping.instances.total_slots] = clip0.x as f32;
                            inst_buf[inst.instance_offset + draw_clip + 1 + i * sh.mapping.instances.total_slots] = clip0.y as f32;
                            inst_buf[inst.instance_offset + draw_clip + 2 + i * sh.mapping.instances.total_slots] = clip1.x as f32;
                            inst_buf[inst.instance_offset + draw_clip + 3 + i * sh.mapping.instances.total_slots] = clip1.y as f32;
                        }
                    }
                },
                AlignEntry::Area(Area::Rect(ra)) => if let Some((clip0, clip1)) = self.turtle_clips.last(){
                    let draw_list = &mut self.cx.draw_lists[ra.draw_list_id];
                    let rect_area = &mut draw_list.rect_areas[ra.rect_id];
                    rect_area.draw_clip.0 = *clip0;
                    rect_area.draw_clip.1 = *clip1;
                }
                _ => (),
            }
            i += 1;
        }
    }
    
    fn get_turtle_walk_align_end(&self, i: usize) -> usize {
        if i < self.turtle_walks.len() - 1 {
            self.turtle_walks[i + 1].align_start
        }
        else {
            self.align_list.len()
        }
    }
    
    pub fn get_turtle_align_range(&self) -> TurtleAlignRange {
        TurtleAlignRange{
            start:  self.turtles.last().unwrap().align_start,
            end: self.align_list.len()
        }
    }
    
    pub fn shift_align_range(&mut self, range: &TurtleAlignRange, shift: DVec2) {
        self.move_align_list(shift.x, shift.y, range.start, range.end, true, dvec2(0.0,0.0));
    }
    
    pub fn add_rect_area(&mut self, area: &mut Area, rect: Rect) {
        self.add_aligned_rect_area(area, rect)
    }
}
pub struct TurtleAlignRange{
    start: usize,
    end: usize
}
impl Turtle {
    pub fn update_width_max(&mut self, pos:f64, dx: f64) {
        self.width_used = self.width_used.max((pos + dx) - self.origin.x);
    }
    
    pub fn update_height_max(&mut self, pos:f64, dy: f64) {
        self.height_used = self.height_used.max((pos + dy) - self.origin.y);
    }
    
    pub fn update_width_min(&mut self, pos:f64, dx: f64) {
        self.width_used = self.width_used.min((pos + dx) - self.origin.x);
    }
    
    pub fn update_height_min(&mut self, pos:f64, dy: f64) {
        self.height_used = self.height_used.min((pos + dy) - self.origin.y);
    }
    
    pub fn set_shift(&mut self, shift: DVec2) {
        self.shift = shift;
    }
    
    pub fn layout(&self)->&Layout{
        &self.layout
    }
    
    pub fn used(&self) -> DVec2 {
        dvec2(self.width_used, self.height_used)
    }
    
    pub fn set_used(&mut self, width_used: f64, height_used: f64) {
        self.width_used = width_used;
        self.height_used = height_used;
    }
    
    
    pub fn set_pos(&mut self, pos: DVec2) {
        self.pos = pos
    }
    
    fn child_spacing(&self, walks_len: usize) -> DVec2 {
        if self.turtle_walks_start < walks_len || self.defer_count > 0 {
            match self.layout.flow {
                Flow::Right => {
                    dvec2(self.layout.spacing, 0.0)
                }
                Flow::Down => {
                    dvec2(0.0, self.layout.spacing)
                }
                Flow::Overlay => {
                    dvec2(0.0, 0.0)
                }
            }
        }
        else {
            dvec2(0.0, 0.0)
        }
    }
    
    pub fn rect_is_visible(&self, geom: Rect) -> bool {
        let view = Rect {pos: self.origin + self.layout.scroll, size: dvec2(self.width, self.height)};
        return view.intersects(geom)
    }
    
    pub fn origin(&self) -> DVec2 {
        self.origin
    }
    
    pub fn rel_pos(&self) -> DVec2 {
        DVec2 {
            x: self.pos.x - self.origin.x,
            y: self.pos.y - self.origin.y
        }
    }
    
    pub fn pos(&self) -> DVec2 {
        self.pos
    }
    
    pub fn scroll(&self) -> DVec2 {
        self.layout.scroll
    }
    
    pub fn eval_width(&self, width: Size, margin: Margin, flow: Flow) -> f64 {
        return match width {
            Size::Fit => std::f64::NAN,
            Size::Fixed(v) => max_zero_keep_nan(v),
            Size::Fill => {
                match flow {
                    Flow::Right => {
                        max_zero_keep_nan(self.width_left() - margin.width())
                    },
                    Flow::Down | Flow::Overlay => {
                        let r = max_zero_keep_nan(self.width - self.layout.padding.width() - margin.width());
                        if r.is_nan() {
                            return self.width_used - margin.width() - self.layout.padding.right
                        }
                        return r
                    }
                }
            },
            Size::All=>self.width
        }
    }
    
    pub fn eval_height(&self, height: Size, margin: Margin, flow: Flow) -> f64 {
        return match height {
            Size::Fit => std::f64::NAN,
            Size::Fixed(v) => max_zero_keep_nan(v),
            Size::Fill => {
                match flow {
                    Flow::Right | Flow::Overlay => {
                        let r = max_zero_keep_nan(self.height - self.layout.padding.height() - margin.height());
                        if r.is_nan() {
                            return self.height_used - margin.height() - self.layout.padding.bottom
                        }
                        return r
                    }
                    Flow::Down => {
                        max_zero_keep_nan(self.height_left() - margin.height())
                    }
                }
            }
            Size::All=>self.height
        }
    }
    
    pub fn rect(&self) -> Rect {
        Rect {
            pos: self.origin,
            size: dvec2(self.width, self.height)
        }
    }
    
   pub fn unscrolled_rect(&self) -> Rect {
        Rect {
            pos: self.origin + self.layout.scroll,
            size: dvec2(self.width, self.height)
        }
    }
    
    pub fn padded_rect_used(&self) -> Rect {
        Rect {
            pos: self.origin + self.layout.padding.left_top(),
            size: self.used() - self.layout.padding.left_top()
        }
    }
    pub fn rect_left(&self) -> Rect {
        Rect {
            pos: self.pos,
            size: dvec2(self.width_left(), self.height_left())
        }
    }
    
    pub fn padded_rect(&self) -> Rect {
        Rect {
            pos: self.origin + self.layout.padding.left_top(),
            size: dvec2(self.width, self.height) - self.layout.padding.size()
        }
    }
    
    pub fn size(&self) -> DVec2 {
        dvec2(self.width, self.height)
    }
    
    pub fn width_left(&self) -> f64 {
        return max_zero_keep_nan(self.width - self.width_used - self.layout.padding.right);
    }
    
    pub fn height_left(&self) -> f64 {
        return max_zero_keep_nan(self.height - self.height_used - self.layout.padding.bottom);
    }
    
    pub fn padded_height_or_used(&self) -> f64 {
        let r = max_zero_keep_nan(self.height - self.layout.padding.height());
        if r.is_nan() {
            self.height_used - self.layout.padding.bottom
        }
        else {
            r
        }
    }
    
    pub fn padded_width_or_used(&self) -> f64 {
        let r = max_zero_keep_nan(self.width - self.layout.padding.width());
        if r.is_nan() {
            self.width_used - self.layout.padding.right
        }
        else {
            r
        }
    }
}
impl DeferWalk {
    
    pub fn resolve(&mut self, cx: &Cx2d) -> Walk {
        match self{
            Self::Resolved(walk)=>{*walk},
            Self::Unresolved{pos, defer_index, margin, other_axis}=>{
                let turtle = cx.turtles.last().unwrap();
                let walk = match turtle.layout.flow {
                    Flow::Right => {
                        let left = turtle.width_left();
                        let part = left / turtle.defer_count as f64;
                        Walk {
                            abs_pos: Some(*pos + dvec2(part * *defer_index as f64, 0.)),
                            margin: *margin,
                            width: Size::Fixed(part),
                            height: *other_axis
                        }
                    },
                    Flow::Down => {
                        let left = turtle.height_left();
                        let part = left / turtle.defer_count as f64;
                        Walk {
                            abs_pos: Some(*pos + dvec2(0., part * *defer_index as f64)),
                            margin: *margin,
                            height: Size::Fixed(part),
                            width: *other_axis
                        }
                    }
                    Flow::Overlay => panic!()
                };
                *self = DeferWalk::Resolved(walk);
                walk
            }
        }
    }
    
}
impl Layout {
    pub fn flow_right() -> Self {
        Self {
            flow: Flow::Right,
            ..Self::default()
        }
    }
    
    pub fn flow_down() -> Self {
        Self {
            flow: Flow::Down,
            ..Self::default()
        }
    }
    
    pub fn with_scroll(mut self, v: DVec2) -> Self {
        self.scroll = v;
        self
    }
    
    pub fn with_align_x(mut self, v: f64) -> Self {
        self.align.x = v;
        self
    }
    
    pub fn with_align_y(mut self, v: f64) -> Self {
        self.align.y = v;
        self
    }
    
    pub fn with_clip(mut self, clip_x:bool, clip_y:bool) -> Self {
        self.clip_x = clip_x;
        self.clip_y = clip_y;
        self
    }
    
    pub fn with_padding_all(mut self, v: f64) -> Self {
        self.padding = Padding {left: v, right: v, top: v, bottom: v};
        self
    }
    
    pub fn with_padding_top(mut self, v: f64) -> Self {
        self.padding.top = v;
        self
    }
    
    pub fn with_padding_right(mut self, v: f64) -> Self {
        self.padding.right = v;
        self
    }
    
    pub fn with_padding_bottom(mut self, v: f64) -> Self {
        self.padding.bottom = v;
        self
    }
    
    pub fn with_padding_left(mut self, v: f64) -> Self {
        self.padding.left = v;
        self
    }
    
    pub fn with_padding(mut self, v: Padding) -> Self {
        self.padding = v;
        self
    }
}
impl Walk {
    pub fn empty() -> Self {
        Self {
            abs_pos: None,
            margin: Margin::default(),
            width: Size::Fixed(0.0),
            height: Size::Fixed(0.0),
        }
    }
    
    pub fn size(w: Size, h: Size) -> Self {
        Self {
            abs_pos: None,
            margin: Margin::default(),
            width: w,
            height: h,
        }
    }
    pub fn fixed(w:f64, h:f64) -> Self {
        Self {
            abs_pos: None,
            margin: Margin::default(),
            width: Size::Fixed(w),
            height: Size::Fixed(h),
        }
    }
        
    pub fn fixed_size(size: DVec2) -> Self {
        Self {
            abs_pos: None,
            margin: Margin::default(),
            width: Size::Fixed(size.x),
            height: Size::Fixed(size.y),
        }
    }
    
    pub fn fit() -> Self {
        Self {
            abs_pos: None,
            margin: Margin::default(),
            width: Size::Fit,
            height: Size::Fit,
        }
    }
    
    pub fn fill() -> Self {
        Self {
            abs_pos: None,
            margin: Margin::default(),
            width: Size::Fill,
            height: Size::Fill,
        }
    }
    
    pub fn fill_fit() -> Self {
        Self {
            abs_pos: None,
            margin: Margin::default(),
            width: Size::Fill,
            height: Size::Fit,
        }
    }
    
    pub fn with_abs_pos(mut self, v: DVec2) -> Self {
        self.abs_pos = Some(v);
        self
    }
    pub fn with_margin_all(mut self, v: f64) -> Self {
        self.margin = Margin {left: v, right: v, top: v, bottom: v};
        self
    }
    
    pub fn with_margin_top(mut self, v: f64) -> Self {
        self.margin.top = v;
        self
    }
    
    pub fn with_margin_right(mut self, v: f64) -> Self {
        self.margin.right = v;
        self
    }
    
    pub fn with_margin_bottom(mut self, v: f64) -> Self {
        self.margin.bottom = v;
        self
    }
    
    pub fn with_margin_left(mut self, v: f64) -> Self {
        self.margin.left = v;
        self
    }
    
    pub fn with_margin(mut self, v: Margin) -> Self {
        self.margin = v;
        self
    }
    
    pub fn with_add_padding(mut self, v: Padding) -> Self {
        self.margin.top += v.top;
        self.margin.left += v.left;
        self.margin.right += v.right;
        self.margin.bottom += v.bottom;
        self
    }
}
impl Padding {
    pub fn left_top(&self) -> DVec2 {
        dvec2(self.left, self.top)
    }
    pub fn right_bottom(&self) -> DVec2 {
        dvec2(self.right, self.bottom)
    }
    pub fn size(&self) -> DVec2 {
        dvec2(self.left + self.right, self.top + self.bottom)
    }
    pub fn width(&self) -> f64 {
        self.left + self.right
    }
    pub fn height(&self) -> f64 {
        self.top + self.bottom
    }
}
impl LiveHook for Padding {
    fn skip_apply(&mut self, _cx: &mut Cx, _apply_from: ApplyFrom, index: usize, nodes: &[LiveNode]) -> Option<usize> {
        if let Some(v) = nodes[index].value.as_float(){
            *self = Self {left: v, top: v, right: v, bottom: v};
            Some(index + 1)
        }
        else{
            None
        }
    }
}
impl Default for Flow {
    fn default() -> Self {Self::Down}
}
impl LiveHook for Size {
    fn skip_apply(&mut self, cx: &mut Cx, _apply_from: ApplyFrom, index: usize, nodes: &[LiveNode]) -> Option<usize> {
        match &nodes[index].value {
            LiveValue::Expr {..} => {
                match live_eval(&cx.live_registry.clone().borrow(), index, &mut (index + 1), nodes) {
                    Ok(ret) => match ret {
                        LiveEval::Float64(v) => {
                            *self = Self::Fixed(v);
                        }
                        LiveEval::Int64(v) => {
                            *self = Self::Fixed(v as f64);
                        }
                        _ => {
                            cx.apply_error_wrong_expression_type_for_primitive(live_error_origin!(), index, nodes, "bool", ret);
                        }
                    }
                    Err(err) => cx.apply_error_eval(err)
                }
                Some(nodes.skip_node(index))
            }
            LiveValue::Float32(v) => {
                *self = Self::Fixed(*v as f64);
                Some(index + 1)
            }
            LiveValue::Float64(v) => {
                *self = Self::Fixed(*v);
                Some(index + 1)
            }
            LiveValue::Int64(v) => {
                *self = Self::Fixed(*v as f64);
                Some(index + 1)
            }
            _ => None
        }
    }
}
impl Default for Size {
    fn default() -> Self {
        Size::Fill
    }
}
impl Size {
    pub fn fixed_or_zero(&self) -> f64 {
        match self {
            Self::Fixed(v) => *v,
            _ => 0.
        }
    }
    
    pub fn fixed_or_nan(&self) -> f64 {
        match self {
            Self::Fixed(v) => max_zero_keep_nan(*v),
            _ => std::f64::NAN,
        }
    }
    pub fn is_fixed(&self) -> bool {
        match self {
            Self::Fixed(_) => true,
            _ => false
        }
    }
    pub fn is_fit(&self) -> bool {
        match self {
            Self::Fit => true,
            _ => false
        }
    }
    
    pub fn is_fill(&self) -> bool {
        match self {
            Self::Fill => true,
            _ => false
        }
    }
}
fn max_zero_keep_nan(v: f64) -> f64 {
    if v.is_nan() {
        v
    }
    else {
        f64::max(v, 0.0)
    }
}