use std::{error::Error, collections::HashMap};
use regex::Regex;
use roxmltree::Node;
use crate::reskit::utility::print_warning;

#[derive(Debug)]
pub struct Entity {
    pub components: HashMap<String, Component>
}

#[derive(Debug)]
pub struct Component {
    pub attributes: HashMap<String, String>
}

pub fn get_ecs( object_group: Node ) -> Result<Vec<Entity>, Box<dyn Error>> {
    let mut entities: Vec<Entity> = Vec::new();

    let objects: Vec<Node> = object_group.descendants().filter( | node | node.tag_name() == "object".into() ).collect();
    for object in objects {
        let object_id = object.attribute( "id" ).ok_or( "invalid file: no id attribute set on object" )?;
        let object_name = object.attribute( "name" ).unwrap_or( "<no name>" );
        let object_name = format!( "({}, ID: {})", object_id, object_name );

        let mut entity: Entity = Entity {
            components: HashMap::new()
        };

        // Get attributes for implicit `position` component
        let x = object.attribute( "x" ).ok_or( "invalid file: position property not present on object" )?;
        let y = object.attribute( "y" ).ok_or( "invalid file: position property not present on object" )?;
        let width = object.attribute( "width" ).ok_or( "invalid file: position property not present on object" )?;
        let height = object.attribute( "height" ).ok_or( "invalid file: position property not present on object" )?;

        entity.components.insert(
            format!( "position" ),
            Component {
                attributes: HashMap::from( [
                    ( format!( "x" ),      x.to_owned() ),
                    ( format!( "y" ),      y.to_owned() ),
                    ( format!( "width" ),  width.to_owned() ),
                    ( format!( "height" ), height.to_owned() )
                ] )
            }
        );

        let properties = object.descendants().find( | node | node.tag_name() == "properties".into() ).ok_or( "invalid file: no properties in object" )?;
        let properties = properties.descendants().filter( | node | node.tag_name() == "property".into() && node.attribute( "name" ).unwrap_or( "" ).starts_with( "reskit-component" ) );
        for component_property in properties {
            let name = component_property.attribute( "name" ).ok_or( "internal error: name attribute expected" )?;
            let prop_type = component_property.attribute( "type" ).unwrap_or( "string" );
            let value = component_property.attribute( "value" );

            if let Some( value ) = value {
                // Set up regex to remove individual parts
                let regex = Regex::new( r#"reskit-component\[([a-z0-9_]+)\](\.[a-z0-9_]*)?"# )?;
                if let Some( captures ) = regex.captures( name ) {
                    let component_name = captures.get( 1 ).ok_or( "internal error: regex did not match properly" )?.as_str();
                    let attribute_name = captures.get( 2 );

                    if let Some( attribute_name ) = attribute_name {
                        // Setting is a component attribute setting ("value" is the setting)
                        let attribute_name = attribute_name.as_str().replace( ".", "" );
                        if let Some( component ) = entity.components.get_mut( component_name ) {
                            component.attributes.insert( attribute_name, value.to_owned() );
                        } else {
                            return Err( format!( "in object {}: undefined component in reskit-component definition \"{}\"", object_name, name ) )?;
                        }
                    } else {
                        // Setting is a component definition
                        if prop_type != "bool" {
                            print_warning( &format!( "in object {}: non-bool type reskit-component definition \"{}\". ignoring...", object_name, name ) );
                        } else {
                            if value == "true" {
                                if entity.components.contains_key( component_name ) {
                                    print_warning( &format!( "in object {}: duplicate reskit-component definition \"{}\". ignoring...", object_name, name ) );
                                } else {
                                    entity.components.insert(
                                        component_name.to_owned(),
                                        Component { attributes: HashMap::new() }
                                    );
                                }
                            } else {
                                print_warning( &format!( "in object {}: reskit-component definition \"{}\" is set to false. ignoring...", object_name, name ) );
                            }
                        }
                    }
                } else {
                    print_warning( &format!( "in object {}: invalid format for reskit-component attribute \"{}\". ignoring...", object_name, name ) );
                }
            } else {
                print_warning( &format!( "in object {}: no value for reskit-component attribute \"{}\". ignoring...", object_name, name ) );
            }
        }

        entities.push( entity );
    }

    Ok( entities )
}