text:
/***************************************************************************
 * \file WbddcComponent.cpp
 * \brief Defines the WBDDC interface for the NDR324.
 * \author DA
 * \author NH
 * \author MN
 * \copyright (c) 2017 CyberRadio Solutions, Inc.  All rights reserved.
 *
 ***************************************************************************/

#include "LibCyberRadio/Driver/NDR324/WbddcComponent.h"
#include "LibCyberRadio/Driver/RadioHandler.h"
#include <boost/format.hpp>
#include <json/json.h>
#include <boost/lexical_cast.hpp>
#include <iostream>

namespace LibCyberRadio
{
    namespace Driver
    {

        namespace NDR324
        {

            WbddcComponent::WbddcComponent(int index,
                    ::LibCyberRadio::Driver::RadioHandler* parent,
                     bool debug,
                     int dataPort,
                     int rateIndex,
                     int udpDestination,
                     int vitaEnable,
                     int streamId) :
                m324Parent( parent ),
                ::LibCyberRadio::Driver::WbddcComponent(
                        /* const std::string& name */ (boost::format("NDR324-WBDDC%02d") % \
                                index).str(),
                        /* int index */ index,
                        /* ::LibCyberRadio::Driver::RadioHandler* parent */ parent,
                        /* bool debug */ debug,
                        /* bool tunable */ true,
                        /* bool selectableSource */ true,
                        /* bool selectableDataPort */ true,
                        /* bool agc */ true,
                        /* double freqRangeMin */ 0.0,
                        /* double freqRangeMax */ 0.0,
                        /* double freqRes */ 1e6,
                        /* double freqUnits */ 1e6,
                        /* int source */ index,
                        /* int dataPort */ dataPort,
                        /* double frequency */ 0.0,
                        /* int rateIndex */ rateIndex,
                        /* int udpDestination */ udpDestination,
                        /* int vitaEnable */ vitaEnable,
                        /* unsigned int streamId */ streamId)
            {
                this->debug("[NDR324::WbddcComponent] index - %d\n", index);

                initConfigurationDict();
                // Set rate set
                _rateSet[0] = 225.28e6;
                _rateSet[1] = 168.96e6;
                _rateSet[2] = 42.24e6;
                _rateSet[3] = 21.12e6;
                _rateSet[4] = 10.56e6;
                _rateSet[5] = 660e3;
            }

            WbddcComponent::~WbddcComponent()
            {

            }

            WbddcComponent::WbddcComponent(const WbddcComponent& other) :
                ::LibCyberRadio::Driver::WbddcComponent(other)
            {
            }

            WbddcComponent& WbddcComponent::operator=(const WbddcComponent& other)
            {
                ::LibCyberRadio::Driver::WbddcComponent::operator=(other);
                if ( this != &other )
                {
                }
                return *this;
            }

            void WbddcComponent::initConfigurationDict()
            {
                //this->debug("[WbddcComponent::initConfigurationDict] Called\n");
                _config.clear();
                // Call the base-class version
                RadioComponent::initConfigurationDict();
                // Define tuner-specific keys
                _config["filter"] = 40;
                _config["dest"] = 0;
                _config["enable"] = false;
                _config["vita"] = 0;
                _config["type"] = "auto";
                _config["decimation"] = _decimation;
                _config["ovs"] = 1;
                _config["mode"] = _mode;
                _config["dgv"]  = _dgv;
                _config["dul"]  = _dul;
                _config["dll"]  = _dll;
                _config["dtl"]  = _dtl;
                _config["dal"]  = _dal;
                _config["ddl"]  = _ddl;
                _config["dao"]  = _dao;
                _config["ddo"]  = _ddo;
                _config["datc"] = _datc;
                _config["ddtc"] = _ddtc;
                _config["ddt"]  = 90;
                if ( _tunable )
                {
                    _config["offset"] = 0.0f;
                }
                if ( _selectableSource )
                {
                    _config["rfch"] = "0";
                }
                if ( _selectableDataPort )
                {
                    _config["link"] = 0;
                }
                //this->debug("[WbddcComponent::initConfigurationDict] Returning\n");
            }

            bool WbddcComponent::setConfiguration(ConfigurationDict& cfg)
            {
                bool ret = false;
                this->debug("[NDR324WbddcComponent::setConfiguration] Called\n");
                // Setup the JSON Command.
                Json::Value command;
                Json::Value params;
                command["cmd"] = "wbddc";
                command["msg"] = _parent->getMessageId();
                command["params"] = Json::objectValue;
                command["params"]["id"] = _index;

                for( ConfigurationDict::const_iterator it = cfg.begin();
                     it != cfg.end(); ++it)
                {
                    if ( (it->first == "mode") ||
                         (it->first == "rfch") ||
                         (it->first == "type") )
                    {
                        command["params"][it->first.c_str()] = it->second.c_str();
                    }
                    else if ( (it->first == "enable") )
                    {
                        command["params"][it->first.c_str()] = it->second.asBool();
                    }
                    else
                    {
                        command["params"][it->first.c_str()] = it->second.asInt();
                    }
                }
                Json::FastWriter fastWriter;
                std::string output = fastWriter.write(command);
                std::cout << output << std::endl;
                BasicStringList rsp = _parent->sendCommand(output);
                Json::Reader reader;
                Json::Value returnVal;
                std::string t = rsp.at(0);
                bool parsingSuccessful = reader.parse( t.c_str(), returnVal );     //parse process
                ret = returnVal["success"].asBool();
                return ret;
            }

            void WbddcComponent::updateConfigurationDict()
            {
                this->debug("[NDR324WbddcComponent::updateConfigurationDict] Called\n");
                RadioComponent::updateConfigurationDict();
                if ( _config.hasKey("filter") )
                    setConfigurationValueToInt("filter", _rateIndex);
                if ( _config.hasKey("dest") )
                    setConfigurationValueToInt("dest", _udpDestination);
                if ( _config.hasKey("enable") )
                    setConfigurationValueToInt("enable", _vitaEnable);
                if ( _config.hasKey("vita") )
                    setConfigurationValueToUInt("vita", _streamId);
                if ( _tunable && _config.hasKey("offset") )
                {
                    setConfigurationValueToDbl("offset", _frequency);
                }
                if ( _selectableSource && _config.hasKey("rfch") )
                {
                    setConfigurationValueToInt("rfch", _source);
                }
                if ( _selectableDataPort && _config.hasKey("link") )
                {
                    setConfigurationValueToInt("link", _dataPort);
                }
                if ( _config.hasKey("mode") )
                {
                    setConfigurationValue("mode", _mode);
                }
                if ( _config.hasKey("decimation") )
                {
                    setConfigurationValueToInt("decimation", _decimation);
                }
                if ( _config.hasKey("dgv" ) )
                {
                    setConfigurationValueToInt("dgv", _dgv);
                }
                if ( _config.hasKey("dul" ) )
                {
                    setConfigurationValueToInt("dul", _dul);
                }
                if ( _config.hasKey("dll" ) )
                {
                    setConfigurationValueToInt("dll", _dll);
                }
                if ( _config.hasKey("dtl" ) )
                {
                    setConfigurationValueToInt("dtl", _dtl);
                }
                if ( _config.hasKey("dal" ) )
                {
                    setConfigurationValueToInt("dal", _dal);
                }
                if ( _config.hasKey("ddl" ) )
                {
                    setConfigurationValueToInt("ddl", _ddl);
                }
                if ( _config.hasKey("dao" ) )
                {
                    setConfigurationValueToInt("dao", _dao);
                }
                if ( _config.hasKey("ddo" ) )
                {
                    setConfigurationValueToInt("ddo", _ddo);
                }
                if ( _config.hasKey("datc") )
                {
                    setConfigurationValueToInt("datc", _datc);
                }
                if ( _config.hasKey("ddtc") )
                {
                    setConfigurationValueToInt("ddtc", _ddtc);
                }
                if ( _config.hasKey("ddt" ) )
                {
                    setConfigurationValueToInt("ddt", _ddt);
                }
                if ( _config.hasKey("type") )
                {
                    setConfigurationValue("type", _type);
                }
            }

            void WbddcComponent::queryConfiguration()
            {
                this->debug("[WbddcComponent::queryConfiguration] Called\n");
                Json::Value command;
                Json::Value params;
                command["cmd"] = "qwbddc";
                command["msg"] = _parent->getMessageId();
                command["params"] = Json::objectValue;
                command["params"]["id"] = _index;
                Json::FastWriter fastWriter;
                std::string output = fastWriter.write(command);
                BasicStringList rsp = _parent->sendCommand(output);
                Json::Reader reader;
                Json::Value returnVal;
                std::string t = rsp.at(0);
                bool parsingSuccessful = reader.parse( t.c_str(), returnVal );     //parse process
                _enabled = boost::lexical_cast<bool>(returnVal["result"]["enable"].asBool());
                _frequency = boost::lexical_cast<double>(returnVal["result"]["offset"].asDouble());
                _source = boost::lexical_cast<int>(returnVal["result"]["rfch"].asInt());
                _dataPort = boost::lexical_cast<int>(returnVal["result"]["link"].asInt());
                _udpDestination = boost::lexical_cast<int>(returnVal["result"]["dest"].asInt());
                _rateIndex = boost::lexical_cast<int>(returnVal["result"]["filter"].asInt());
                _streamId = boost::lexical_cast<int>(returnVal["result"]["vita"].asUInt());
                _ovs = boost::lexical_cast<int>(returnVal["result"]["ovs"].asInt());
                _decimation = boost::lexical_cast<int>(returnVal["result"]["decimation"].asInt());
                _type = boost::lexical_cast<std::string>(returnVal["result"]["type"].asString());
                _mode = boost::lexical_cast<std::string>(returnVal["result"]["mode"].asString());
                _dgv = boost::lexical_cast<int>(returnVal["result"]["dgv"].asInt());
                _dul = boost::lexical_cast<int>(returnVal["result"]["dul"].asInt());
                _dll = boost::lexical_cast<int>(returnVal["result"]["dll"].asInt());
                _dtl = boost::lexical_cast<int>(returnVal["result"]["dtl"].asInt());
                _dal = boost::lexical_cast<int>(returnVal["result"]["dal"].asInt());
                _ddl = boost::lexical_cast<int>(returnVal["result"]["ddl"].asInt());
                _dao = boost::lexical_cast<int>(returnVal["result"]["dao"].asInt());
                _ddo = boost::lexical_cast<int>(returnVal["result"]["ddo"].asInt());
                _datc = boost::lexical_cast<int>(returnVal["result"]["datc"].asInt());
                _ddtc = boost::lexical_cast<int>(returnVal["result"]["ddtc"].asInt());
                _dat = boost::lexical_cast<int>(returnVal["result"]["dat"].asInt());
                _ddt = boost::lexical_cast<int>(returnVal["result"]["ddt"].asInt());
                updateConfigurationDict();
                this->debug("[WbddcComponent::queryConfiguration] Returning\n");
            }

            /*!
            ** \brief Execute a WBDDC Query and respond with settings
            ** \param index The DDC index
            */
            bool WbddcComponent::executeWbddcQuery(int index, int& rateIndex,
                    int& udpDestination, bool& enabled, int& vitaEnable,
                    unsigned int& streamId)
            {
                bool ret = false;
                if ( (_parent != NULL) && (_parent->isConnected()) )
                {
                    Json::Value root(Json::objectValue);
                    root["msg"] = m324Parent->getMessageId();
                    root["cmd"] = "qwbddc";
                    Json::Value params(Json::objectValue);
                    params["id"] = index;
                    root["params"] = params;
                    Json::FastWriter fastWriter;
                    std::string output = fastWriter.write(root);
                    LibCyberRadio::BasicStringList recv = _parent->sendCommand(output,1.0);
                    Json::Reader reader;
                    Json::Value returnVal;
                    std::string t = recv.at(0);
                    bool parsingSuccessful = reader.parse( t.c_str(), returnVal );     //parse process
                    Json::Value result = returnVal["result"];
                    rateIndex = boost::lexical_cast<int>(result["filter"].asInt());
                    udpDestination = boost::lexical_cast<int>(result["dest"].asInt());
                    enabled = boost::lexical_cast<int>(result["enable"].asBool());
                    vitaEnable = boost::lexical_cast<int>(result["enable"].asBool());
                    streamId = boost::lexical_cast<unsigned int>(result["vita"].asUInt());
                }
                return ret;
            }

            bool WbddcComponent::executeDataPortCommand(int index, int& dataPort)
            {
                bool ret = false;
                if ( (_parent != NULL) && (_parent->isConnected()) )
                {
                    Json::Value root(Json::objectValue);
                    root["msg"] = m324Parent->getMessageId();
                    root["cmd"] = "wbddc";
                    Json::Value params(Json::objectValue);
                    params["id"] = index;
                    params["link"] = dataPort;
                    root["params"] = params;
                    Json::FastWriter fastWriter;
                    std::string output = fastWriter.write(root);
                    LibCyberRadio::BasicStringList recv = _parent->sendCommand(output,1.0);
                    Json::Reader reader;
                    Json::Value returnVal;
                    std::string t = recv.at(0);
                    bool parsingSuccessful = reader.parse( t.c_str(), returnVal );     //parse process
                    ret = returnVal["success"].asBool();
                }
                return ret;
            }

            // Default implementation returns false, since it is based on
            // the NDR308, which does not support selectable-source WBDDCs.
            bool WbddcComponent::executeSourceCommand(int index, int& source)
            {
                bool ret = false;
                if ( (_parent != NULL) && (_parent->isConnected()) )
                {
                    Json::Value root(Json::objectValue);
                    root["msg"] = m324Parent->getMessageId();
                    root["cmd"] = "wbddc";
                    Json::Value params(Json::objectValue);
                    params["id"] = index;

                    params["rfch"] = source;
                    root["params"] = params;
                    Json::FastWriter fastWriter;
                    std::string output = fastWriter.write(root);
                    LibCyberRadio::BasicStringList recv = _parent->sendCommand(output,1.0);
                    Json::Reader reader;
                    Json::Value returnVal;
                    std::string t = recv.at(0);
                    bool parsingSuccessful = reader.parse( t.c_str(), returnVal );     //parse process
                    ret = returnVal["success"].asBool();
                }
                return ret;
            }
            // Default implementation returns false, since it is based on
            // the NDR308, which does not support tunable WBDDCs.
            bool WbddcComponent::executeFreqCommand(int index, double& freq)
            {
                bool ret = false;
                if ( (_parent != NULL) && (_parent->isConnected()) )
                {
                    Json::Value root(Json::objectValue);
                    root["msg"] = m324Parent->getMessageId();
                    root["cmd"] = "wbddc";
                    Json::Value params(Json::objectValue);
                    params["id"] = index;
                    params["offset"] = freq;
                    root["params"] = params;
                    Json::FastWriter fastWriter;
                    std::string output = fastWriter.write(root);
                    LibCyberRadio::BasicStringList recv = _parent->sendCommand(output,1.0);
                    Json::Reader reader;
                    Json::Value returnVal;
                    std::string t = recv.at(0);
                    bool parsingSuccessful = reader.parse( t.c_str(), returnVal );     //parse process
                    ret = returnVal["success"].asBool();
                }
                return ret;
            }

            // Default implementation uses the NDR308 syntax.
            // WBDDC <index>, <rate index>, <udp dest>, <enable>, <vita enable>, <stream id>
            bool WbddcComponent::executeWbddcCommand(int index, int& rateIndex,
                    int& udpDestination, bool& enabled, int& vitaEnable,
                    unsigned int& streamId)
            {
                bool ret = false;
                if ( (_parent != NULL) && (_parent->isConnected()) )
                {
                    Json::Value root(Json::objectValue);
                    root["msg"] = m324Parent->getMessageId();
                    root["cmd"] = "wbddc";
                    Json::Value params(Json::objectValue);
                    params["id"] = index;
                    params["filter"] = rateIndex;
                    params["dest"] = udpDestination;
                    params["enable"] = boost::lexical_cast<bool>(enabled);
                    params["vita"] = streamId;
                    root["params"] = params;
                    Json::FastWriter fastWriter;
                    std::string output = fastWriter.write(root);
                    this->debug("CMD: %s\n", output.c_str());
                    LibCyberRadio::BasicStringList recv = _parent->sendCommand(output,1.0);
                    Json::Reader reader;
                    Json::Value returnVal;
                    std::string t = recv.at(0);
                    bool parsingSuccessful = reader.parse( t.c_str(), returnVal );     //parse process
                    ret = returnVal["success"].asBool();
                }
                return ret;
            }

            bool WbddcComponent::setRateIndex(int index)
            {
                this->debug("Index: %d\n", index);
                bool ret = false;
                if ( _config.hasKey("filter") )
                {
                    int adjRateIndex = index;
                    int adjUdpDest = _udpDestination;
                    int adjVita = _vitaEnable;
                    unsigned int adjStream = _streamId;
                    bool adjEnabled = _enabled;
                    this->debug("Index: %d\n", index);
                    ret = executeWbddcCommand(_index, adjRateIndex, adjUdpDest, adjEnabled, adjVita, adjStream);
                    if ( ret )
                    {
                        _rateIndex = adjRateIndex;
                        updateConfigurationDict();
                    }
                }
                return ret;
            }

            bool WbddcComponent::setFrequency(double freq)
            {
                bool ret = false;
                if ( _tunable && _config.hasKey("offset") )
                {
                    double adjFreq = freq;
                    ret = executeFreqCommand(_index, adjFreq);
                    if ( ret )
                    {
                        _frequency = adjFreq;
                        updateConfigurationDict();
                    }
                }
                return ret;
            }

            bool WbddcComponent::setSource(int source)
            {
                bool ret = false;
                if ( _config.hasKey("rfch") )
                {
                    int adjSource = source;
                    ret = executeSourceCommand(_index, adjSource);
                    if ( ret )
                    {
                        _source = adjSource;
                        updateConfigurationDict();
                    }
                }
                return ret;
            }

            bool WbddcComponent::setUdpDestination(int dest)
            {
                bool ret = false;
                if ( _config.hasKey("dest") )
                {
                    int adjRateIndex = _rateIndex;
                    int adjUdpDest = dest;
                    int adjVita = _vitaEnable;
                    unsigned int adjStream = _streamId;
                    bool adjEnabled = _enabled;
                    ret = executeWbddcCommand(_index, adjRateIndex, adjUdpDest, adjEnabled, adjVita, adjStream);
                    if ( ret )
                    {
                        _udpDestination = adjUdpDest;
                        updateConfigurationDict();
                    }
                }
                return ret;
            }

        } /* namespace NDR324 */

    } // namespace Driver

} // namespace LibCyberRadio

max_stars_repo_name: CyberRadio/libcyberradio

max_issues_repo_name: CyberRadio/libcyberradio

max_forks_repo_name: CyberRadio/libcyberradio


text:
//
// ContextGroup.cpp
//
// LiquidPlayer project
// https://github.com/LiquidPlayer
//
// Created by Eric Lange
//
/*
 Copyright (c) 2016 - 2018 Eric Lange. All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:

 - Redistributions of source code must retain the above copyright notice, this
 list of conditions and the following disclaimer.

 - Redistributions in binary form must reproduce the above copyright notice,
 this list of conditions and the following disclaimer in the documentation
 and/or other materials provided with the distribution.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <exception>
#include <malloc.h>
#include <stdio.h>
#include <condition_variable>
#include <JSC/Macros.h>
#include <boost/make_shared.hpp>
#include "Common/ContextGroup.h"
#include "Common/JSValue.h"
#include "Macros.h"

class GenericAllocator : public ArrayBuffer::Allocator {
public:
    GenericAllocator() {}
    virtual ~GenericAllocator() {}
    virtual void* Allocate(size_t length) {
        unsigned char * mem =  (unsigned char *) malloc(length);
        memset(mem, 0, length);
        return (void*)mem;
    }
    virtual void* AllocateUninitialized(size_t length) {
        return malloc(length);
    }
    virtual void Free(void* data, size_t length) {
        free(data);
    }
};
static GenericAllocator s_allocator;

struct Runnable {
    jobject thiz;
    jobject runnable;
    JavaVM *jvm;
    std::function<void()> c_runnable;
};

class ContextGroupData {
public:
    ContextGroupData(boost::shared_ptr<ContextGroup> cg) : m_context_group(cg) {}
    ~ContextGroupData() { boost::shared_ptr<ContextGroup> group = m_context_group; group.reset(); }
    boost::atomic_shared_ptr<ContextGroup> m_context_group;
};

void ContextGroup::StaticGCPrologueCallback(Isolate *isolate, GCType type, GCCallbackFlags flags)
{
    s_mutex.lock();
    if (s_isolate_map.count(isolate)) {
        s_isolate_map[isolate]->GCPrologueCallback(type, flags);
    }
    s_mutex.unlock();
}

Platform *ContextGroup::s_platform = NULL;
int ContextGroup::s_init_count = 0;
std::mutex ContextGroup::s_mutex;
std::map<Isolate *, ContextGroup *> ContextGroup::s_isolate_map;

void ContextGroup::init_v8()
{
    s_mutex.lock();
    if (s_init_count++ == 0) {
        /* Add any required flags here.
        const char *flags = "--expose_gc";
        V8::SetFlagsFromString(flags, strlen(flags));
        */

        s_platform = platform::CreateDefaultPlatform(4);
        V8::InitializePlatform(s_platform);
        V8::Initialize();
    }

    s_mutex.unlock();
}

void ContextGroup::dispose_v8()
{
    s_mutex.lock();
    // FIXME: Once disposed, an attempt to re-init will crash
    // For now, init once and never dispose
    //--s_init_count;
    if (s_init_count == 0) {
        V8::Dispose();
        V8::ShutdownPlatform();
        delete s_platform;
        s_platform = nullptr;
    }
    s_mutex.unlock();
}

ContextGroup::ContextGroup()
{
    init_v8();
    m_create_params.array_buffer_allocator = &s_allocator;
    m_isolate = Isolate::New(m_create_params);
    m_manage_isolate = true;
    m_uv_loop = nullptr;
    m_thread_id = std::this_thread::get_id();
    m_async_handle = nullptr;
    m_isDefunct = false;
    m_startup_data.data = nullptr;
    m_startup_data.raw_size = 0;
    m_count = 0;

    s_isolate_map[m_isolate] = this;
    m_gc_callbacks.clear();
    m_isolate->AddGCPrologueCallback(StaticGCPrologueCallback);
}

ContextGroup::ContextGroup(Isolate *isolate, uv_loop_t *uv_loop)
{
    m_isolate = isolate;
    m_manage_isolate = false;
    m_uv_loop = uv_loop;
    m_thread_id = std::this_thread::get_id();
    m_async_handle = nullptr;
    m_isDefunct = false;
    m_startup_data.data = nullptr;
    m_startup_data.raw_size = 0;
    m_count = 0;

    s_mutex.lock();
    s_isolate_map[m_isolate] = this;
    s_mutex.unlock();
    m_gc_callbacks.clear();
    m_isolate->AddGCPrologueCallback(StaticGCPrologueCallback);
}

ContextGroup::ContextGroup(char *snapshot, int size)
{
    init_v8();
    m_create_params.array_buffer_allocator = &s_allocator;
    m_startup_data.data = snapshot;
    m_startup_data.raw_size = size;

    if (snapshot && size) {
        m_create_params.snapshot_blob = &m_startup_data;
    }
    m_isolate = Isolate::New(m_create_params);
    m_manage_isolate = true;
    m_uv_loop = nullptr;
    m_thread_id = std::this_thread::get_id();
    m_async_handle = nullptr;
    m_isDefunct = false;
    m_count = 0;

    s_isolate_map[m_isolate] = this;
    m_gc_callbacks.clear();
    m_isolate->AddGCPrologueCallback(StaticGCPrologueCallback);
}

void ContextGroup::MarkZombie(boost::shared_ptr<JSValue> obj)
{
    if ((void*)&*obj != this) {
        m_zombie_mutex.lock();
        m_value_zombies.push_back(obj);
        m_zombie_mutex.unlock();
    }
}

void ContextGroup::MarkZombie(boost::shared_ptr<JSContext> obj)
{
    if ((void*)&*obj != this) {
        m_zombie_mutex.lock();
        m_context_zombies.push_back(obj);
        m_zombie_mutex.unlock();
    }
}

void ContextGroup::FreeZombies()
{
    m_zombie_mutex.lock();
    auto vit = m_value_zombies.begin();
    while (vit != m_value_zombies.end()) {
        auto value = boost::atomic_load(&(*vit));
        value.reset();
        ++vit;
    }
    m_value_zombies.clear();

    /*
     * JSContext zombies indicate that Java is done with the context, however the process
     * is still running.  To ensure that unused processes don't continue to run, we
     * exit the process here.
     */
    auto it = m_context_zombies.begin();
    while (it != m_context_zombies.end()) {
        auto ctx = boost::atomic_load(&(*it));
        if (!ctx->IsDefunct()) {
            V8_ISOLATE_CTX(ctx, isolate, context)
                Local<Object> process =
                        context->Global()->Get(String::NewFromUtf8(isolate, "process"))->ToObject();
                Local<Function> exit = process->Get(
                        String::NewFromUtf8(isolate, "exit")).As<Function>();
                Local<Value> exit_code = Number::New(isolate,
                                                     CONTEXT_GARBAGE_COLLECTED_BUT_PROCESS_STILL_ACTIVE);
                exit->Call(process, 1, &exit_code);
#if DEBUG
                __android_log_assert("condition", "FreeZombies()",
                                     "Context was collected but process still runnning");
#endif
            V8_UNLOCK()
        }
        ctx.reset();
        ++it;
    }

    m_context_zombies.clear();
    m_zombie_mutex.unlock();
}

void ContextGroup::callback(uv_async_t* handle)
{
    ContextGroupData *data = reinterpret_cast<ContextGroupData*>(handle->data);
    boost::shared_ptr<ContextGroup> group = data->m_context_group;
    delete data;

    // Since we are in the correct thread now, free the zombies!
    group->FreeZombies();

    group->m_async_mutex.lock();
    struct Runnable *r = group->m_runnables.empty() ? nullptr :
                         (struct Runnable *) group->m_runnables.front();

    while (r) {
        group->m_async_mutex.unlock();

        if (r->c_runnable) {
            r->c_runnable();
        } else {
            JNIEnv *env;
            int getEnvStat = r->jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
            if (getEnvStat == JNI_EDETACHED) {
                r->jvm->AttachCurrentThread(&env, NULL);
            }

            jclass cls = env->GetObjectClass(r->thiz);
            jmethodID mid;
            do {
                mid = env->GetMethodID(cls,"inContextCallback","(Ljava/lang/Runnable;)V");
                if (!env->ExceptionCheck()) break;
                env->ExceptionClear();
                jclass super = env->GetSuperclass(cls);
                env->DeleteLocalRef(cls);
                if (super == NULL || env->ExceptionCheck()) {
                    if (super != NULL) env->DeleteLocalRef(super);
                    if (getEnvStat == JNI_EDETACHED) {
                        r->jvm->DetachCurrentThread();
                    }
                    __android_log_assert("FAIL", "ContextGroup::callback",
                        "Can't find the class to call back?");
                }
                cls = super;
            } while (true);
            env->DeleteLocalRef(cls);

            env->CallVoidMethod(r->thiz, mid, r->runnable);

            env->DeleteGlobalRef(r->thiz);
            env->DeleteGlobalRef(r->runnable);

            if (getEnvStat == JNI_EDETACHED) {
                r->jvm->DetachCurrentThread();
            }
        }

        group->m_async_mutex.lock();

        group->m_runnables.erase(group->m_runnables.begin());
        delete r;

        r = group->m_runnables.empty() ? nullptr : (struct Runnable *) group->m_runnables.front();
    }
    // Close the handle.  We will create a new one if we
    // need another.  This keeps the node process from staying alive
    // indefinitely
    uv_close((uv_handle_t*)handle, [](uv_handle_t *h){
        delete (uv_async_t*)h;
    });
    group->m_async_handle = nullptr;
    group->m_async_mutex.unlock();
}

void ContextGroup::RegisterGCCallback(void (*cb)(GCType, GCCallbackFlags, void*), void *data)
{
    auto gc = std::unique_ptr<struct GCCallback>(new struct GCCallback);
    gc->cb = cb;
    gc->data = data;
    m_gc_callbacks.push_back(std::move(gc));
}

void ContextGroup::UnregisterGCCallback(void (*cb)(GCType, GCCallbackFlags, void*), void *data)
{
    auto it = m_gc_callbacks.begin();

    while (it != m_gc_callbacks.end()) {
        const auto& item = *it;
        ++it;
        if (item->cb == cb && item->data == data) {
            m_gc_callbacks.remove(item);
        }
    }
}

void ContextGroup::GCPrologueCallback(GCType type, GCCallbackFlags flags)
{
    auto it = m_gc_callbacks.begin();

    while (it != m_gc_callbacks.end()) {
        const auto& item = *it;
        ++it;
        item->cb(type, flags, item->data);
    }
}

void ContextGroup::Manage(boost::shared_ptr<JSValue> obj)
{
    m_managedValues.push_back(obj);
}

void ContextGroup::Manage(boost::shared_ptr<JSContext> obj)
{
    m_managedContexts.push_back(obj);
}

void ContextGroup::Dispose()
{
    if (!m_isDefunct) {
        // Make sure we don't get destructed during the managed values/context disposal process
        auto wait = shared_from_this();

        //ASSERTJSC(m_runnables.empty());
        m_runnables.clear();

        m_isolate->RemoveGCPrologueCallback(StaticGCPrologueCallback);

        for (auto it = m_managedValues.begin(); it != m_managedValues.end(); ++it) {
            boost::shared_ptr<JSValue> valid = (*it).lock();
            if (valid) {
                valid->Dispose();
            }
        }
        for (auto it = m_managedContexts.begin(); it != m_managedContexts.end(); ++it) {
            boost::shared_ptr<JSContext> valid = (*it).lock();
            if (valid) {
                valid->Dispose();
            }
        }
        m_isDefunct = true;
        m_managedValues.clear();
        m_managedContexts.clear();
        FreeZombies();

        s_mutex.lock();
        if (s_isolate_map.count(m_isolate)) {
            s_isolate_map.erase(m_isolate);
        }
        s_mutex.unlock();
        if (m_manage_isolate) {
            m_isolate->Dispose();
        } else {
            dispose_v8();
        }

        if (m_startup_data.data && m_startup_data.raw_size) {
            delete [] m_startup_data.data;
        }

        wait.reset();
    }
}

ContextGroup::~ContextGroup()
{
    Dispose();
}

void ContextGroup::sync_(std::function<void()> runnable)
{
    std::condition_variable cv;
    bool signaled = false;

    struct Runnable *r = new struct Runnable;
    r->thiz = nullptr;
    r->runnable = nullptr;
    r->jvm = nullptr;
    r->c_runnable = [&]() {
        runnable();
        {
            std::lock_guard<std::mutex> lk(m_async_mutex);
            signaled = true;
        }
        cv.notify_one();
    };

    std::unique_lock<std::mutex> lk(m_async_mutex);
    m_runnables.push_back((void*)r);

    if (!m_async_handle) {
        m_async_handle = new uv_async_t();
        m_async_handle->data = new ContextGroupData(shared_from_this());
        uv_async_init(Loop(), m_async_handle, ContextGroup::callback);
        uv_async_send(m_async_handle);
    }

    cv.wait(lk, [&]{return signaled;});
    lk.unlock();
}

void ContextGroup::schedule_java_runnable(JNIEnv *env, jobject thiz, jobject runnable)
{
    m_async_mutex.lock();

    struct Runnable *r = new struct Runnable;
    r->thiz = env->NewGlobalRef(thiz);
    r->runnable = env->NewGlobalRef(runnable);
    r->c_runnable = nullptr;
    env->GetJavaVM(&r->jvm);
    m_runnables.push_back((void*)r);

    if (!m_async_handle) {
        m_async_handle = new uv_async_t();
        m_async_handle->data = new ContextGroupData(shared_from_this());
        uv_async_init(Loop(), m_async_handle, ContextGroup::callback);
        uv_async_send(m_async_handle);
    }
    m_async_mutex.unlock();
}

boost::shared_ptr<ContextGroup> ContextGroup::New(const char *snapshotFile)
{
    char *data = nullptr;
    int size;

    FILE *fp = fopen(snapshotFile, "rb");
    if (fp) {
        fseek(fp, 0L, SEEK_END);
        size = ftell(fp);
        rewind(fp);
        data = new char[size];
        int read = fread((void*)data, sizeof (char), size, fp);
        if (read != size) {
            delete[] data;
            data = nullptr;
        }
        fclose(fp);
    }

    if (data) {
        return boost::make_shared<ContextGroup>(data, size);
    } else {
        return boost::make_shared<ContextGroup>();
    }
}

ext: cpp

max_stars_repo_name: maorhayoun/LiquidCore

max_issues_repo_name: maorhayoun/LiquidCore

max_forks_repo_name: maorhayoun/LiquidCore


text:
#include <iostream>
#include <cmath>
#include <utility>
#ifndef HAVE_OLD_CPP
# include <unordered_map>
#else
# include <tr1/unordered_map>
namespace std { using std::tr1::unordered_map; }
#endif

#include <boost/functional/hash.hpp>
#include <boost/program_options.hpp>
#include <boost/program_options/variables_map.hpp>

#include "m.h"
#include "corpus_tools.h"
#include "stringlib.h"
#include "filelib.h"
#include "ttables.h"
#include "tdict.h"
#include "da.h"

namespace po = boost::program_options;
using namespace std;

bool InitCommandLine(int argc, char** argv, po::variables_map* conf) {
  po::options_description opts("Configuration options");
  opts.add_options()
        ("input,i",po::value<string>(),"Parallel corpus input file")
        ("reverse,r","Reverse estimation (swap source and target during training)")
        ("iterations,I",po::value<unsigned>()->default_value(5),"Number of iterations of EM training")
        //("bidir,b", "Run bidirectional alignment")
        ("favor_diagonal,d", "Use a static alignment distribution that assigns higher probabilities to alignments near the diagonal")
        ("prob_align_null", po::value<double>()->default_value(0.08), "When --favor_diagonal is set, what's the probability of a null alignment?")
        ("diagonal_tension,T", po::value<double>()->default_value(4.0), "How sharp or flat around the diagonal is the alignment distribution (<1 = flat >1 = sharp)")
        ("optimize_tension,o", "Optimize diagonal tension during EM")
        ("variational_bayes,v","Infer VB estimate of parameters under a symmetric Dirichlet prior")
        ("alpha,a", po::value<double>()->default_value(0.01), "Hyperparameter for optional Dirichlet prior")
        ("no_null_word,N","Do not generate from a null token")
        ("output_parameters,p", po::value<string>(), "Write model parameters to file")
        ("beam_threshold,t",po::value<double>()->default_value(-4),"When writing parameters, log_10 of beam threshold for writing parameter (-10000 to include everything, 0 max parameter only)")
        ("hide_training_alignments,H", "Hide training alignments (only useful if you want to use -x option and just compute testset statistics)")
        ("testset,x", po::value<string>(), "After training completes, compute the log likelihood of this set of sentence pairs under the learned model")
        ("no_add_viterbi,V","When writing model parameters, do not add Viterbi alignment points (may generate a grammar where some training sentence pairs are unreachable)")
		("force_align,f",po::value<string>(), "Load previously written parameters to 'force align' input. Set --diagonal_tension and --mean_srclen_multiplier as estimated during training.")
		("mean_srclen_multiplier,m",po::value<double>()->default_value(1), "When --force_align, use this source length multiplier")
    ("init_ttable,J",po::value<string>(), "Initialize ttable with this file (output of -p). Also give --diagonal_tension.");
  po::options_description clo("Command line options");
  clo.add_options()
        ("config", po::value<string>(), "Configuration file")
        ("help,h", "Print this help message and exit");
  po::options_description dconfig_options, dcmdline_options;
  dconfig_options.add(opts);
  dcmdline_options.add(opts).add(clo);

  po::store(parse_command_line(argc, argv, dcmdline_options), *conf);
  if (conf->count("config")) {
    ifstream config((*conf)["config"].as<string>().c_str());
    po::store(po::parse_config_file(config, dconfig_options), *conf);
  }
  po::notify(*conf);

  if (conf->count("help") || conf->count("input") == 0) {
    cerr << "Usage " << argv[0] << " [OPTIONS] -i corpus.fr-en\n";
    cerr << dcmdline_options << endl;
    return false;
  }
  return true;
}

int main(int argc, char** argv) {
  po::variables_map conf;
  if (!InitCommandLine(argc, argv, &conf)) return 1;
  const string fname = conf["input"].as<string>();
  const bool reverse = conf.count("reverse") > 0;
  const int ITERATIONS = (conf.count("force_align")) ? 0 : conf["iterations"].as<unsigned>();
  const double BEAM_THRESHOLD = pow(10.0, conf["beam_threshold"].as<double>());
  const bool use_null = (conf.count("no_null_word") == 0);
  const WordID kNULL = TD::Convert("<eps>");
  const bool add_viterbi = (conf.count("no_add_viterbi") == 0);
  const bool variational_bayes = (conf.count("variational_bayes") > 0);
  const bool output_parameters = (conf.count("force_align")) ? false : conf.count("output_parameters");
  double diagonal_tension = conf["diagonal_tension"].as<double>();
  bool optimize_tension = conf.count("optimize_tension");
  bool hide_training_alignments = (conf.count("hide_training_alignments") > 0);
  const bool write_alignments = (conf.count("force_align")) ? true : !hide_training_alignments;
  string testset;
  if (conf.count("testset")) testset = conf["testset"].as<string>();
  if (conf.count("force_align")) testset = fname;
  double prob_align_null = conf["prob_align_null"].as<double>();
  double prob_align_not_null = 1.0 - prob_align_null;
  const double alpha = conf["alpha"].as<double>();
  const bool favor_diagonal = conf.count("favor_diagonal");
  if (variational_bayes && alpha <= 0.0) {
    cerr << "--alpha must be > 0\n";
    return 1;
  }

  TTable s2t, t2s;
  TTable::Word2Word2Double s2t_viterbi;
  unordered_map<pair<short, short>, unsigned, boost::hash<pair<short, short> > > size_counts;
  double tot_len_ratio = 0;
  double mean_srclen_multiplier = 0;
  vector<double> probs;

  if (conf.count("force_align")) {
	// load model parameters
	ReadFile s2t_f(conf["force_align"].as<string>());
	s2t.DeserializeLogProbsFromText(s2t_f.stream());
	mean_srclen_multiplier = conf["mean_srclen_multiplier"].as<double>();
  }

  if (conf.count("init_ttable")) {
	  ReadFile s2t_f(conf["init_ttable"].as<string>());
	  s2t.DeserializeLogProbsFromText(s2t_f.stream());
  }

  for (int iter = 0; iter < ITERATIONS; ++iter) {
    const bool final_iteration = (iter == (ITERATIONS - 1));
    cerr << "ITERATION " << (iter + 1) << (final_iteration ? " (FINAL)" : "") << endl;
    ReadFile rf(fname);
    istream& in = *rf.stream();
    double likelihood = 0;
    double denom = 0.0;
    int lc = 0;
    bool flag = false;
    string line;
    string ssrc, strg;
    vector<WordID> src, trg;
    double c0 = 0;
    double emp_feat = 0;
    double toks = 0;
    while(true) {
      getline(in, line);
      if (!in) break;
      ++lc;
      if (lc % 1000 == 0) { cerr << '.'; flag = true; }
      if (lc %50000 == 0) { cerr << " [" << lc << "]\n" << flush; flag = false; }
      src.clear(); trg.clear();
      CorpusTools::ReadLine(line, &src, &trg);
      if (reverse) swap(src, trg);
      if (src.size() == 0 || trg.size() == 0) {
        cerr << "Error: " << lc << "\n" << line << endl;
        return 1;
      }
      if (iter == 0)
        tot_len_ratio += static_cast<double>(trg.size()) / static_cast<double>(src.size());
      denom += trg.size();
      probs.resize(src.size() + 1);
      if (iter == 0)
        ++size_counts[make_pair<short,short>(trg.size(), src.size())];
      bool first_al = true;  // used for write_alignments
      toks += trg.size();
      for (unsigned j = 0; j < trg.size(); ++j) {
        const WordID& f_j = trg[j];
        double sum = 0;
        double prob_a_i = 1.0 / (src.size() + use_null);  // uniform (model 1)
        if (use_null) {
          if (favor_diagonal) prob_a_i = prob_align_null;
          probs[0] = s2t.prob(kNULL, f_j) * prob_a_i;
          sum += probs[0];
        }
        double az = 0;
        if (favor_diagonal)
          az = DiagonalAlignment::ComputeZ(j+1, trg.size(), src.size(), diagonal_tension) / prob_align_not_null;
        for (unsigned i = 1; i <= src.size(); ++i) {
          if (favor_diagonal)
            prob_a_i = DiagonalAlignment::UnnormalizedProb(j + 1, i, trg.size(), src.size(), diagonal_tension) / az;
          probs[i] = s2t.prob(src[i-1], f_j) * prob_a_i;
          sum += probs[i];
        }
        if (final_iteration) {
          if (add_viterbi || write_alignments) {
            WordID max_i = 0;
            double max_p = -1;
            int max_index = -1;
            if (use_null) {
              max_i = kNULL;
              max_index = 0;
              max_p = probs[0];
            }
            for (unsigned i = 1; i <= src.size(); ++i) {
              if (probs[i] > max_p) {
                max_index = i;
                max_p = probs[i];
                max_i = src[i-1];
              }
            }
            if (!hide_training_alignments && write_alignments) {
              if (max_index > 0) {
                if (first_al) first_al = false; else cout << ' ';
                if (reverse)
                  cout << j << '-' << (max_index - 1);
                else
                  cout << (max_index - 1) << '-' << j;
              }
            }
            if (s2t_viterbi.size() <= static_cast<unsigned>(max_i)) s2t_viterbi.resize(max_i + 1);
            s2t_viterbi[max_i][f_j] = 1.0;
          }
        } else {
          if (use_null) {
            double count = probs[0] / sum;
            c0 += count;
            s2t.Increment(kNULL, f_j, count);
          }
          for (unsigned i = 1; i <= src.size(); ++i) {
            const double p = probs[i] / sum;
            s2t.Increment(src[i-1], f_j, p);
            emp_feat += DiagonalAlignment::Feature(j, i, trg.size(), src.size()) * p;
          }
        }
        likelihood += log(sum);
      }
      if (write_alignments && final_iteration && !hide_training_alignments) cout << endl;
    }

    // log(e) = 1.0
    double base2_likelihood = likelihood / log(2);

    if (flag) { cerr << endl; }
    if (iter == 0) {
      mean_srclen_multiplier = tot_len_ratio / lc;
      cerr << "expected target length = source length * " << mean_srclen_multiplier << endl;
    }
    emp_feat /= toks;
    cerr << "  log_e likelihood: " << likelihood << endl;
    cerr << "  log_2 likelihood: " << base2_likelihood << endl;
    cerr << "     cross entropy: " << (-base2_likelihood / denom) << endl;
    cerr << "        perplexity: " << pow(2.0, -base2_likelihood / denom) << endl;
    cerr << "      posterior p0: " << c0 / toks << endl;
    cerr << " posterior al-feat: " << emp_feat << endl;
    //cerr << "     model tension: " << mod_feat / toks << endl;
    cerr << "       size counts: " << size_counts.size() << endl;
    if (!final_iteration) {
      if (favor_diagonal && optimize_tension && iter > 0) {
        for (int ii = 0; ii < 8; ++ii) {
          double mod_feat = 0;
          unordered_map<pair<short,short>,unsigned,boost::hash<pair<short, short> > >::iterator it = size_counts.begin();
          for(; it != size_counts.end(); ++it) {
            const pair<short,short>& p = it->first;
            for (short j = 1; j <= p.first; ++j)
              mod_feat += it->second * DiagonalAlignment::ComputeDLogZ(j, p.first, p.second, diagonal_tension);
          }
          mod_feat /= toks;
          cerr << "  " << ii + 1 << "  model al-feat: " << mod_feat << " (tension=" << diagonal_tension << ")\n";
          diagonal_tension += (emp_feat - mod_feat) * 20.0;
          if (diagonal_tension <= 0.1) diagonal_tension = 0.1;
          if (diagonal_tension > 14) diagonal_tension = 14;
        }
        cerr << "     final tension: " << diagonal_tension << endl;
      }
      if (variational_bayes)
        s2t.NormalizeVB(alpha);
      else
        s2t.Normalize();
      //prob_align_null *= 0.8; // XXX
      //prob_align_null += (c0 / toks) * 0.2;
      prob_align_not_null = 1.0 - prob_align_null;
    }
  }
  if (testset.size()) {
    ReadFile rf(testset);
    istream& in = *rf.stream();
    int lc = 0;
    double tlp = 0;
    string line;
    while (getline(in, line)) {
      ++lc;
      vector<WordID> src, trg;
      CorpusTools::ReadLine(line, &src, &trg);
      cout << TD::GetString(src) << " ||| " << TD::GetString(trg) << " |||";
      if (reverse) swap(src, trg);
      double log_prob = Md::log_poisson(trg.size(), 0.05 + src.size() * mean_srclen_multiplier);

      // compute likelihood
      for (unsigned j = 0; j < trg.size(); ++j) {
        const WordID& f_j = trg[j];
        double sum = 0;
        int a_j = 0;
        double max_pat = 0;
        double prob_a_i = 1.0 / (src.size() + use_null);  // uniform (model 1)
        if (use_null) {
          if (favor_diagonal) prob_a_i = prob_align_null;
          max_pat = s2t.prob(kNULL, f_j) * prob_a_i;
          sum += max_pat;
        }
        double az = 0;
        if (favor_diagonal)
          az = DiagonalAlignment::ComputeZ(j+1, trg.size(), src.size(), diagonal_tension) / prob_align_not_null;
        for (unsigned i = 1; i <= src.size(); ++i) {
          if (favor_diagonal)
            prob_a_i = DiagonalAlignment::UnnormalizedProb(j + 1, i, trg.size(), src.size(), diagonal_tension) / az;
          double pat = s2t.prob(src[i-1], f_j) * prob_a_i;
          if (pat > max_pat) { max_pat = pat; a_j = i; }
          sum += pat;
        }
        log_prob += log(sum);
        if (write_alignments) {
          if (a_j > 0) {
            cout << ' ';
            if (reverse)
              cout << j << '-' << (a_j - 1);
            else
              cout << (a_j - 1) << '-' << j;
          }
        }
      }
      tlp += log_prob;
      cout << " ||| " << log_prob << endl << flush;
    } // loop over test set sentences
    cerr << "TOTAL LOG PROB " << tlp << endl;
  }

  if (output_parameters) {
    WriteFile params_out(conf["output_parameters"].as<string>());
    for (unsigned eind = 1; eind < s2t.ttable.size(); ++eind) {
      const auto& cpd = s2t.ttable[eind];
      const TTable::Word2Double& vit = s2t_viterbi[eind];
      const string& esym = TD::Convert(eind);
      double max_p = -1;
      for (auto& fi : cpd)
        if (fi.second > max_p) max_p = fi.second;
      const double threshold = max_p * BEAM_THRESHOLD;
      for (auto& fi : cpd) {
        if (fi.second > threshold || (vit.find(fi.first) != vit.end())) {
          *params_out << esym << ' ' << TD::Convert(fi.first) << ' ' << log(fi.second) << endl;
        }
      }
    }
  }
  return 0;
}

ext: cc

max_stars_repo_name: pks/cdec-dtrain-legacy

max_issues_repo_name: pks/cdec-dtrain-legacy

max_forks_repo_name: pks/cdec-dtrain-legacy


text:
// __BEGIN_LICENSE__
//  Copyright (c) 2009-2013, United States Government as represented by the
//  Administrator of the National Aeronautics and Space Administration. All
//  rights reserved.
//
//  The NGT platform is licensed under the Apache License, Version 2.0 (the
//  "License"); you may not use this file except in compliance with the
//  License. You may obtain a copy of the License at
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// __END_LICENSE__

/// \file bundlevis.cc
///

#include <vw/Core/ProgressCallback.h>
#include <vw/Math/BBox.h>
#include <vw/Math/Statistics.h>
#include <asp/Tools/bundlevis.h>

#include <boost/foreach.hpp>

using namespace vw;

// This builds the 3 Axis that represents the camera
osg::Geode* build3Axis( float const& line_length ){
  osg::Geode* axisGeode = new osg::Geode();
  osg::Geometry* axis = new osg::Geometry();

  osg::Vec3Array* vertices = new osg::Vec3Array(6);
  (*vertices)[0].set( 0.0f, 0.0f, 0.0f );
  (*vertices)[1].set( line_length, 0.0f, 0.0f );
  (*vertices)[2].set( 0.0f, 0.0f, 0.0f );
  (*vertices)[3].set( 0.0f, line_length, 0.0f );
  (*vertices)[4].set( 0.0f, 0.0f, 0.0f );
  (*vertices)[5].set( 0.0f, 0.0f, line_length );
  axis->setVertexArray( vertices );

  osg::Vec4Array* colours = new osg::Vec4Array(6);
  (*colours)[0].set( 1.0f, 0.0f, 0.0f, 1.0f );
  (*colours)[1].set( 1.0f, 0.0f, 0.0f, 1.0f );
  (*colours)[2].set( 0.0f, 1.0f, 0.0f, 1.0f );
  (*colours)[3].set( 0.0f, 1.0f, 0.0f, 1.0f );
  (*colours)[4].set( 0.0f, 0.0f, 1.0f, 1.0f );
  (*colours)[5].set( 0.0f, 0.0f, 1.0f, 1.0f );
  axis->setColorArray( colours );
  axis->setColorBinding( osg::Geometry::BIND_PER_VERTEX );

  axis->addPrimitiveSet( new osg::DrawArrays( GL_LINES, 0, vertices->getNumElements()));

  osg::StateSet* stateSet = new osg::StateSet();
  stateSet->setAttribute( new osg::LineWidth(2) );

  axisGeode->setStateSet( stateSet );
  axisGeode->addDrawable( axis );

  return axisGeode;
}

// This builds the matrix transform for a camera
osg::MatrixTransform*
CameraIter::matrix_transform( int const& step, int const& vertice = 0 ) {
  osg::MatrixTransform* mt = new osg::MatrixTransform;

  osg::Matrixf rot, trans;
  rot.makeRotate( this->pose( step, vertice ) );
  trans.makeTranslate( this->position( step, vertice ) );
  mt->setMatrix( rot*trans );
  mt->setUpdateCallback( new cameraMatrixCallback( this, vertice ));

  return mt;
}

// This will load a points iteration file
std::vector<PointIter*> loadPointData( std::string pntFile,
                                       int* step ){
  std::cout << "Loading Points Iteration Data : " << pntFile << std::endl;

  // Determing the number of lines in the file
  std::ifstream file(pntFile.c_str(), std::ios::in);
  if ( !file.is_open() )
    vw_throw( ArgumentErr() << "Unable to open: " << pntFile << "!\n" );
  int numLines = 0, numPoints = 0, numTimeIter = 0;
  char c;
  while (!file.eof()){
    c=file.get();
    if (c == '\n'){
      int buffer;
      numLines++;
      file >> buffer;
      if ( buffer > numPoints )
        numPoints = buffer;
    }
  }
  numPoints++;
  numTimeIter=numLines/numPoints;

  std::cout << "Number of Points found: " << numPoints << std::endl;

  // Starting from the top again
  file.clear();
  file.seekg(0);

  // Building the point organizational structure
  std::vector<PointIter*> pointData;
  for ( int i = 0; i < numPoints; ++i )
    pointData.push_back( new PointIter(i, step) );

  // Loading up the data finally

  // For every time iteration
  for ( int t = 0; t < numTimeIter; ++t ){
    for ( int i = 0; i < numPoints; ++i ){
      float float_fill_buffer;
      osg::Vec3f vec_fill_buffer;

      // The first one is just the point number
      file >> float_fill_buffer;
      if (float_fill_buffer != i)
        std::cout << "Reading number mismatch. Reading pnt " << i
                 << " , found it to be " << float_fill_buffer << std::endl;

      // Filling the vector
      file >> vec_fill_buffer[0];
      file >> vec_fill_buffer[1];
      file >> vec_fill_buffer[2];

      // Now attaching the data
      pointData[i]->add_iteration( vec_fill_buffer,
                                  0.0f);

    }
  }

  if ( pointData[0]->size() != (unsigned) numTimeIter )
    std::cout << "Number of Time Iterations found, " << numTimeIter
             << ", not equal loaded, " << pointData[0]->size()
             << std::endl;

  return pointData;
}

// This will load a camera data file
std::vector<CameraIter*> loadCameraData( std::string camFile,
                                         int* step ){

  std::cout << "Loading Cameras Iteration Data : " << camFile << std::endl;

  // Determing the number of lines in the file
  std::ifstream file(camFile.c_str(), std::ifstream::in);
  if ( !file.is_open() )
    vw_throw( ArgumentErr() << "Unable to open: " << camFile << "!\n" );
  int32 numLines = 0, numCameras = 0, numTimeIter = 0, numCameraParam = 1;
  int32 last_camera = -1, buffer;
  while (file.good()){
    file >> buffer;
    if (file.eof())
      break;

    if ( numCameras == 0 ) {
      if ( buffer == last_camera )
        numCameraParam++;
      last_camera = buffer;
    }
    if ( buffer > numCameras )
      numCameras = buffer;

    numLines++;
    char c = file.get();
    while ( c != '\n' && !file.eof() )
      c = file.get();
  }
  numCameras++;
  numTimeIter=numLines/(numCameras*numCameraParam);

  std::cout << "Number of Cameras found: " << numCameras << "\n"
            << "Number of Camera Parameters found: " << numCameraParam << "\n"
            << "Number of Time Iterations found: " << numTimeIter << "\n";

  // Starting from the top again
  file.clear();
  file.seekg(0);

  // Building the camera organizational structure
  std::vector<CameraIter*> cameraData;
  for ( int j = 0; j < numCameras; ++j ) {
    if ( numCameraParam == 1 || numCameraParam == 6 )
      cameraData.push_back( new CameraIter(j, step) );
    else
      cameraData.push_back( new CameraIter(j, step, numCameraParam) );
  }

  // Loading up the data finally

  // For every time iteration
  std::string throwAway;
  for ( int t = 0; t < numTimeIter; ++t ){
    for ( int j = 0; j < numCameras; ++j ){
      float float_fill_buffer;

      if ( numCameraParam == 1 ) {
        // Simple Camera

        // First one is just the Camera ID
        file >> float_fill_buffer;
        if (float_fill_buffer != j)
          std::cout << "Reading number mismatch. Reading camera " << j
                   << ", found it to be " << float_fill_buffer << std::endl;

        osg::Vec3f position_buffer;
        osg::Quat  pose_buffer;

        // This whole conditional mess is to handle NAN, at least I
        // hope it works!
        for ( unsigned i = 0; i < 3; i++ ) {
          if (!(file >> position_buffer[i])) {
            file >> throwAway;
            position_buffer[i] = 0;
          }
        }
        // Reason for this .. is that the Quat is written in VW format
        if (!(file >> pose_buffer[3])) {
          file >> throwAway;
          pose_buffer[3] = 0;
        }
        for ( unsigned i = 0; i < 3; i++ ) {
          if (!(file >> pose_buffer[i])) {
            file >> throwAway;
            pose_buffer[i] = 0;
          }
        }

        // Now attaching the data
        cameraData[j]->add_iteration( position_buffer,
                                      pose_buffer );
      } else {
        // Linescan Camera
        std::vector< osg::Vec3f> position_buffer;
        std::vector< osg::Quat> pose_buffer;

        // Going through all the vertice data which was given
        for ( int p = 0; p < numCameraParam; ++p ) {
          // The first on is just the Camera ID
          file >> float_fill_buffer;
          if ( float_fill_buffer != j)
            std::cout << "Reading number mismatch. Reading camera " << j
                      << ", found it to be " << float_fill_buffer << std::endl;

          position_buffer.push_back( osg::Vec3f() );
          for ( unsigned i = 0; i < 3; i++ ) {
            if (!(file >> position_buffer.back()[i])) {
              file >> throwAway;
              position_buffer.back()[i] = 0;
            }
          }
          pose_buffer.push_back( osg::Quat() );
          if (!(file >> pose_buffer.back()[3])) { // Files are written
                                                  // in VW notation
            file >> throwAway;
            pose_buffer.back()[3] = 0;
          }
          for ( unsigned i = 0; i < 3; i++ ) {
            if (!(file >> pose_buffer.back()[i])) {
              file >> throwAway;
              pose_buffer.back()[i] = 0;
            }
          }
        }

        // Cool.. now I'm going to attach this data
        cameraData[j]->add_iteration( position_buffer, pose_buffer );
      }
    }
  }

  if ( (cameraData[0]->size())/(cameraData[0]->num_vertices()) != (unsigned) numTimeIter )
    std::cout << "Number of Time Iterations found, " << numTimeIter
              << ", not equal loaded, " << cameraData[0]->size()/cameraData[0]->num_vertices()
             << std::endl;
  return cameraData;
}

// This will load a control network file
std::vector<ConnLineIter*> loadControlNet( std::string cnetFile,
                                           std::vector<PointIter*>& points,
                                           std::vector<CameraIter*>& cameras,
                                           ba::ControlNetwork* cnet,
                                           int* step ) {
  std::cout << "Loading Control Network : " << cnetFile << std::endl;

  cnet = new ba::ControlNetwork("Bundlevis");
  cnet->read_binary( cnetFile );

  if ( cnet->size() != points.size() ) {
    std::cout << "Control network doesn't seem to match loaded points data. Number of points: "
             << points.size() << " Number of points in Control Network: "
             << cnet->size() << std::endl;
    delete cnet;
    std::vector<ConnLineIter*> blank;
    return blank;
  }

  // Building the Connecting Line organizational structure
  std::vector<ConnLineIter*> connLineData;
  // For every point
  for ( unsigned p = 0; p < cnet->size(); ++p ) {
    points[p]->set_gcp( (*cnet)[p].type() == ba::ControlPoint::GroundControlPoint  );

    if (cameras.size()) {
      // For every measure
      for ( unsigned m = 0; m < (*cnet)[p].size(); ++m ){
        // Is this a valid camera ?
        if ( (*cnet)[p][m].image_id() < cameras.size() ) {
          // Now popping on a new connection
          connLineData.push_back( new ConnLineIter( points[p],
                                                    cameras[(*cnet)[p][m].image_id()],
                                                    step ) );
        }
      } //end for
    } // end if
  } // end for

  std::cout << "Number of Connections found: " << connLineData.size()
            << std::endl;

  return connLineData;
}

// This will build the entire scene
osg::Node* createScene( std::vector<PointIter*>& points,
                        std::vector<CameraIter*>& cameras,
                        std::vector<ConnLineIter*>& connLines,
                        std::vector< std::vector<PointIter*> >& addPoints,
                        BBox3f const* point_cloud, double cam_distance ) {

  osg::Group* scene = new osg::Group();

  // First, setting the back drop to be dark
  {
    osg::ClearNode* backdrop = new osg::ClearNode;
    backdrop->setClearColor( osg::Vec4f( 0.1f, 0.1f, 0.1f, 1.0f ) );
    scene->addChild(backdrop);
  }

  // Second, drawing the points
  if ( points.size() ){
    osg::Geometry* geometry = new osg::Geometry;

    // Setting up vertices
    osg::Vec3Array* vertices = new osg::Vec3Array( points.size()*2 );
    geometry->setVertexArray( vertices );

    for ( unsigned i = 0; i < points.size()*2; i+=2 ) {
      (*vertices)[i] = points[i/2]->position(0);
      (*vertices)[i+1] = (*vertices)[i] + osg::Vec3f(0.0f, 0.0f, 0.1f);
    }

    // Setting up color
    osg::Vec4Array* colours = new osg::Vec4Array( points.size()*2 );
    (*colours)[0].set(1.0f, 0.5f, 1.0f, 1.0f);  // Generic Tie Points (PURPLE)
    (*colours)[1].set(0.5f, 1.0f, 0.5f, 1.0f);  // Ground Control Points (GREEN)
    for ( unsigned i = 0; i < points.size(); i++ ) {
      if ( points[i/2]->is_gcp() ) {
        (*colours)[2*i].set(0.5f,1.0f,0.5f,1.0f);
        (*colours)[2*i+1].set(0.5f,1.0f,0.5f,1.0f);
      } else {
        (*colours)[2*i].set(1.0f,0.5f,1.0f,1.0f);
        (*colours)[2*i+1].set(1.0f,0.5f,1.0f,1.0f);
      }
    }
    geometry->setColorArray( colours );

    // Setting up primitive to draw line to last location
    geometry->addPrimitiveSet( new osg::DrawArrays(GL_LINES, 0, vertices->size() ));

    // Setting up to draw points at the front end of the line
    osg::DrawElementsUInt* pointsArray = new osg::DrawElementsUInt(GL_POINTS, points.size());
    geometry->addPrimitiveSet( pointsArray );
    for (unsigned i = 0; i < points.size(); ++i)
      (*pointsArray)[i] = i*2;

    geometry->setUseDisplayList(false);
    geometry->setDrawCallback( new pointsDrawCallback( &points ) );

    // Making geode
    osg::Geode* geode = new osg::Geode;
    geode->addDrawable( geometry );

    // Making the points large
    osg::StateSet* stateSet = new osg::StateSet();
    osg::Point* point = new osg::Point();
    point->setSize( 2.0f );
    stateSet->setAttribute( point );
    geode->setStateSet( stateSet );

    scene->addChild( geode );
  }

  // 2.b Adding additional points
  for ( size_t n = 0; n < addPoints.size(); ++n ) {
    osg::Geometry* geometry = new osg::Geometry;

    // Setting up vertices
    osg::Vec3Array* vertices = new osg::Vec3Array( addPoints[n].size()*2 );
    geometry->setVertexArray( vertices );

    for ( unsigned i = 0; i < addPoints[n].size()*2; i+=2 ) {
      (*vertices)[i] = addPoints[n][i/2]->position(0);
      (*vertices)[i+1] = (*vertices)[i] + osg::Vec3f(0.0f, 0.0f, 0.1f);
    }

    // Setting up color
    osg::Vec4Array* colours = new osg::Vec4Array( 1 );
    geometry->setColorArray( colours );
    geometry->setColorBinding( osg::Geometry::BIND_OVERALL );
    (*colours)[0].set(1.0f, 0.25f, 0.5f, 0.5f);

    // Setting up primitive to draw line to last location
    geometry->addPrimitiveSet( new osg::DrawArrays(GL_LINES, 0, vertices->size() ));

    // Setting up to draw points at the front end of the line
    osg::DrawElementsUInt* pointsArray = new osg::DrawElementsUInt(GL_POINTS, addPoints[n].size());
    geometry->addPrimitiveSet( pointsArray );
    for (unsigned i = 0; i < addPoints[n].size(); ++i)
      (*pointsArray)[i] = i*2;

    geometry->setUseDisplayList(false);
    geometry->setDrawCallback( new pointsDrawCallback( &(addPoints[n]) ) );

    // Making geode
    osg::Geode* geode = new osg::Geode;
    geode->addDrawable( geometry );

    // Making the points large
    osg::StateSet* stateSet = new osg::StateSet();
    osg::Point* point = new osg::Point();
    point->setSize( 2.0f );
    stateSet->setAttribute( point );
    geode->setStateSet( stateSet );

    scene->addChild( geode );
  }

  // Thirdly, draw cameras
  if ( cameras.size() ){

    //osg::Geode* the3Axis = build3Axis();
    osg::Geode* the3Axis;

    if ( point_cloud ) {
      the3Axis = build3Axis( cam_distance*0.8 );
    } else {
      the3Axis = build3Axis( 1.0 );
    }

    osg::Group* camerasGroup = new osg::Group;

    for (unsigned j = 0; j < cameras.size(); ++j){
      osg::MatrixTransform* mt = cameras[j]->matrix_transform( 0 );
      mt->addChild( the3Axis );
      camerasGroup->addChild( mt );
    }

    // 3b.) Draw potential pushbroom camera lines
    osg::Group* pushbroomGroup = new osg::Group;
    {
      vw::TerminalProgressCallback tpc("asp","Loading Camera: ");
      tpc.report_progress(0);
      for (unsigned j = 0; j < cameras.size(); ++j){
        tpc.report_progress(float(j)/float(cameras.size()));
        if ( cameras[j]->is_pushbroom() ) {
          osg::Geometry* geometry = new osg::Geometry;

          // Setting up vertices
          osg::Vec3Array* vertices = new osg::Vec3Array( cameras[j]->num_vertices() );
          geometry->setVertexArray( vertices );

          geometry->setUseDisplayList( false );
          geometry->setDrawCallback( new pushbroomDrawCallback( cameras[j] ) );

          for ( unsigned i = 0; i < vertices->size(); ++i ){
            (*vertices)[i] = cameras[j]->position( 0, i );
          }

          // Setting up color
          osg::Vec4Array* colours = new osg::Vec4Array( 1 );
          geometry->setColorArray( colours );
          geometry->setColorBinding( osg::Geometry::BIND_OVERALL );
          (*colours)[0].set(1.0f, 1.0f, 0.0f, 1.0f);

          // Setting up primitive to draw lines
          geometry->addPrimitiveSet( new osg::DrawArrays(GL_LINE_STRIP, 0, vertices->size() ));

          // Making geode
          osg::Geode* geode = new osg::Geode;
          geode->addDrawable( geometry );

          // Setting the line width
          osg::StateSet* stateSet = new osg::StateSet();
          stateSet->setAttribute( new osg::LineWidth(2) );
          geode->setStateSet( stateSet );
          pushbroomGroup->addChild( geode );

          // Also throwing in another 3Axis at the end of the line
          osg::MatrixTransform* mt = cameras[j]->matrix_transform(0, cameras[j]->num_vertices()-1);
          mt->addChild( the3Axis );
          pushbroomGroup->addChild( mt );
        }
      }
      camerasGroup->addChild( pushbroomGroup );
      tpc.report_finished();
    }

    scene->addChild( camerasGroup );
  }

  // 4.) Draw Connecting Lines
  if ( connLines.size() ){
    osg::Group* linesGroup = new osg::Group;

    for ( unsigned i = 0; i < connLines.size(); i+= 1 ){
      osg::Geometry* geometry = new osg::Geometry;

      osg::Vec3Array* vertices = new osg::Vec3Array( 2 );
      (*vertices)[0] = connLines[i]->point()->position( 0 );
      (*vertices)[1] = connLines[i]->camera()->position( 0 );
      geometry->setVertexArray( vertices );

      osg::Vec4Array* colours = new osg::Vec4Array( 1 );
      geometry->setColorArray( colours );
      geometry->setColorBinding( osg::Geometry::BIND_OVERALL );
      (*colours)[0].set(0.8f, 0.8f, 0.8f, 1.0f);

      geometry->addPrimitiveSet( new osg::DrawArrays(GL_LINES, 0, 2) );

      geometry->setUseDisplayList( false );
      geometry->setDrawCallback( new linesDrawCallback( connLines[i] ));

      osg::Geode* geode = new osg::Geode;
      geode->addDrawable( geometry );
      linesGroup->addChild( geode );
    }

    scene->addChild( linesGroup );

  }

  // 5.) Build hit points, surfaces that the mouse intersector can use
  {
    double scale = cam_distance * 0.5;
    osg::Group* hitTargetGroup = new osg::Group;

    // Targets for cameras
    if ( cameras.size() ) {
      // Building the target that all will use
      osg::Vec3Array* vertices = new osg::Vec3Array(4);
      (*vertices)[0].set( 1.0f, -1.0f, -0.1f );
      (*vertices)[1].set( 1.0f, 1.0f, -0.1f );
      (*vertices)[2].set( -1.0f, 1.0f, -0.1f );
      (*vertices)[3].set( -1.0f, -1.0f, -0.1f );
      osg::Vec4Array* colours = new osg::Vec4Array(1);
      (*colours)[0].set( 1.0f, 1.0f, 1.0f, 0.0f );

      for ( size_t j = 0; j < cameras.size(); ++j ) {

        // Geode that is the target
        osg::Geometry* geometry = new osg::Geometry;
        geometry->setVertexArray( vertices );
        geometry->setColorArray( colours );
        geometry->setColorBinding( osg::Geometry::BIND_OVERALL );
        geometry->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::QUADS, 0, 4 ) );
        osg::Geode* geode = new osg::Geode;
        geode->setName( cameras[j]->description() );
        geode->setUserData( cameras[j] );
        geode->addDrawable( geometry );

        // Add some text, cause it's cool
        osgText::Text* text = new osgText::Text;
        std::ostringstream os;
        os << (j + 1);
        text->setCharacterSize( 70.0 );
        //text->setFontResolution(40,40);
        text->setText( os.str() );
        text->setAlignment( osgText::Text::CENTER_CENTER );
        //text->setCharacterSizeMode( osgText::Text::OBJECT_COORDS_WITH_MAXIMUM_SCREEN_SIZE_CAPPED_BY_FONT_HEIGHT );
        geode->addDrawable( text );

        // Transform that does my bidding
        osg::AutoTransform* autoT = new osg::AutoTransform;
        autoT->addChild( geode );
        autoT->setAutoRotateMode( osg::AutoTransform::ROTATE_TO_SCREEN );
        autoT->setAutoScaleToScreen( true );
        autoT->setMinimumScale( 0.0f );
        autoT->setMaximumScale( 0.1f * scale );
        autoT->setPosition( cameras[j]->position( 0 ) );
        autoT->setUpdateCallback( new cameraAutoMatrixCallback( cameras[j] ));

        hitTargetGroup->addChild( autoT );
      }
    }

    // Targets for points
    if ( points.size() ) {
      // Building the target that all will use
      osg::Vec3Array* vertices = new osg::Vec3Array(4);
      (*vertices)[0].set( 10.0f, -10.0f, -0.5f );
      (*vertices)[1].set( 10.0f, 10.0f, -0.5f );
      (*vertices)[2].set( -10.0f, 10.0f, -0.5f );
      (*vertices)[3].set( -10.0f, -10.0f, -0.5f );
      osg::Vec4Array* colours = new osg::Vec4Array(1);
      (*colours)[0].set( 0.0f, 0.0f, 0.0f, 0.0f );

      for ( size_t i = 0; i < points.size(); ++i ) {

        // Geode that is the target
        osg::Geometry* geometry = new osg::Geometry;
        geometry->setVertexArray( vertices );
        geometry->setColorArray( colours );
        geometry->setColorBinding( osg::Geometry::BIND_OVERALL );
        geometry->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::QUADS,
                                                       0,
                                                       4 ) );

        // Add some text
        osgText::Text* text = new osgText::Text;
        std::ostringstream os;
        os << (i + 1);
        text->setCharacterSize( 25.0 );
        //text->setFontResolution(40,40);
        text->setText( os.str() );
        text->setAlignment( osgText::Text::CENTER_CENTER );
        //text->setCharacterSizeMode( osgText::Text::OBJECT_COORDS_WITH_MAXIMUM_SCREEN_SIZE_CAPPED_BY_FONT_HEIGHT );

        osg::Geode* geode =new osg::Geode;
        geode->addDrawable( text );
        geode->addDrawable( text );
        geode->setName( points[i]->description() );
        geode->addDrawable( geometry );
        geode->setUserData( points[i] );

        // Building an LOD so it shuts off when too far away
        osg::LOD* lod = new osg::LOD;
        lod->addChild( geode, 10.0f, 1000.0f );
        lod->setRangeMode( osg::LOD::PIXEL_SIZE_ON_SCREEN );
        lod->setRadius( scale/10.0 );

        // Transform that does my bidding
        osg::AutoTransform* autoT = new osg::AutoTransform;
        autoT->addChild(lod);
        autoT->setAutoRotateMode( osg::AutoTransform::ROTATE_TO_SCREEN );
        autoT->setAutoScaleToScreen( true );
        autoT->setMinimumScale( 0.0f );
        autoT->setMaximumScale( 0.0005f * scale );
        autoT->setPosition( points[i]->position( 0 ) );
        autoT->setUpdateCallback( new pointAutoMatrixCallback( points[i] ));

        hitTargetGroup->addChild( autoT );
      }
    }

    scene->addChild ( hitTargetGroup );
  }

  return scene;
}

// This is the event handler, mouse, and keyboard.
bool AllEventHandler::handle( const osgGA::GUIEventAdapter& ea,
                              osgGA::GUIActionAdapter& aa ) {
  // Note: for some reason, you should only perform getEventType()
  // once.
  osgGA::GUIEventAdapter::EventType event = ea.getEventType();

  if ( event == osgGA::GUIEventAdapter::KEYDOWN ) {
    switch (ea.getKey()){
    case 'z':     //Step Backwards
      if ( *m_step - 1 < 1 )
        *m_step = m_numIter;
      else
        (*m_step)--;
      break;
    case 'x':     //Play
      m_playControl->set_play();
      break;
    case 'c':     //Pause
      m_playControl->set_pause();
      break;
    case 'v':     //Stop
      m_playControl->set_stop();
      break;
    case 'b':     //Step Forward
      if ( *m_step + 1 > m_numIter )
        *m_step = 1;
      else
        (*m_step)++;
      break;
    case '0':     //Show all steps
      *m_step = 0;
      break;
    case '1':     //Move to the first frame
      *m_step = 1;
      break;
    case '2':     //Move ro a place somewhere in between
      *m_step = (m_numIter * 2) / 9;
      break;
    case '3':
      *m_step = (m_numIter * 3) / 9;
      break;
    case '4':
      *m_step = (m_numIter * 4) / 9;
      break;
    case '5':
      *m_step = (m_numIter * 5) / 9;
      break;
    case '6':
      *m_step = (m_numIter * 6) / 9;
      break;
    case '7':
      *m_step = (m_numIter * 7) / 9;
      break;
    case '8':
      *m_step = (m_numIter * 8) / 9;
      break;
    case '9':     //Move to the last frame
      *m_step = m_numIter;
      break;
    default:
      break;
    }
  } else if ( event == osgGA::GUIEventAdapter::PUSH ) {
    osgViewer::View* view = dynamic_cast< osgViewer::View* >(&aa);
    if (view)
      pick( view, ea );
  }
  return false;
}

void AllEventHandler::pick( osgViewer::View* view, const osgGA::GUIEventAdapter& ea ) {
  osgUtil::LineSegmentIntersector::Intersections intersections;

  float x = ea.getX();
  float y = ea.getY();

  if (view->computeIntersections( x, y, intersections )){
    //I only care about the first intersection event
    osgUtil::LineSegmentIntersector::Intersections::iterator hit =
      intersections.begin();
    if (!hit->nodePath.empty() && !(hit->nodePath.back()->getName().empty())){
      std::cout << hit->nodePath.back()->getName() << std::endl;

      // Now I'll determine if it's a point
      PointIter* point =
        dynamic_cast< PointIter* >( hit->nodePath.back()->getUserData() );
      if ( point ) {
        point->toggle_drawconnlines();
        return;
      }

      // Well... snap, is it a camera?
      CameraIter* camera =
        dynamic_cast< CameraIter* >( hit->nodePath.back()->getUserData() );
      if ( camera ) {
        camera->toggle_drawconnlines();
        return;
      }
    }
  }
}

// Main Execution
int main(int argc, char* argv[]){

  //Variables to be used later in this section
  int* controlStep = new int(0);
  std::string camera_iter_file, points_iter_file;
  std::string pixel_iter_file, control_net_file;
  std::vector<std::string> additional_pnt_files;
  ba::ControlNetwork* cnet = NULL;
  std::vector<PointIter*> pointData;
  std::vector<CameraIter*> cameraData;
  std::vector<ConnLineIter*> connLineData;
  std::vector<std::vector<PointIter*> > addPointData;
  PlaybackControl* playControl = new PlaybackControl(controlStep);
  boost::scoped_ptr<BBox3f> point_cloud;        // World size of the objects
  double q25_camera_distance; // Average distance between cameras

  //OpenSceneGraph Variable which are important overall
  osgViewer::Viewer viewer;
  osg::ref_ptr<osg::Group> root = new osg::Group();

  //This first half of the program is wholly devoted to setting up boost program options
  //Boost program options code
  po::options_description general_options("Your most awesome Options");
  general_options.add_options()
    ("camera-iteration-file,c",po::value<std::string>(&camera_iter_file),"Load the camera parameters for each iteration from a file.")
    ("points-iteration-file,p",po::value<std::string>(&points_iter_file),"Load the 3d points parameters for each iteration from a file.")
    ("pixel-iteration-file,x",po::value<std::string>(&pixel_iter_file),"Load the pixel information data. Allowing for an illustration of the pixel data over time.")
    ("control-network-file,n",po::value<std::string>(&control_net_file),"Load the control network for point and camera relationship status. Camera and Point Iteration data is needed before a control network file can be used.")
    ("additional-pnt-files",po::value< std::vector<std::string> >(&additional_pnt_files),"For additional iterPoint files that can be plotted simultaneously.")
    ("fullscreen","Set bundlevis to render with the entire screen, does not work so well with dual screens.")
    ("stereo","Set bundlevis to display in anagylph mode.")
    ("show-moon","Add a transparent Moon to the display.")
    ("show-mars","Add a transparent Mars to the display.")
    ("show-earth","Add a transparent Earth to the display.")
    ("help,h","Display this help message.");

  std::ostringstream usage;
  usage << "Usage: " << argv[0] << "[options] <filename> ... " << std::endl << std::endl;
  usage << general_options << std::endl;

  po::variables_map vm;
  try {
    po::store(po::parse_command_line(argc,argv,general_options),vm);
    po::notify(vm);
  } catch (po::error const& e) {
    std::cout << "An error occured while parsing command line arguments.\n";
    std::cout << "\t" << e.what() << "\n\n";
    std::cout << usage.str();
    return 1;
  }

  if (vm.count("help")){
    std::cout<<usage.str();
    return 1;
  }

  //////////////////////////////////////////////////////////
  //Stereo?
  if (vm.count("stereo")){
    osg::DisplaySettings::instance()->setStereo(true);
  }

  //////////////////////////////////////////////////////////
  //Did the User mess up?
  if (!vm.count("points-iteration-file") && !vm.count("camera-iteration-file")){
    std::cout << usage.str();
    return 1;
  }

  //////////////////////////////////////////////////////////
  //Loading up point iteration data
  if (vm.count("points-iteration-file")){
    pointData = loadPointData( points_iter_file,
                               controlStep );
  }

  //////////////////////////////////////////////////////////
  //Loading up camera iteration data
  if (vm.count("camera-iteration-file")){
    cameraData = loadCameraData( camera_iter_file,
                                 controlStep );
  }

  //////////////////////////////////////////////////////////
  //Gathering Statistics about point cloud
  {
    math::CDFAccumulator<double> xcdf, ycdf, zcdf, dcdf;

    if (vm.count("points-iteration-file")) {
      BOOST_FOREACH( PointIter* pitr, pointData ) {
        xcdf( pitr->position(0)[0] );
        ycdf( pitr->position(0)[1] );
        zcdf( pitr->position(0)[2] );
      }
    }

    Vector3 before;
    if (vm.count("camera-iteration-file")) {
      BOOST_FOREACH( CameraIter* citr, cameraData ) {
        xcdf( citr->position(0)[0] );
        ycdf( citr->position(0)[1] );
        zcdf( citr->position(0)[2] );
        if ( before == Vector3() ) {
          before = Vector3( citr->position(0)[0],
                            citr->position(0)[1],
                            citr->position(0)[2] );
        } else {
          Vector3 current( citr->position(0)[0],
                           citr->position(0)[1],
                           citr->position(0)[2] );
          dcdf( norm_2(current-before) );
          before = current;
        }
      }
    }

    xcdf.update(); ycdf.update(); zcdf.update(); dcdf.update();
    point_cloud.reset( new BBox3f(Vector3(xcdf.quantile(.1),
                                          ycdf.quantile(.1),
                                          zcdf.quantile(.1)),
                                  Vector3(xcdf.quantile(.9),
                                          ycdf.quantile(.9),
                                          zcdf.quantile(.9))) );
    q25_camera_distance = dcdf.quantile(.25);
    if (q25_camera_distance < 1 ) q25_camera_distance = 1;

    Vector3 center = point_cloud->center();
    Vector3 size = point_cloud->size();

    std::cout << "Point Cloud:\n\t> Center " << center << "\n\t> Size " << size;
    std::cout << "\n\t> CSize " << q25_camera_distance << std::endl;
  }

  //////////////////////////////////////////////////////////
  //Loading up additional point iteration data
  if (vm.count("additional-pnt-files")) {
    std::cout << "Checking to see if there is additional:" << std::endl;
    for (unsigned n = 0; n < additional_pnt_files.size(); ++n) {
      std::cout << " > " << additional_pnt_files[n] << std::endl;
      addPointData.push_back( loadPointData( additional_pnt_files[n],
                                             controlStep ) );
    }
  }

  //////////////////////////////////////////////////////////
  //Loading the control network
  if (vm.count("control-network-file") && (pointData.size()) ){
    connLineData = loadControlNet( control_net_file,
                                   pointData,
                                   cameraData,
                                   cnet,
                                   controlStep );
  }

  //////////////////////////////////////////////////////////
  //Setting up window if requested
  if ( !vm.count( "fullscreen" ) ){
    std::cout << "Building Window\n";

    osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
    traits->x = 100;
    traits->y = 100;
    traits->width = 1280;
    traits->height = 1024;
    traits->windowDecoration = true;
    traits->doubleBuffer = true;
    traits->sharedContext = 0;
    traits->windowName = "Bundlevis 2.1";
    traits->vsync = true;
    traits->useMultiThreadedOpenGLEngine = false;
    traits->supportsResize = true;
    traits->useCursor = true;

    osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());

    osg::ref_ptr<osg::Camera> camera = new osg::Camera;
    camera->setGraphicsContext(gc.get());
    camera->setViewport(new osg::Viewport(0,0, traits->width, traits->height));
    GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;
    camera->setDrawBuffer(buffer);
    camera->setReadBuffer(buffer);

    viewer.addSlave(camera.get());
  }

  //////////////////////////////////////////////////////////
  // Adding user Requested data
  {
    // I can haz moon?
    if (vm.count("show-moon")){

      osg::ref_ptr<osg::Geode> moon_geode = new osg::Geode();

      osg::ref_ptr<osg::ShapeDrawable> sphere = new osg::ShapeDrawable( new osg::Sphere(osg::Vec3(0,0,0), 1737100)); // meters
      sphere->setColor( osg::Vec4f(0.5,0.5,0.5,0.25)); // Gray 25% Opacity
      osg::ref_ptr<osg::PolygonMode> pm = new osg::PolygonMode( osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE );
      sphere->getOrCreateStateSet()->setAttributeAndModes( pm.get(), osg::StateAttribute::ON); // Wireframe
      moon_geode->addDrawable( sphere.get() );

      root->addChild( moon_geode.get() );
    }

    if (vm.count("show-mars")){

      osg::ref_ptr<osg::Geode> mars_geode = new osg::Geode();

      osg::ref_ptr<osg::ShapeDrawable> sphere = new osg::ShapeDrawable( new osg::Sphere(osg::Vec3(0,0,0), 3396200)); // meters
      sphere->setColor( osg::Vec4f(1.0,0.0,0.0,0.25)); // Red 25% Opacity
      osg::ref_ptr<osg::PolygonMode> pm = new osg::PolygonMode( osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE );
      sphere->getOrCreateStateSet()->setAttributeAndModes( pm.get(), osg::StateAttribute::ON); // Wireframe
      mars_geode->addDrawable( sphere.get() );

      root->addChild( mars_geode.get() );
    }

    if (vm.count("show-earth")){

      osg::ref_ptr<osg::Geode> earth_geode = new osg::Geode();

      osg::ref_ptr<osg::ShapeDrawable> sphere = new osg::ShapeDrawable( new osg::Sphere(osg::Vec3(0,0,0), 6371000)); // meters
      sphere->setColor( osg::Vec4f(0.2,0.2,1.0,0.25)); // Blue 25% Opacity
      osg::ref_ptr<osg::PolygonMode> pm = new osg::PolygonMode( osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE );
      sphere->getOrCreateStateSet()->setAttributeAndModes( pm.get(), osg::StateAttribute::ON); // Wireframe
      earth_geode->addDrawable( sphere.get() );

      root->addChild( earth_geode.get() );
    }
  }

  //////////////////////////////////////////////////////////
  //Building Scene
  {
    root->addChild( createScene( pointData,    cameraData,
                                 connLineData, addPointData,
                                 point_cloud.get(),  q25_camera_distance ) );

    // Optimizing the Scene (seems like good practice in OSG)
    osgUtil::Optimizer optimizer;
    optimizer.optimize( root.get() );

    osgGA::TrackballManipulator* trackball = new osgGA::TrackballManipulator();
    viewer.setCameraManipulator( trackball );
    viewer.setSceneData( root.get() );

    int numIter;
    if ( pointData.size() > 0 )
      numIter = pointData[0]->size();
    else
      numIter = cameraData[0]->size();
    viewer.addEventHandler( new AllEventHandler( controlStep,
                                                 numIter,
                                                 playControl) );

    root->setUpdateCallback( new playbackNodeCallback ( controlStep,
                                                        numIter,
                                                        playControl) );

    trackball->setDistance(norm_2(point_cloud->size()));
    trackball->setCenter( osg::Vec3(point_cloud->center()[0],
                                    point_cloud->center()[1],
                                    point_cloud->center()[2]) );
  }

  //////////////////////////////////////////////////////////
  //Setting some global scene parameters
  {
    osg::StateSet* stateSet = new osg::StateSet();
    stateSet->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
    stateSet->setMode( GL_BLEND, osg::StateAttribute::ON );
    root->setStateSet( stateSet );
  }

  viewer.realize();
  viewer.run();
  std::cout << "Ending\n";

  return 0;
}

max_stars_repo_name: AndrewAnnex/StereoPipeline

max_issues_repo_name: AndrewAnnex/StereoPipeline

max_forks_repo_name: AndrewAnnex/StereoPipeline