// Copyright 2025 Lars Brubaker
// Output generation methods for the Tessellator.

use super::{ElementType, TessStatus, Tessellator, TESS_UNDEF};
use crate::mesh::{F_HEAD, INVALID, V_HEAD};

impl Tessellator {
    pub(crate) fn output_polymesh(&mut self, element_type: ElementType, poly_size: usize, vertex_size: usize) {
        if poly_size > 3 {
            if let Some(ref mut mesh) = self.mesh {
                if !mesh.merge_convex_faces(poly_size) {
                    self.status = TessStatus::OutOfMemory;
                    return;
                }
            }
        }

        let mesh = match self.mesh.as_mut() {
            Some(m) => m,
            None => return,
        };

        let mut v = mesh.verts[V_HEAD as usize].next;
        while v != V_HEAD {
            mesh.verts[v as usize].n = TESS_UNDEF;
            v = mesh.verts[v as usize].next;
        }

        let mut max_vert = 0u32;
        let mut max_face = 0u32;

        let mut f = mesh.faces[F_HEAD as usize].next;
        while f != F_HEAD {
            mesh.faces[f as usize].n = TESS_UNDEF;
            if !mesh.faces[f as usize].inside {
                f = mesh.faces[f as usize].next;
                continue;
            }

            let e_start = mesh.faces[f as usize].an_edge;
            let mut e = e_start;
            loop {
                let org = mesh.edges[e as usize].org;
                if mesh.verts[org as usize].n == TESS_UNDEF {
                    mesh.verts[org as usize].n = max_vert;
                    max_vert += 1;
                }
                e = mesh.edges[e as usize].lnext;
                if e == e_start {
                    break;
                }
            }
            mesh.faces[f as usize].n = max_face;
            max_face += 1;
            f = mesh.faces[f as usize].next;
        }

        self.out_element_count = max_face as usize;
        self.out_vertex_count = max_vert as usize;

        let stride = if element_type == ElementType::ConnectedPolygons {
            poly_size * 2
        } else {
            poly_size
        };
        self.out_elements = vec![TESS_UNDEF; max_face as usize * stride];
        self.out_vertices = vec![0.0; max_vert as usize * vertex_size];
        self.out_vertex_indices = vec![TESS_UNDEF; max_vert as usize];

        let mesh = self.mesh.as_ref().unwrap();
        let mut v = mesh.verts[V_HEAD as usize].next;
        while v != V_HEAD {
            let n = mesh.verts[v as usize].n;
            if n != TESS_UNDEF {
                let base = n as usize * vertex_size;
                self.out_vertices[base] = mesh.verts[v as usize].coords[0];
                self.out_vertices[base + 1] = mesh.verts[v as usize].coords[1];
                if vertex_size > 2 {
                    self.out_vertices[base + 2] = mesh.verts[v as usize].coords[2];
                }
                self.out_vertex_indices[n as usize] = mesh.verts[v as usize].idx;
            }
            v = mesh.verts[v as usize].next;
        }

        let mut ep = 0;
        let mut f = mesh.faces[F_HEAD as usize].next;
        while f != F_HEAD {
            if !mesh.faces[f as usize].inside {
                f = mesh.faces[f as usize].next;
                continue;
            }
            let e_start = mesh.faces[f as usize].an_edge;
            let mut e = e_start;
            let mut fv = 0;
            loop {
                let org = mesh.edges[e as usize].org;
                self.out_elements[ep] = mesh.verts[org as usize].n;
                ep += 1;
                fv += 1;
                e = mesh.edges[e as usize].lnext;
                if e == e_start {
                    break;
                }
            }
            for _ in fv..poly_size {
                self.out_elements[ep] = TESS_UNDEF;
                ep += 1;
            }

            if element_type == ElementType::ConnectedPolygons {
                let e_start = mesh.faces[f as usize].an_edge;
                let mut e = e_start;
                let mut fv2 = 0;
                loop {
                    let rf = mesh.rface(e);
                    let nf = if rf != INVALID && mesh.faces[rf as usize].inside {
                        mesh.faces[rf as usize].n
                    } else {
                        TESS_UNDEF
                    };
                    self.out_elements[ep] = nf;
                    ep += 1;
                    fv2 += 1;
                    e = mesh.edges[e as usize].lnext;
                    if e == e_start {
                        break;
                    }
                }
                for _ in fv2..poly_size {
                    self.out_elements[ep] = TESS_UNDEF;
                    ep += 1;
                }
            }

            f = mesh.faces[f as usize].next;
        }
    }

    pub(crate) fn output_contours(&mut self, vertex_size: usize) {
        let mesh = match self.mesh.as_ref() {
            Some(m) => m,
            None => return,
        };
        let mut total_verts = 0usize;
        let mut total_elems = 0usize;
        let mut f = mesh.faces[F_HEAD as usize].next;
        while f != F_HEAD {
            if mesh.faces[f as usize].inside {
                let e_start = mesh.faces[f as usize].an_edge;
                let mut e = e_start;
                loop {
                    total_verts += 1;
                    e = mesh.edges[e as usize].lnext;
                    if e == e_start {
                        break;
                    }
                }
                total_elems += 1;
            }
            f = mesh.faces[f as usize].next;
        }
        self.out_element_count = total_elems;
        self.out_vertex_count = total_verts;
        self.out_elements = vec![TESS_UNDEF; total_elems * 2];
        self.out_vertices = vec![0.0; total_verts * vertex_size];
        self.out_vertex_indices = vec![TESS_UNDEF; total_verts];

        let mesh = self.mesh.as_ref().unwrap();
        let mut vp = 0usize;
        let mut ep = 0usize;
        let mut sv = 0usize;
        let mut f = mesh.faces[F_HEAD as usize].next;
        while f != F_HEAD {
            if !mesh.faces[f as usize].inside {
                f = mesh.faces[f as usize].next;
                continue;
            }
            let e_start = mesh.faces[f as usize].an_edge;
            let mut e = e_start;
            let mut vc = 0usize;
            loop {
                let org = mesh.edges[e as usize].org;
                let base = vp * vertex_size;
                self.out_vertices[base] = mesh.verts[org as usize].coords[0];
                self.out_vertices[base + 1] = mesh.verts[org as usize].coords[1];
                if vertex_size > 2 {
                    self.out_vertices[base + 2] = mesh.verts[org as usize].coords[2];
                }
                self.out_vertex_indices[vp] = mesh.verts[org as usize].idx;
                vp += 1;
                vc += 1;
                e = mesh.edges[e as usize].lnext;
                if e == e_start {
                    break;
                }
            }
            self.out_elements[ep] = sv as u32;
            self.out_elements[ep + 1] = vc as u32;
            ep += 2;
            sv += vc;
            f = mesh.faces[f as usize].next;
        }
    }
}
