use {
    crate::{
        makepad_platform::*,
        turtle::{Walk, Size, Align},
        font_atlas::{CxFontsAtlasTodo, CxFont, CxFontsAtlas, Font},
        draw_list_2d::ManyInstances,
        geometry::GeometryQuad2D,
        cx_2d::Cx2d
    },
};
live_design!{
    
    DrawText = {{DrawText}} {
        color: #fff
        
        uniform brightness: float
        uniform curve: float
        
        texture tex: texture2d
        
        varying tex_coord1: vec2
        varying tex_coord2: vec2
        varying tex_coord3: vec2
        varying clipped: vec2
        varying pos: vec2
        
        fn vertex(self) -> vec4 {
            let min_pos = vec2(self.rect_pos.x, self.rect_pos.y)
            let max_pos = vec2(self.rect_pos.x + self.rect_size.x, self.rect_pos.y - self.rect_size.y)
            
            self.clipped = clamp(
                mix(min_pos, max_pos, self.geom_pos),
                self.draw_clip.xy,
                self.draw_clip.zw
            )
            
            let normalized: vec2 = (self.clipped - min_pos) / vec2(self.rect_size.x, -self.rect_size.y)
            self.tex_coord1 = mix(
                self.font_t1.xy,
                self.font_t2.xy,
                normalized.xy
            )
            self.pos = normalized;
            return self.camera_projection * (self.camera_view * (self.view_transform * vec4(
                self.clipped.x,
                self.clipped.y,
                self.char_depth + self.draw_zbias,
                1.
            )))
        }
        
        fn get_color(self) -> vec4 {
            return self.color;
        }
        fn blend_color(self, incol:vec4)->vec4{
            return incol
        }
        fn pixel(self) -> vec4 {
            
            let s = sample2d_rt(self.tex, self.tex_coord1.xy).x;
            s = pow(s, self.curve);
            let col = self.get_color(); return self.blend_color(vec4(s * col.rgb * self.brightness * col.a, s * col.a));
        }
    }
}
#[derive(Clone, Live, LiveHook)]
#[live_ignore]
pub struct TextStyle {
    #[live()] pub font: Font,
    #[live(9.0)] pub font_size: f64,
    #[live(1.0)] pub brightness: f32,
    #[live(0.6)] pub curve: f32,
    #[live(1.4)] pub line_spacing: f64,
    #[live(1.1)] pub top_drop: f64,
    #[live(1.3)] pub height_factor: f64,
}
#[derive(Clone, Live, LiveHook)]
#[live_ignore]
pub enum TextWrap {
    #[pick] Ellipsis,
    Word,
    Line
}
struct WordIterator<'a> {
    char_iter: Option<std::str::CharIndices<'a >>,
    eval_width: f64,
    word_width: f64,
    word_start: usize,
    last_is_whitespace: bool,
    last_char: char,
    last_index: usize,
    font_size_total: f64,
}
struct WordIteratorItem {
    start: usize,
    end: usize,
    width: f64,
    with_newline: bool
}
impl<'a> WordIterator<'a> {
    fn new(char_iter: std::str::CharIndices<'a>, eval_width: f64, font_size_total: f64) -> Self {
        Self {
            eval_width,
            char_iter: Some(char_iter),
            last_is_whitespace: false,
            word_width: 0.0,
            word_start: 0,
            last_char: '\0',
            last_index: 0,
            font_size_total
        }
    }
    fn next_word(&mut self, font: &mut CxFont) -> Option<WordIteratorItem> {
        if let Some(char_iter) = &mut self.char_iter {
            while let Some((i, c)) = char_iter.next() {
                self.last_index = i;
                self.last_char = c;
                let ret = WordIteratorItem {
                    start: self.word_start,
                    end: i,
                    width: self.word_width,
                    with_newline: false
                };
                
                let adv = if let Some(glyph) = font.get_glyph(c) {
                    glyph.horizontal_metrics.advance_width * self.font_size_total
                }else {0.0};
                
                if c == '\r' {
                    continue;
                }
                if c == '\n' {
                    self.last_is_whitespace = false;
                    self.word_start = i;
                    self.word_width = 0.0;
                    return Some(WordIteratorItem {with_newline: true, end: i, ..ret})
                }
                else if c.is_whitespace() { self.last_is_whitespace = true;
                }
                else if self.last_is_whitespace {
                    self.last_is_whitespace = false;
                    self.word_start = i;
                    self.word_width = adv;
                    return Some(ret);
                }
                if self.word_width + adv >= self.eval_width {
                    self.word_start = i;
                    self.word_width = adv;
                    return Some(ret);
                }
                self.word_width += adv;
            }
            self.char_iter = None;
            
            let mut buffer = [0; 4];
            let char_bytes_len = self.last_char.encode_utf8(&mut buffer).len();
            
            return Some(WordIteratorItem {
                start: self.word_start,
                end: self.last_index + char_bytes_len,
                width: self.word_width,
                with_newline: false
            });
        }
        else {
            None
        }
    }
}
pub struct TextGeom {
    pub eval_width: f64,
    pub eval_height: f64,
    pub measured_width: f64,
    pub measured_height: f64,
    pub ellip_pt: Option<(usize, f64, usize)>
}
#[derive(Live)]
#[repr(C)]
pub struct DrawText {
    #[rust] pub many_instances: Option<ManyInstances>,
    
    #[live] pub geometry: GeometryQuad2D,
    #[live] pub text_style: TextStyle,
    #[live] pub wrap: TextWrap,
    #[live(1.0)] pub font_scale: f64,
    #[live(1.0)] pub draw_depth: f32,
    
    #[deref] pub draw_vars: DrawVars,
    #[live] pub color: Vec4,
    #[calc] pub font_t1: Vec2,
    #[calc] pub font_t2: Vec2,
    #[calc] pub rect_pos: Vec2,
    #[calc] pub rect_size: Vec2,
    #[calc] pub draw_clip: Vec4,
    #[calc] pub char_depth: f32,
    #[calc] pub delta: Vec2,
    #[calc] pub font_size: f32,
    #[calc] pub advance: f32,
}
impl LiveHook for DrawText {
    fn before_apply(&mut self, cx: &mut Cx, apply_from: ApplyFrom, index: usize, nodes: &[LiveNode]) {
        self.draw_vars.before_apply_init_shader(cx, apply_from, index, nodes, &self.geometry);
    }
    fn after_apply(&mut self, cx: &mut Cx, apply_from: ApplyFrom, index: usize, nodes: &[LiveNode]) {
        self.draw_vars.after_apply_update_self(cx, apply_from, index, nodes, &self.geometry);
    }
}
impl DrawText {
    
    pub fn draw(&mut self, cx: &mut Cx2d, pos: DVec2, val: &str) {
        self.draw_inner(cx, pos, val, &mut *cx.fonts_atlas_rc.clone().0.borrow_mut());
        if self.many_instances.is_some() {
            self.end_many_instances(cx)
        }
    }
    
    pub fn draw_rel(&mut self, cx: &mut Cx2d, pos: DVec2, val: &str) {
        self.draw_inner(cx, pos + cx.turtle().origin(), val, &mut *cx.fonts_atlas_rc.clone().0.borrow_mut());
        if self.many_instances.is_some() {
            self.end_many_instances(cx)
        }
    }
    
    pub fn draw_abs(&mut self, cx: &mut Cx2d, pos: DVec2, val: &str) {
        self.draw_inner(cx, pos, val, &mut *cx.fonts_atlas_rc.clone().0.borrow_mut());
        if self.many_instances.is_some() {
            self.end_many_instances(cx)
        }
    }
    
    pub fn begin_many_instances(&mut self, cx: &mut Cx2d) {
        let fonts_atlas_rc = cx.fonts_atlas_rc.clone();
        let fonts_atlas = fonts_atlas_rc.0.borrow();
        self.begin_many_instances_internal(cx, &*fonts_atlas);
    }
    
    fn begin_many_instances_internal(&mut self, cx: &mut Cx2d, fonts_atlas: &CxFontsAtlas) {
        self.update_draw_call_vars(fonts_atlas);
        let mi = cx.begin_many_aligned_instances(&self.draw_vars);
        self.many_instances = mi;
    }
    
    pub fn end_many_instances(&mut self, cx: &mut Cx2d) {
        if let Some(mi) = self.many_instances.take() {
            let new_area = cx.end_many_instances(mi);
            self.draw_vars.area = cx.update_area_refs(self.draw_vars.area, new_area);
        }
    }
    
    pub fn new_draw_call(&self, cx: &mut Cx2d) {
        cx.new_draw_call(&self.draw_vars);
    }
    
    pub fn update_draw_call_vars(&mut self, font_atlas: &CxFontsAtlas) {
        self.draw_vars.texture_slots[0] = Some(font_atlas.texture_id);
        self.draw_vars.user_uniforms[0] = self.text_style.brightness;
        self.draw_vars.user_uniforms[1] = self.text_style.curve;
    }
    
    fn draw_inner(&mut self, cx: &mut Cx2d, pos: DVec2, chunk: &str, fonts_atlas: &mut CxFontsAtlas) {
        if !self.draw_vars.can_instance()
            || pos.x.is_nan()
            || pos.y.is_nan()
            || self.text_style.font.font_id.is_none() {
            return
        }
        let font_id = self.text_style.font.font_id.unwrap();
        
        if fonts_atlas.fonts[font_id].is_none() {
            return
        }
        
        let mut walk_x = pos.x;
        if walk_x.is_infinite() || walk_x.is_nan() {
            return
        }
        if !self.many_instances.is_some() {
            self.begin_many_instances_internal(cx, fonts_atlas);
        }
        
        let cxfont = fonts_atlas.fonts[font_id].as_mut().unwrap();
        let dpi_factor = cx.current_dpi_factor();
        
        let atlas_page_id = cxfont.get_atlas_page_id(dpi_factor, self.text_style.font_size);
        
        let font = &mut cxfont.ttf_font;
        let owned_font_face = &cxfont.owned_font_face;
        
        let font_size_logical = self.text_style.font_size * 96.0 / (72.0 * font.units_per_em);
        let font_size_pixels = font_size_logical * dpi_factor;
        
        let atlas_page = &mut cxfont.atlas_pages[atlas_page_id];
        
        let mi = if let Some(mi) = &mut self.many_instances {mi} else {return};
        let zbias_step = 0.00001;
        let mut char_depth = self.draw_depth;
        
        let mut rustybuzz_buffer = rustybuzz::UnicodeBuffer::new();
        
        let bidi_info = unicode_bidi::BidiInfo::new(chunk, None);
        
        if bidi_info.paragraphs.len() == 1 {
            let runs_with_level_and_range = {
                let para = &bidi_info.paragraphs[0];
                let (adjusted_levels, runs) = bidi_info.visual_runs(para, para.range.clone());
                runs.into_iter().map(move | run_range | (adjusted_levels[run_range.start], run_range))
            };
            
            for (run_level, run_range) in runs_with_level_and_range {
                let (glyph_ids, new_rustybuzz_buffer) = cxfont
                    .shape_cache
                    .get_or_compute_glyph_ids(
                    (
                            if run_level.is_rtl() {
                                rustybuzz::Direction::RightToLeft
                            } else {
                                rustybuzz::Direction::LeftToRight
                            },
                            &bidi_info.text[run_range]
                        ),
                        rustybuzz_buffer,
                        owned_font_face
                    );
                rustybuzz_buffer = new_rustybuzz_buffer;
                for &glyph_id in glyph_ids {
                    let glyph = owned_font_face.with_ref(|face| font.get_glyph_by_id(face, glyph_id).unwrap());
                    
                    let advance = glyph.horizontal_metrics.advance_width * font_size_logical * self.font_scale;
                    
                    let w = ((glyph.bounds.p_max.x - glyph.bounds.p_min.x) * font_size_pixels).ceil() + 1.0;
                    let h = ((glyph.bounds.p_max.y - glyph.bounds.p_min.y) * font_size_pixels).ceil() + 1.0;
                    
                    let min_pos_x = walk_x + font_size_logical * glyph.bounds.p_min.x;
                    let min_pos_y = pos.y - font_size_logical * glyph.bounds.p_min.y + self.text_style.font_size * self.text_style.top_drop;
                    
                    let subpixel_x_fract = min_pos_x - (min_pos_x * dpi_factor).floor() / dpi_factor;
                    let subpixel_y_fract = min_pos_y - (min_pos_y * dpi_factor).floor() / dpi_factor;
                    let subpixel_id = if self.text_style.font_size>32.0 {
                        0
                    }
                    else { ((subpixel_y_fract * dpi_factor * 7.0) as usize) << 3 |
                        (subpixel_x_fract * dpi_factor * 7.0) as usize
                    };
                    
                    let subpixel_map = if let Some(tc) = atlas_page.atlas_glyphs.get_mut(&glyph_id){
                        tc
                    }
                    else{
                        atlas_page.atlas_glyphs.insert(glyph_id, [None; crate::font_atlas::ATLAS_SUBPIXEL_SLOTS]);
                        atlas_page.atlas_glyphs.get_mut(&glyph_id).unwrap()
                    };
                    
                    let tc = if let Some(tc) = &subpixel_map[subpixel_id]{
                        tc
                    }
                    else {
                        fonts_atlas.alloc.todo.push(CxFontsAtlasTodo {
                            subpixel_x_fract,
                            subpixel_y_fract,
                            font_id,
                            atlas_page_id,
                            glyph_id,
                            subpixel_id
                        });
                        
                        subpixel_map[subpixel_id] = Some(
                            fonts_atlas.alloc.alloc_atlas_glyph(w, h)
                        );
                        subpixel_map[subpixel_id].as_ref().unwrap()
                    };
                    
                    let delta_x = font_size_logical * self.font_scale * glyph.bounds.p_min.x - subpixel_x_fract;
                    let delta_y = -font_size_logical * self.font_scale * glyph.bounds.p_min.y + self.text_style.font_size * self.font_scale * self.text_style.top_drop - subpixel_y_fract;
                    self.font_t1 = tc.t1;
                    self.font_t2 = tc.t2;
                    self.rect_pos = dvec2(walk_x + delta_x, pos.y + delta_y).into();
                    self.rect_size = dvec2(w * self.font_scale / dpi_factor, h * self.font_scale / dpi_factor).into();
                    self.char_depth = char_depth;
                    self.delta.x = delta_x as f32;
                    self.delta.y = delta_y as f32;
                    self.font_size = self.text_style.font_size as f32;
                    self.advance = advance as f32; char_depth += zbias_step;
                    mi.instances.extend_from_slice(self.draw_vars.as_slice());
                    walk_x += advance;
                }
            }
        }
        
    }
    pub fn compute_geom(&self, cx: &Cx2d, walk: Walk, text: &str) -> Option<TextGeom> {
        self.compute_geom_inner(cx, walk, text, &mut *cx.fonts_atlas_rc.0.borrow_mut())
    }
    
    fn compute_geom_inner(&self, cx: &Cx2d, walk: Walk, text: &str, fonts_atlas: &mut CxFontsAtlas) -> Option<TextGeom> {
        let font_id = self.text_style.font.font_id.unwrap();
        
        if fonts_atlas.fonts[font_id].is_none() {
            return None
        }
        
        let font_size_logical = self.text_style.font_size * 96.0 / (72.0 * fonts_atlas.fonts[font_id].as_ref().unwrap().ttf_font.units_per_em);
        let line_height = self.text_style.font_size * self.text_style.height_factor * self.font_scale;
        let eval_width = cx.turtle().eval_width(walk.width, walk.margin, cx.turtle().layout().flow);
        let eval_height = cx.turtle().eval_height(walk.height, walk.margin, cx.turtle().layout().flow);
        
        match if walk.width.is_fit() {&TextWrap::Line}else {&self.wrap} {
            TextWrap::Ellipsis => {
                let ellip_width = if let Some(glyph) = fonts_atlas.fonts[font_id].as_mut().unwrap().get_glyph('.') {
                    glyph.horizontal_metrics.advance_width * font_size_logical * self.font_scale
                }
                else {
                    0.0
                };
                
                let mut measured_width = 0.0;
                let mut ellip_pt = None;
                for (i, c) in text.chars().enumerate() {
                    
                    if measured_width + ellip_width * 3.0 < eval_width {
                        ellip_pt = Some((i, measured_width, 3));
                    }
                    if let Some(glyph) = fonts_atlas.fonts[font_id].as_mut().unwrap().get_glyph(c) {
                        let adv = glyph.horizontal_metrics.advance_width * font_size_logical * self.font_scale;
                        if measured_width + adv >= eval_width { if ellip_pt.is_none() {
                                let dots = if ellip_width * 3.0 < eval_width {3}
                                else if ellip_width * 2.0 < eval_width {2}
                                else if ellip_width < eval_width {1}
                                else {0};
                                ellip_pt = Some((0, 0.0, dots));
                            }
                            return Some(TextGeom {
                                eval_width,
                                eval_height,
                                measured_width: ellip_pt.unwrap().1 + ellip_width,
                                measured_height: line_height,
                                ellip_pt
                            })
                        }
                        measured_width += adv;
                    }
                }
                
                Some(TextGeom {
                    eval_width,
                    eval_height,
                    measured_width,
                    measured_height: line_height,
                    ellip_pt: None
                })
            }
            TextWrap::Word => {
                let mut max_width = 0.0;
                let mut measured_width = 0.0;
                let mut measured_height = line_height;
                
                let mut iter = WordIterator::new(text.char_indices(), eval_width, font_size_logical * self.font_scale);
                while let Some(word) = iter.next_word(fonts_atlas.fonts[font_id].as_mut().unwrap()) {
                    if measured_width + word.width >= eval_width {
                        measured_height += line_height * self.text_style.line_spacing;
                        measured_width = word.width;
                    }
                    else {
                        measured_width += word.width;
                    }
                    if measured_width > max_width {max_width = measured_width}
                    if word.with_newline {
                        measured_height += line_height * self.text_style.line_spacing;
                        measured_width = 0.0;
                    }
                }
                
                Some(TextGeom {
                    eval_width,
                    eval_height,
                    measured_width: max_width,
                    measured_height,
                    ellip_pt: None
                })
            }
            TextWrap::Line => {
                let mut max_width = 0.0;
                let mut measured_width = 0.0;
                let mut measured_height = line_height;
                
                for c in text.chars() {
                    if c == '\n' {
                        measured_height += line_height * self.text_style.line_spacing;
                    }
                    if let Some(glyph) = fonts_atlas.fonts[font_id].as_mut().unwrap().get_glyph(c) {
                        let adv = glyph.horizontal_metrics.advance_width * font_size_logical * self.font_scale;
                        measured_width += adv;
                    }
                    if measured_width > max_width {
                        max_width = measured_width;
                    }
                }
                Some(TextGeom {
                    eval_width,
                    eval_height,
                    measured_width: max_width,
                    measured_height: measured_height,
                    ellip_pt: None
                })
            }
        }
    }
    
    
    pub fn draw_walk(&mut self, cx: &mut Cx2d, walk: Walk, align: Align, text: &str) {
        let font_id = if let Some(font_id) = self.text_style.font.font_id{font_id}else{
            return
        };
        let fonts_atlas_rc = cx.fonts_atlas_rc.clone();
        let mut fonts_atlas = fonts_atlas_rc.0.borrow_mut();
        let fonts_atlas = &mut*fonts_atlas;
        
        if text.len() == 0 {
            return
        }
        if let Some(geom) = self.compute_geom_inner(cx, walk, text, fonts_atlas) {
            let height = if walk.height.is_fit() {
                geom.measured_height
            } else {
                geom.eval_height
            };
            let y_align = (height - geom.measured_height) * align.y;
            
            match if walk.width.is_fit() {&TextWrap::Line}else {&self.wrap} {
                TextWrap::Ellipsis => {
                    if let Some((ellip, at_x, dots)) = geom.ellip_pt {
                        let rect = cx.walk_turtle(Walk {
                            abs_pos: walk.abs_pos,
                            margin: walk.margin,
                            width: Size::Fixed(geom.eval_width),
                            height: Size::Fixed(height)
                        });
                        
                        self.draw_inner(cx, rect.pos + dvec2(0.0, y_align), &text[0..ellip], fonts_atlas);
                        self.draw_inner(cx, rect.pos + dvec2(at_x, y_align), &"..."[0..dots], fonts_atlas);
                    }
                    else { let rect = cx.walk_turtle(Walk {
                            abs_pos: walk.abs_pos,
                            margin: walk.margin,
                            width: Size::Fixed(geom.eval_width),
                            height: Size::Fixed(
                                if walk.height.is_fit() {
                                    geom.measured_height
                                } else {
                                    geom.eval_height
                                }
                            )
                        });
                        let x_align = (geom.eval_width - geom.measured_width) * align.x;
                        self.draw_inner(cx, rect.pos + dvec2(x_align, y_align), text, fonts_atlas);
                    }
                }
                TextWrap::Word => {
                    let font_size_logical = self.text_style.font_size * 96.0 / (72.0 * fonts_atlas.fonts[font_id].as_ref().unwrap().ttf_font.units_per_em);
                    let line_height = self.text_style.font_size * self.text_style.height_factor * self.font_scale;
                    
                    let rect = cx.walk_turtle(Walk {
                        abs_pos: walk.abs_pos,
                        margin: walk.margin,
                        width: Size::Fixed(geom.eval_width),
                        height: Size::Fixed(geom.measured_height)
                    });
                    let mut pos = dvec2(0.0, 0.0);
                    
                    let mut iter = WordIterator::new(text.char_indices(), geom.eval_width, font_size_logical * self.font_scale);
                    while let Some(word) = iter.next_word(fonts_atlas.fonts[font_id].as_mut().unwrap()) {
                        if pos.x + word.width >= geom.eval_width {
                            pos.y += line_height * self.text_style.line_spacing;
                            pos.x = 0.0;
                        }
                        self.draw_inner(cx, rect.pos + pos, &text[word.start..word.end], fonts_atlas);
                        pos.x += word.width;
                        
                        if word.with_newline {
                            pos.y += line_height * self.text_style.line_spacing;
                            pos.x = 0.0;
                        }
                    }
                }
                TextWrap::Line => {
                    let line_height = self.text_style.font_size * self.text_style.height_factor * self.font_scale;
                    let rect = cx.walk_turtle(Walk {
                        abs_pos: walk.abs_pos,
                        margin: walk.margin,
                        width: Size::Fixed(geom.measured_width),
                        height: Size::Fixed(height)
                    });
                    let mut ypos = 0.0;
                    for line in text.split('\n') {
                        self.draw_inner(cx, rect.pos + dvec2(0.0, y_align + ypos), line, fonts_atlas);
                        ypos += line_height * self.text_style.line_spacing;
                    }
                    
                }
            }
        }
        if self.many_instances.is_some() {
            self.end_many_instances(cx)
        }
    }
    
    pub fn closest_offset(&self, cx: &Cx, pos: DVec2) -> Option<usize> {
        let area = &self.draw_vars.area;
        
        if !area.is_valid(cx) {
            return None
        }
        let line_spacing = self.get_line_spacing();
        let rect_pos = area.get_read_ref(cx, live_id!(rect_pos), ShaderTy::Vec2).unwrap();
        let delta = area.get_read_ref(cx, live_id!(delta), ShaderTy::Vec2).unwrap();
        let advance = area.get_read_ref(cx, live_id!(advance), ShaderTy::Float).unwrap();
        let mut last_y = None;
        for i in 0..rect_pos.repeat {
            let index = rect_pos.stride * i;
            let x = rect_pos.buffer[index + 0] as f64 - delta.buffer[index + 0] as f64;
            let y = rect_pos.buffer[index + 1] - delta.buffer[index + 1];
            if last_y.is_none() {last_y = Some(y)}
            let advance = advance.buffer[index + 0] as f64;
            if i > 0 && y > last_y.unwrap() && pos.y < last_y.unwrap() as f64 + line_spacing as f64 {
                return Some(i - 1)
            }
            if pos.x < x + advance * 0.5 && pos.y < y as f64 + line_spacing as f64 {
                return Some(i)
            }
            last_y = Some(y)
        }
        return Some(rect_pos.repeat);
        
    }
    
    pub fn get_selection_rects(&self, cx: &Cx, start: usize, end: usize, shift: DVec2, pad: DVec2) -> Vec<Rect> {
        let area = &self.draw_vars.area;
        
        if !area.is_valid(cx) {
            return Vec::new();
        }
        
        let rect_pos = area.get_read_ref(cx, live_id!(rect_pos), ShaderTy::Vec2).unwrap();
        let delta = area.get_read_ref(cx, live_id!(delta), ShaderTy::Vec2).unwrap();
        let advance = area.get_read_ref(cx, live_id!(advance), ShaderTy::Float).unwrap();
        
        if rect_pos.repeat == 0 || start >= rect_pos.repeat{
            return Vec::new();
        }
        let index = start * rect_pos.stride;
        let start_x = rect_pos.buffer[index + 0] - delta.buffer[index + 0]; let start_y = rect_pos.buffer[index + 1] - delta.buffer[index + 1];
        let line_spacing = self.get_line_spacing();
        let mut last_y = start_y;
        let mut min_x = start_x;
        let mut last_x = start_x;
        let mut last_advance = advance.buffer[index + 0];
        let mut out = Vec::new();
        for index in start..end {
            if index >= rect_pos.repeat{
                break;
            }
            let index = index * rect_pos.stride;
            let end_x = rect_pos.buffer[index + 0] - delta.buffer[index + 0];
            let end_y = rect_pos.buffer[index + 1] - delta.buffer[index + 1];
            last_advance = advance.buffer[index + 0];
            if end_y > last_y { out.push(Rect {
                    pos: dvec2(min_x as f64, last_y as f64) + shift,
                    size: dvec2((last_x - min_x + last_advance) as f64, line_spacing) + pad
                });
                min_x = end_x;
                last_y = end_y;
            }
            last_x = end_x;
        }
        out.push(Rect {
            pos: dvec2(min_x as f64, last_y as f64) + shift,
            size: dvec2((last_x - min_x + last_advance) as f64, line_spacing) + pad
        });
        out
    }
    
    
    pub fn get_char_count(&self, cx: &Cx) -> usize {
        let area = &self.draw_vars.area;
        if !area.is_valid(cx) {
            return 0
        }
        let rect_pos = area.get_read_ref(cx, live_id!(rect_pos), ShaderTy::Vec2).unwrap();
        rect_pos.repeat
    }
    
    pub fn get_cursor_pos(&self, cx: &Cx, pos: f32, index: usize) -> Option<DVec2> {
        let area = &self.draw_vars.area;
        
        if !area.is_valid(cx) {
            return None
        }
        
        let rect_pos = area.get_read_ref(cx, live_id!(rect_pos), ShaderTy::Vec2).unwrap();
        let delta = area.get_read_ref(cx, live_id!(delta), ShaderTy::Vec2).unwrap();
        let advance = area.get_read_ref(cx, live_id!(advance), ShaderTy::Float).unwrap();
        
        if rect_pos.repeat == 0 {
            return None
        }
        if index >= rect_pos.repeat {
            let index = (rect_pos.repeat - 1) * rect_pos.stride;
            let x = rect_pos.buffer[index + 0] - delta.buffer[index + 0] + advance.buffer[index + 0];
            let y = rect_pos.buffer[index + 1] - delta.buffer[index + 1];
            Some(dvec2(x as f64, y as f64))
        }
        else {
            let index = index * rect_pos.stride;
            let x = rect_pos.buffer[index + 0] - delta.buffer[index + 0] + advance.buffer[index + 0] * pos;
            let y = rect_pos.buffer[index + 1] - delta.buffer[index + 1];
            Some(dvec2(x as f64, y as f64))
        }
    }
    
    pub fn get_line_spacing(&self) -> f64 {
        self.text_style.font_size * self.text_style.height_factor * self.font_scale * self.text_style.line_spacing
    }
    
    pub fn get_font_size(&self) -> f64 {
        self.text_style.font_size * self.font_scale
    }
    
    pub fn get_monospace_base(&self, cx: &Cx2d) -> DVec2 {
        let mut fonts_atlas = cx.fonts_atlas_rc.0.borrow_mut();
        if self.text_style.font.font_id.is_none() {
            return DVec2::default();
        }
        let font_id = self.text_style.font.font_id.unwrap();
        if fonts_atlas.fonts[font_id].is_none() {
            return DVec2::default();
        }
        let font = fonts_atlas.fonts[font_id].as_mut().unwrap();
        let slot = font.owned_font_face.with_ref( | face | face.glyph_index('!').map_or(0, | id | id.0 as usize));
        let glyph = font.get_glyph_by_id(slot).unwrap();
        
        DVec2 {
            x: glyph.horizontal_metrics.advance_width * (96.0 / (72.0 * font.ttf_font.units_per_em)),
            y: self.text_style.line_spacing
        }
    }
}