/* prefixpathtree.cc
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2013 Ralf Hoffmann.
 * You can contact me at: ralf@boomerangsworld.de
 *   or http://www.boomerangsworld.de/worker
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "prefixpathtree.hh"
#include "aguix/util.h"

PrefixPathTree::PrefixPathTree()
{
}

PrefixPathTree::insert_result PrefixPathTree::insert( const std::string &path )
{
    std::vector< std::string > path_components;

    AGUIXUtils::split_string( path_components, path, '/' );

    if ( ! path_components.empty() &&
         path_components[ path_components.size() - 1 ].empty() ) {
        path_components.resize( path_components.size() - 1 );
    }

    insert_result res = lookup( m_root, path_components, 0 );

    if ( res == IS_IGNORED ||
         res == IS_MATCH ) return res;

    return storePath( m_root, path_components, 0 );
}

static void push_leafs( const DeepPathNode &root,
                        const DeepPathNode &node,
                        std::list< std::string > &l,
                        const std::string &prefix )
{
    std::string new_prefix( prefix );

    new_prefix += node.get_name();

    if ( &root != &node &&
         ( ! node.get_children().empty() ||
           node.get_name().empty() ) ) {
        new_prefix += "/";
    }

    if ( node.get_children().empty() ) {
        if ( ! new_prefix.empty() ) {
            l.push_back( new_prefix );
        }
    } else {
        for ( std::list< DeepPathNode >::const_iterator it1 = node.get_children().begin();
              it1 != node.get_children().end();
              it1++ ) {
            push_leafs( root, *it1, l, new_prefix );
        }
    }
}

std::list< std::string > PrefixPathTree::getPaths()
{
    std::list< std::string > l;

    push_leafs( m_root, m_root, l, "" );

    return l;
}

PrefixPathTree::insert_result PrefixPathTree::storePath( DeepPathNode &node,
                                                         const std::vector< std::string > &path_components,
                                                         size_t offset )
{
    if ( offset >= path_components.size() ) return IS_NOT_FOUND;

    const std::string &basename = path_components[offset];

    if ( basename.empty() && offset != 0 ) {
        // ignore empty components unless it's the first one
        return storePath( node, path_components, offset + 1 );
    }

    DeepPathNode::node_change_t changed = DeepPathNode::NODE_UNCHANGED;

    DeepPathNode &child = node.lookup_or_insert( basename, 0, &changed );

    if ( offset + 1 >= path_components.size() ) {
        if ( changed == DeepPathNode::NODE_INSERTED ) return IS_ADDED;

        if ( child.get_children().empty() ) return IS_MATCH;

        child.clear();

        return IS_PREFIX;
    }

    return storePath( child, path_components, offset + 1 );
}

PrefixPathTree::insert_result PrefixPathTree::lookup( DeepPathNode &node,
                                                      const std::vector< std::string > &path_components,
                                                      size_t offset )
{
    if ( offset >= path_components.size() ) return IS_NOT_FOUND;

    const std::string &basename = path_components[offset];

    if ( basename.empty() && offset != 0 ) {
        // ignore empty components unless the first one
        return lookup( node, path_components, offset + 1 );
    }

    DeepPathNode *child = node.lookup( basename );

    if ( child ) {
        if ( child->get_children().empty() ) {
            if ( offset + 1 >= path_components.size() ) {
                return IS_MATCH;
            }
            return IS_IGNORED;
        }

        if ( offset + 1 >= path_components.size() ) {
            // last segment
            return IS_PREFIX;
        }

        return lookup( *child, path_components, offset + 1 );
    } else {
        return IS_NOT_FOUND;
    }
}

PrefixPathTree::insert_result PrefixPathTree::lookup( const std::string &path )
{
    std::vector< std::string > path_components;

    AGUIXUtils::split_string( path_components, path, '/' );

    if ( ! path_components.empty() &&
         path_components[ path_components.size() - 1 ].empty() ) {
        path_components.resize( path_components.size() - 1 );
    }

    return lookup( m_root, path_components, 0 );
}

void PrefixPathTree::clear()
{
    m_root = DeepPathNode();
}
