// Copyright (c) 2016, the SDSL Project Authors.  All rights reserved.
// Please see the AUTHORS file for details.  Use of this source code is governed
// by a BSD license that can be found in the LICENSE file.
/*!\file wt_blcd.hpp
 *  \brief wt_blcd.hpp contains a generic wavelet tree class.
 *  \author Simon Gog
 */
#ifndef INCLUDED_SDSL_WT_BLCD
#define INCLUDED_SDSL_WT_BLCD

#include <cstdint>
#include <memory>
#include <utility>
#include <vector>

#include <sdsl/int_vector.hpp>
#include <sdsl/wt_helper.hpp>
#include <sdsl/wt_pc.hpp>

//! Namespace for the succinct data structure library.
namespace sdsl
{

// forward declaration
struct balanced_shape;

//! A balanced wavelet tree.
/*!
 * \par Space complexity
 *      \f$\Order{n\log|\Sigma| + 2|\Sigma|\log n}\f$ bits, where \f$n\f$ is
 *      the size of the vector the wavelet tree was build for.
 *
 * \tparam t_bitvector   Underlying bitvector structure.
 * \tparam t_rank        Type of the support structure for rank on pattern `1`.
 * \tparam t_select      Type of the support structure for select on pattern `1`.
 * \tparam t_select_zero Type of the support structure for select on pattern `0`.
 *
 * \par Reference
 *  Roberto Grossi, Ankur Gupta, Jeffrey Scott Vitter:
 *  High-order entropy-compressed text indexes.
 *  Proceedings of the 14th Annual ACM-SIAM Symposium on
 *  Discrete Algorithms (SODA 2003).
 *
 * @ingroup wt
 */
template <class t_bitvector = bit_vector,
          class t_rank = typename t_bitvector::rank_1_type,
          class t_select_one = typename t_bitvector::select_1_type,
          class t_select_zero = typename t_bitvector::select_0_type,
          class t_tree_strat = byte_tree<>>
using wt_blcd = wt_pc<balanced_shape, t_bitvector, t_rank, t_select_one, t_select_zero, t_tree_strat>;

template <class t_wt>
struct _balanced_shape
{
    typedef typename t_wt::size_type size_type;
    typedef std::pair<uint64_t, uint64_t> tPII; // (freq, nodenr)-pair
    enum
    {
        lex_ordered = 1
    };

    template <class t_rac>
    static void construct_tree(t_rac & C, std::vector<pc_node> & temp_nodes)
    {
        size_type c = 0;
        std::vector<uint64_t> symbols;
        std::for_each(std::begin(C),
                      std::end(C),
                      [&](decltype(*std::begin(C)) & freq)
                      {
                          if (freq > 0)
                          {
                              symbols.push_back(c);
                          }
                          ++c;
                      });
        uint64_t sigma = symbols.size();
        if (sigma > 0)
        {
            _construct_tree(pc_node::undef, symbols, 0, sigma, C, temp_nodes);
            pc_node root = temp_nodes[0];
            for (uint64_t i = 1; i < temp_nodes.size(); ++i)
            {
                temp_nodes[i - 1] = temp_nodes[i];
                temp_nodes[i - 1].parent = (temp_nodes[i - 1].parent + temp_nodes.size() - 1) % temp_nodes.size();
                temp_nodes[i - 1].child[0] -= (temp_nodes[i - 1].child[0] != pc_node::undef);
                temp_nodes[i - 1].child[1] -= (temp_nodes[i - 1].child[1] != pc_node::undef);
            }
            root.child[0] -= (root.child[0] != pc_node::undef);
            root.child[1] -= (root.child[1] != pc_node::undef);
            temp_nodes[temp_nodes.size() - 1] = root;
        }
    }

    // recursive construct_tree method returns node frequency and node pointer
    template <class t_rac>
    static tPII _construct_tree(uint64_t parent,
                                std::vector<uint64_t> const & symbols,
                                uint64_t lb,
                                uint64_t sigma,
                                t_rac const & C,
                                std::vector<pc_node> & temp_nodes)
    {
        if (sigma == 1)
        {
            uint64_t freq = C[symbols[lb]];
            temp_nodes.emplace_back(pc_node(freq, symbols[lb], parent, pc_node::undef, pc_node::undef));
            return tPII(freq, temp_nodes.size() - 1);
        }
        else
        {
            temp_nodes.emplace_back(pc_node(0, 0, parent, pc_node::undef, pc_node::undef));
            uint64_t node_id = temp_nodes.size() - 1;
            uint64_t l_sigma = (sigma + 1) / 2;
            tPII freq_nptr_0 = _construct_tree(node_id, symbols, lb, l_sigma, C, temp_nodes);
            tPII freq_nptr_1 = _construct_tree(node_id, symbols, lb + l_sigma, sigma - l_sigma, C, temp_nodes);
            uint64_t freq = freq_nptr_0.first + freq_nptr_1.first;
            temp_nodes[node_id].freq = freq;
            temp_nodes[node_id].child[0] = freq_nptr_0.second;
            temp_nodes[node_id].child[1] = freq_nptr_1.second;
            return tPII(freq, node_id);
        }
    }
};

struct balanced_shape
{
    template <class t_wt>
    using type = _balanced_shape<t_wt>;
};

} // end namespace sdsl
#endif
