#include "moab/Tree.hpp"
#include "moab/Range.hpp"
#include "moab/Interface.hpp"

#include <limits>

namespace moab
{
ErrorCode Tree::parse_common_options( FileOptions& options )
{
    double tmp_dbl;
    int tmp_int;
    // MAX_PER_LEAF: max entities per leaf; default = 6
    ErrorCode rval = options.get_int_option( "MAX_PER_LEAF", tmp_int );
    if( MB_SUCCESS == rval ) maxPerLeaf = std::max( tmp_int, 1 );

    // MAX_DEPTH: max depth of the tree; default = 30
    rval = options.get_int_option( "MAX_DEPTH", tmp_int );
    if( MB_SUCCESS == rval ) maxDepth = tmp_int;
    if( maxDepth < 1 ) maxDepth = std::numeric_limits< unsigned >::max();

    // MIN_WIDTH: minimum width of box, used like a tolerance; default = 1.0e-10
    rval = options.get_real_option( "MIN_WIDTH", tmp_dbl );
    if( MB_SUCCESS == rval ) minWidth = tmp_dbl;

    // MESHSET_FLAGS: flags passed into meshset creation for tree nodes; should be a value from
    //          ENTITY_SET_PROPERTY (see Types.hpp); default = MESHSET_SET
    rval = options.get_int_option( "MESHSET_FLAGS", tmp_int );
    if( MB_SUCCESS == rval && 0 <= tmp_int )
        meshsetFlags = (unsigned)tmp_int;
    else if( 0 > tmp_int )
        return MB_FAILURE;

    // CLEAN_UP: if false, do not delete tree sets upon tree class destruction; default = true
    bool tmp_bool;
    rval = options.get_toggle_option( "CLEAN_UP", true, tmp_bool );
    if( MB_SUCCESS == rval && !tmp_bool ) cleanUp = false;

    // TAG_NAME: tag name to store tree information on tree nodes; default = "AKDTree"
    std::string tmp_str;
    rval = options.get_str_option( "TAG_NAME", tmp_str );
    if( MB_SUCCESS == rval ) boxTagName = tmp_str;

    return MB_SUCCESS;
}

ErrorCode Tree::find_all_trees( Range& results )
{
    Tag tag        = get_box_tag();
    ErrorCode rval = moab()->get_entities_by_type_and_tag( 0, MBENTITYSET, &tag, 0, 1, results );
    if( MB_SUCCESS != rval || results.empty() ) return rval;
    std::vector< BoundBox > boxes( results.size() );
    rval = moab()->tag_get_data( tag, results, &boxes[0] );
    if( MB_SUCCESS != rval ) return rval;
    for( std::vector< BoundBox >::iterator vit = boxes.begin(); vit != boxes.end(); ++vit )
        boundBox.update( *vit );

    if( results.size() == 1 ) myRoot = *results.begin();

    return MB_SUCCESS;
}

ErrorCode Tree::create_root( const double box_min[3], const double box_max[3], EntityHandle& root_handle )
{
    ErrorCode rval = mbImpl->create_meshset( meshsetFlags, root_handle );
    if( MB_SUCCESS != rval ) return rval;

    myRoot = root_handle;

    double box_tag[6];
    for( int i = 0; i < 3; i++ )
    {
        box_tag[i]     = box_min[i];
        box_tag[3 + i] = box_max[i];
    }
    rval = mbImpl->tag_set_data( get_box_tag(), &root_handle, 1, box_tag );
    if( MB_SUCCESS != rval ) return rval;

    boundBox.bMin = box_min;
    boundBox.bMax = box_max;

    return MB_SUCCESS;
}

ErrorCode Tree::delete_tree_sets()
{
    if( !myRoot ) return MB_SUCCESS;

    ErrorCode rval;
    std::vector< EntityHandle > children, dead_sets, current_sets;
    current_sets.push_back( myRoot );
    while( !current_sets.empty() )
    {
        EntityHandle set = current_sets.back();
        current_sets.pop_back();
        dead_sets.push_back( set );
        rval = mbImpl->get_child_meshsets( set, children );
        if( MB_SUCCESS != rval ) return rval;
        std::copy( children.begin(), children.end(), std::back_inserter( current_sets ) );
        children.clear();
    }

    rval = mbImpl->tag_delete_data( boxTag, &myRoot, 1 );
    if( MB_SUCCESS != rval ) return rval;

    rval = mbImpl->delete_entities( &dead_sets[0], dead_sets.size() );
    if( MB_SUCCESS != rval ) return rval;

    myRoot = 0;

    return MB_SUCCESS;
}

}  // namespace moab
