<commit_before>use obj::*;
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::str::FromStr;
use utils::*;
use xml::Element;
use xml::Xml::CharacterNode;

use vecmath;
use xml;

use {
    Animation, BindData, BindDataSet, Joint, JointIndex, Skeleton, VertexWeight,
    ROOT_JOINT_PARENT_INDEX,
};

enum GeometryBindingType {
    Polylist,
    Triangles,
    // TODO types below are not implemented
    // Lines,
    // Linestrips,
    // Polygons,
    // Trifans,
    // Tristrips,
}

impl GeometryBindingType {
    fn name(&self) -> &'static str {
        match *self {
            GeometryBindingType::Polylist => "polylist",
            GeometryBindingType::Triangles => "triangles",
        }
    }
}

///
/// Commonly used shading techniques
/// Holds a description of the textures, samplers, shaders, parameters, and
/// passes necessary for rendering this effect using one method.
///
#[derive(Clone, Debug, PartialEq)]
pub struct PhongEffect {
    pub emission: [f32; 4],
    pub ambient: [f32; 4],
    pub diffuse: Diffuse,
    pub specular: [f32; 4],
    pub shininess: f32,
    pub index_of_refraction: f32,
}

///
/// Can be a plain color or point to a texture in the images library
///
#[derive(Clone, Debug, PartialEq)]
pub enum Diffuse {
    Color([f32; 4]),
    Texture(String),
}

// TODO: Add more effect types and then unify them under a technique enum.
// Lambert
// Blinn
// Constant
//

pub struct ColladaDocument {
    pub root_element: xml::Element, // TODO figure out how to cache skeletal and skinning data, as we need to
                                    // access them multiple times
}

impl ColladaDocument {
    ///
    /// Construct a ColladaDocument for the XML document at the given path
    ///
    pub fn from_path(path: &Path) -> Result<ColladaDocument, &'static str> {
        let file_result = File::open(path);

        let mut file = match file_result {
            Ok(file) => file,
            Err(_) => return Err("Failed to open COLLADA file at path."),
        };

        let mut xml_string = String::new();
        match file.read_to_string(&mut xml_string) {
            Ok(_) => {}
            Err(_) => return Err("Failed to read COLLADA file."),
        };

        ColladaDocument::from_str(&xml_string)
    }

    ///
    /// Construct a ColladaDocument from an XML string
    ///
    pub fn from_str(xml_string: &str) -> Result<ColladaDocument, &'static str> {
        match xml_string.parse() {
            Ok(root_element) => Ok(ColladaDocument {
                root_element: root_element,
            }),
            Err(_) => Err("Error while parsing COLLADA document."),
        }
    }

    fn get_color(el: &Element) -> Option<[f32; 4]> {
        let v: Vec<f32> = parse_string_to_vector(el.content_str().as_str());
        if v.len() == 4 {
            Some([v[0], v[1], v[2], v[3]])
        } else {
            None
        }
    }

    ///
    /// Returns the library of effects.
    /// Current only supports Phong shading.
    ///
    pub fn get_effect_library(&self) -> HashMap<String, PhongEffect> {
        let ns = self.get_ns();
        let lib_effs = self
            .root_element
            .get_child("library_effects", ns)
            .expect("Could not get library_effects from the document.");
        lib_effs
            .get_children("effect", ns)
            .flat_map(|el: &Element| -> Option<(String, PhongEffect)> {
                let id = el
                    .get_attribute("id", None)
                    .expect(&format!("effect is missing its id. {:#?}", el));
                let prof = el.get_child("profile_COMMON", ns)?;
                let tech = prof.get_child("technique", ns)?;
                let phong = tech.get_child("phong", ns)?;
                let emission_color = phong
                    .get_child("emission", ns)
                    .expect("phong is missing emission")
                    .get_child("color", ns)
                    .expect("emission is missing color");
                let emission = ColladaDocument::get_color(emission_color)
                    .expect("could not get emission color.");
                let ambient_color = phong
                    .get_child("ambient", ns)
                    .expect("phong is missing ambient")
                    .get_child("color", ns)
                    .expect("ambient is missing color");
                let ambient = ColladaDocument::get_color(ambient_color)
                    .expect("could not get ambient color.");
                let diffuse = phong
                    .get_child("diffuse", ns)
                    .expect("phong is missing diffuse");
                let diffuse_texture = diffuse.get_child("texture", ns);
                let diffuse_color = diffuse.get_child("color", ns);
                let diffuse = match diffuse_texture {
                    Some(texture) => Diffuse::Texture(
                        texture
                            .get_attribute("texture", ns)
                            .expect("texture had no texture")
                            .to_string(),
                    ),
                    _ => Diffuse::Color(
                        ColladaDocument::get_color(
                            diffuse_color.expect("diffuse is missing color"),
                        )
                        .expect("could not get diffuse color."),
                    ),
                };
                let specular_color = phong
                    .get_child("specular", ns)
                    .expect("phong is missing specular")
                    .get_child("color", ns)
                    .expect("specular is missing color");
                let specular = ColladaDocument::get_color(specular_color)
                    .expect("could not get specular color.");
                let shininess: f32 = phong
                    .get_child("shininess", ns)
                    .expect("phong is missing shininess")
                    .get_child("float", ns)
                    .expect("shininess is missing float")
                    .content_str()
                    .as_str()
                    .parse()
                    .ok()
                    .expect("could not parse shininess");
                let index_of_refraction: f32 = phong
                    .get_child("index_of_refraction", ns)
                    .expect("phong is missing index_of_refraction")
                    .get_child("float", ns)
                    .expect("index_of_refraction is missing float")
                    .content_str()
                    .as_str()
                    .parse()
                    .ok()
                    .expect("could not parse index_of_refraction");
                Some((
                    id.to_string(),
                    PhongEffect {
                        emission,
                        ambient,
                        diffuse,
                        specular,
                        shininess,
                        index_of_refraction,
                    },
                ))
            })
            .collect()
    }

    pub fn get_material_to_effect(&self) -> HashMap<String, String> {
        let ns = self.get_ns();
        let lib_mats = self
            .root_element
            .get_child("library_materials", ns)
            .expect("Could not get library_materials from the document");
        lib_mats
            .get_children("material", ns)
            .flat_map(|el| {
                let id = el
                    .get_attribute("id", None)
                    .expect(&format!("material is missing its id. {:#?}", el));
                let mut url: String = el
                    .get_child("instance_effect", ns)
                    .expect("could not get material instance_effect")
                    .get_attribute("url", None)
                    .expect("could not get material instance_effect url attribute")
                    .to_string();
                if url.remove(0) == '#' {
                    Some((id.to_string(), url))
                } else {
                    None
                }
            })
            .collect()
    }

    ///
    /// Returns a hashmap of <imageid, filename>
    ///
    pub fn get_images(&self) -> HashMap<String, String> {
        let ns = self.get_ns();
        let lib_images = self
            .root_element
            .get_child("library_images", ns)
            .expect("Could not get library_images from the document");
        lib_images
            .get_children("image", ns)
            .flat_map(|el| {
                let id = el
                    .get_attribute("id", ns)
                    .expect(&format!("image is missing its id. {:#?}", el))
                    .to_string();
                let file_name = el
                    .get_child("init_from", ns)
                    .expect("Could not get image from the element")
                    .content_str();
                Some((id, file_name))
            })
            .collect()
    }

    ///
    /// Return a vector of all Animations in the Collada document
    ///
    pub fn get_animations(&self) -> Option<Vec<Animation>> {
        match self
            .root_element
            .get_child("library_animations", self.get_ns())
        {
            Some(library_animations) => {
                let animations = library_animations.get_children("animation", self.get_ns());
                Some(animations.filter_map(|a| self.get_animation(a)).collect())
            }
            None => None,
        }
    }

    ///
    /// Construct an Animation struct for the given <animation> element if possible
    ///
    fn get_animation(&self, animation_element: &Element) -> Option<Animation> {<commit_msg>fix: id attribute on images<commit_after>use obj::*;
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::str::FromStr;
use utils::*;
use xml::Element;
use xml::Xml::CharacterNode;

use vecmath;
use xml;

use {
    Animation, BindData, BindDataSet, Joint, JointIndex, Skeleton, VertexWeight,
    ROOT_JOINT_PARENT_INDEX,
};

enum GeometryBindingType {
    Polylist,
    Triangles,
    // TODO types below are not implemented
    // Lines,
    // Linestrips,
    // Polygons,
    // Trifans,
    // Tristrips,
}

impl GeometryBindingType {
    fn name(&self) -> &'static str {
        match *self {
            GeometryBindingType::Polylist => "polylist",
            GeometryBindingType::Triangles => "triangles",
        }
    }
}

///
/// Commonly used shading techniques
/// Holds a description of the textures, samplers, shaders, parameters, and
/// passes necessary for rendering this effect using one method.
///
#[derive(Clone, Debug, PartialEq)]
pub struct PhongEffect {
    pub emission: [f32; 4],
    pub ambient: [f32; 4],
    pub diffuse: Diffuse,
    pub specular: [f32; 4],
    pub shininess: f32,
    pub index_of_refraction: f32,
}

///
/// Can be a plain color or point to a texture in the images library
///
#[derive(Clone, Debug, PartialEq)]
pub enum Diffuse {
    Color([f32; 4]),
    Texture(String),
}

// TODO: Add more effect types and then unify them under a technique enum.
// Lambert
// Blinn
// Constant
//

pub struct ColladaDocument {
    pub root_element: xml::Element, // TODO figure out how to cache skeletal and skinning data, as we need to
                                    // access them multiple times
}

impl ColladaDocument {
    ///
    /// Construct a ColladaDocument for the XML document at the given path
    ///
    pub fn from_path(path: &Path) -> Result<ColladaDocument, &'static str> {
        let file_result = File::open(path);

        let mut file = match file_result {
            Ok(file) => file,
            Err(_) => return Err("Failed to open COLLADA file at path."),
        };

        let mut xml_string = String::new();
        match file.read_to_string(&mut xml_string) {
            Ok(_) => {}
            Err(_) => return Err("Failed to read COLLADA file."),
        };

        ColladaDocument::from_str(&xml_string)
    }

    ///
    /// Construct a ColladaDocument from an XML string
    ///
    pub fn from_str(xml_string: &str) -> Result<ColladaDocument, &'static str> {
        match xml_string.parse() {
            Ok(root_element) => Ok(ColladaDocument {
                root_element: root_element,
            }),
            Err(_) => Err("Error while parsing COLLADA document."),
        }
    }

    fn get_color(el: &Element) -> Option<[f32; 4]> {
        let v: Vec<f32> = parse_string_to_vector(el.content_str().as_str());
        if v.len() == 4 {
            Some([v[0], v[1], v[2], v[3]])
        } else {
            None
        }
    }

    ///
    /// Returns the library of effects.
    /// Current only supports Phong shading.
    ///
    pub fn get_effect_library(&self) -> HashMap<String, PhongEffect> {
        let ns = self.get_ns();
        let lib_effs = self
            .root_element
            .get_child("library_effects", ns)
            .expect("Could not get library_effects from the document.");
        lib_effs
            .get_children("effect", ns)
            .flat_map(|el: &Element| -> Option<(String, PhongEffect)> {
                let id = el
                    .get_attribute("id", None)
                    .expect(&format!("effect is missing its id. {:#?}", el));
                let prof = el.get_child("profile_COMMON", ns)?;
                let tech = prof.get_child("technique", ns)?;
                let phong = tech.get_child("phong", ns)?;
                let emission_color = phong
                    .get_child("emission", ns)
                    .expect("phong is missing emission")
                    .get_child("color", ns)
                    .expect("emission is missing color");
                let emission = ColladaDocument::get_color(emission_color)
                    .expect("could not get emission color.");
                let ambient_color = phong
                    .get_child("ambient", ns)
                    .expect("phong is missing ambient")
                    .get_child("color", ns)
                    .expect("ambient is missing color");
                let ambient = ColladaDocument::get_color(ambient_color)
                    .expect("could not get ambient color.");
                let diffuse = phong
                    .get_child("diffuse", ns)
                    .expect("phong is missing diffuse");
                let diffuse_texture = diffuse.get_child("texture", ns);
                let diffuse_color = diffuse.get_child("color", ns);
                let diffuse = match diffuse_texture {
                    Some(texture) => Diffuse::Texture(
                        texture
                            .get_attribute("texture", ns)
                            .expect("texture had no texture")
                            .to_string(),
                    ),
                    _ => Diffuse::Color(
                        ColladaDocument::get_color(
                            diffuse_color.expect("diffuse is missing color"),
                        )
                        .expect("could not get diffuse color."),
                    ),
                };
                let specular_color = phong
                    .get_child("specular", ns)
                    .expect("phong is missing specular")
                    .get_child("color", ns)
                    .expect("specular is missing color");
                let specular = ColladaDocument::get_color(specular_color)
                    .expect("could not get specular color.");
                let shininess: f32 = phong
                    .get_child("shininess", ns)
                    .expect("phong is missing shininess")
                    .get_child("float", ns)
                    .expect("shininess is missing float")
                    .content_str()
                    .as_str()
                    .parse()
                    .ok()
                    .expect("could not parse shininess");
                let index_of_refraction: f32 = phong
                    .get_child("index_of_refraction", ns)
                    .expect("phong is missing index_of_refraction")
                    .get_child("float", ns)
                    .expect("index_of_refraction is missing float")
                    .content_str()
                    .as_str()
                    .parse()
                    .ok()
                    .expect("could not parse index_of_refraction");
                Some((
                    id.to_string(),
                    PhongEffect {
                        emission,
                        ambient,
                        diffuse,
                        specular,
                        shininess,
                        index_of_refraction,
                    },
                ))
            })
            .collect()
    }

    pub fn get_material_to_effect(&self) -> HashMap<String, String> {
        let ns = self.get_ns();
        let lib_mats = self
            .root_element
            .get_child("library_materials", ns)
            .expect("Could not get library_materials from the document");
        lib_mats
            .get_children("material", ns)
            .flat_map(|el| {
                let id = el
                    .get_attribute("id", None)
                    .expect(&format!("material is missing its id. {:#?}", el));
                let mut url: String = el
                    .get_child("instance_effect", ns)
                    .expect("could not get material instance_effect")
                    .get_attribute("url", None)
                    .expect("could not get material instance_effect url attribute")
                    .to_string();
                if url.remove(0) == '#' {
                    Some((id.to_string(), url))
                } else {
                    None
                }
            })
            .collect()
    }

    ///
    /// Returns a hashmap of <imageid, filename>
    ///
    pub fn get_images(&self) -> HashMap<String, String> {
        let ns = self.get_ns();
        let lib_images = self
            .root_element
            .get_child("library_images", ns)
            .expect("Could not get library_images from the document");
        lib_images
            .get_children("image", ns)
            .flat_map(|el| {
                let id = el
                    .get_attribute("id", None)
                    .expect(&format!("image is missing its id. {:#?}", el))
                    .to_string();
                let file_name = el
                    .get_child("init_from", ns)
                    .expect("Could not get image from the element")
                    .content_str();
                Some((id, file_name))
            })
            .collect()
    }

    ///
    /// Return a vector of all Animations in the Collada document
    ///
    pub fn get_animations(&self) -> Option<Vec<Animation>> {
        match self
            .root_element
            .get_child("library_animations", self.get_ns())
        {
            Some(library_animations) => {
                let animations = library_animations.get_children("animation", self.get_ns());
                Some(animations.filter_map(|a| self.get_animation(a)).collect())
            }
            None => None,
        }
    }

    ///
    /// Construct an Animation struct for the given <animation> element if possible
    ///
    fn get_animation(&self, animation_element: &Element) -> Option<Animation> {
{'fraction_non_alphanumeric': 0.1127946127946128, 'fraction_numerical': 0.003162942556881951, 'mean_word_length': 1.442741433021807, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 73, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 18}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5063100', 'n_tokens_mistral': 5346, 'n_tokens_neox': 5037, 'n_words': 1340}