//  node graph, implementation
//  Copyright (C) 2008, 2009, 2010 Tim Blechmann
//
//  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; see the file COPYING.  If not, write to
//  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
//  Boston, MA 02111-1307, USA.

#include <fstream>

#include "dependency_graph_generator.hpp"
#include "dsp_thread_queue_node.hpp"
#include "node_graph.hpp"
#include "synth.hpp"
#include "utilities/exists.hpp"

#include "dsp_thread_queue/dsp_thread_queue.hpp"

namespace nova {

void node_graph::add_node(server_node* n) {
    node_position_constraint to_root = std::make_pair(&root_group_, head);
    add_node(n, to_root, [](server_node& node) {});
}

void node_graph::add_node(server_node* n, node_position_constraint const& constraint) {
    add_node(n, constraint, [](server_node& node) {});
}


node_graph::dsp_thread_queue_ptr node_graph::generate_dsp_queue(void) {
    dependency_graph_generator gen;

    node_graph::dsp_thread_queue* ret = gen(this);
    return dsp_thread_queue_ptr(ret);
}

void node_graph::synth_reassign_id(int32_t node_id) {
    node_set_type::iterator it = node_set.find(node_id, hash_node(), equal_node());
    if (it == node_set.end())
        throw std::runtime_error("node id not found");
    server_node* node = &(*it);

    if (!node->is_synth())
        return;

    boost::hash<int32_t> hasher;

    int32_t hidden_id = -std::abs(node_id);

    while (!node_id_available(hidden_id))
        hidden_id = -std::abs(static_cast<int32_t>(hasher(node_id)));

    assert(hidden_id < 0);

    node_set.erase(*node);
    node->reset_id(hidden_id);
    node_set.insert(*node);
}

int32_t node_graph::generate_node_id(void) {
    int32_t new_id;
    do {
        for (;;) {
            new_id = -std::abs(static_cast<int32_t>(server_node::hash(generated_id)));
            if (likely(new_id != generated_id))
                break;

            generated_id -= 1;
        }

    } while (!node_id_available(new_id));
    generated_id = new_id;
    return generated_id;
}


abstract_group::~abstract_group(void) {
    while (!child_nodes.empty()) {
        server_node& node = child_nodes.front();
        remove_child(&node);
    }
}

void abstract_group::pause(void) {
    for (server_node& child_node : child_nodes)
        child_node.pause();
}

void abstract_group::resume(void) {
    for (server_node& child_node : child_nodes)
        child_node.resume();
}

void abstract_group::add_child(server_node* node) {
    assert(!has_child(node));

    child_nodes.push_front(*node);
    node->set_parent(this);
}

void abstract_group::replace_child(server_node* node, server_node* node_to_replace) {
    assert(!has_child(node));
    assert(has_child(node_to_replace));

    server_node_list::iterator position_of_old_element = server_node_list::s_iterator_to(*node_to_replace);
    child_nodes.insert(position_of_old_element, *node);
    node->set_parent(this);
    remove_child(node_to_replace);
}

bool abstract_group::has_child(const server_node* node) const {
    if (!node->server_node::parent_hook::is_linked())
        return false;

    /* find node in container */
    for (const auto& elem : child_nodes) {
        if (&elem == node)
            return true;
    }
    return false;
}

void abstract_group::remove_child(server_node* node) {
    assert(has_child(node));
    node->server_node::parent_hook::unlink();
    node->clear_parent();
}

void abstract_group::set(slot_index_t slot_id, float val) {
    for (auto& elem : child_nodes)
        elem.set(slot_id, val);
}

void abstract_group::set(const char* slot_str, float val) {
    size_t hashed_str = string_hash(slot_str);
    for (auto& elem : child_nodes)
        elem.set(slot_str, hashed_str, val);
}

void abstract_group::set(const char* slot_str, std::size_t hashed_str, float val) {
    for (auto& elem : child_nodes)
        elem.set(slot_str, hashed_str, val);
}


void abstract_group::set_control_array(const char* slot_str, size_t count, float* val) {
    size_t hashed_str = string_hash(slot_str);
    set_control_array(slot_str, hashed_str, count, val);
}

void abstract_group::set_control_array(const char* slot_str, std::size_t hashed_str, size_t count, float* val) {
    for (auto& elem : child_nodes)
        elem.set_control_array(slot_str, hashed_str, count, val);
}

void abstract_group::set_control_array(slot_index_t slot_id, size_t count, float* val) {
    for (auto& elem : child_nodes)
        elem.set_control_array(slot_id, count, val);
}


void abstract_group::set_control_array_element(const char* slot_str, size_t index, float val) {
    size_t hashed_str = string_hash(slot_str);
    set_control_array_element(slot_str, hashed_str, index, val);
}

void abstract_group::set_control_array_element(const char* slot_str, std::size_t hashed_str, size_t index, float val) {
    for (auto& elem : child_nodes)
        elem.set_control_array_element(slot_str, hashed_str, index, val);
}

void abstract_group::set_control_array_element(slot_index_t slot_id, size_t index, float val) {
    for (auto& elem : child_nodes)
        elem.set_control_array_element(slot_id, index, val);
}


int parallel_group::tail_nodes(void) const {
    int ret = child_synth_count;
    for (const auto& elem : child_groups)
        ret += elem.tail_nodes();

    return ret;
}

void group::add_child(server_node* node, node_position_constraint const& constraint) {
    assert(!has_child(node));

    server_node* ref = constraint.first;
    node_position position = constraint.second;

    assert((position == after) || (position == before) || (position == insert));

    server_node_list::const_iterator it = child_nodes.iterator_to(*ref);

    if (position == after)
        ++it;

    child_nodes.insert(it, *node);
    node->set_parent(this);
}

void parallel_group::add_child(server_node* node, node_position_constraint const& constraint) {
    abstract_group::add_child(node);
    /* later warn about this! */
}

void group::add_child(server_node* node, node_position position) {
    assert(!has_child(node));
    assert((position == head) || (position == tail));

    if (position == head)
        child_nodes.push_front(*node);
    else
        child_nodes.push_back(*node);
    node->set_parent(this);
}

void parallel_group::add_child(server_node* node, node_position position) {
    abstract_group::add_child(node);
    /* later warn about this! */
}

} /* namespace nova */
