/**
 * UGENE - Integrated Bioinformatics Tools.
 * Copyright (C) 2008-2024 UniPro <ugene@unipro.ru>
 * http://ugene.net
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 */

#include "DebugStatusWrap.h"

#include <U2Core/AppContext.h>

#include <U2Lang/ScriptContext.h>

#include "ActorWrap.h"
#include "NodeApiUtils.h"

const char* WRONG_ARGUMENT_TYPE_ERROR = "Wrong argument type";

namespace U2 {

namespace Js {

Persistent<Function> DebugStatusWrap::CONSTRUCTOR;
const char* DebugStatusWrap::CLASS_NAME = "DebugStatus";

DebugStatusWrap::DebugStatusWrap(WorkflowDebugStatus* initDebugStatus)
    : debugStatus(initDebugStatus) {
}

DebugStatusWrap::~DebugStatusWrap() {
}

void DebugStatusWrap::init() {
    Local<FunctionTemplate> tpl = FunctionTemplate::New(newObject);
    tpl->SetClassName(String::NewSymbol(CLASS_NAME));
    tpl->InstanceTemplate()->SetInternalFieldCount(1);

    tpl->PrototypeTemplate()->Set(String::NewSymbol("addBreakpointToActor"),
                                  FunctionTemplate::New(addBreakpointToActor)->GetFunction());
    tpl->PrototypeTemplate()->Set(String::NewSymbol("removeBreakpointFromActor"),
                                  FunctionTemplate::New(removeBreakpointFromActor)->GetFunction());
    tpl->PrototypeTemplate()->Set(String::NewSymbol("setBreakpointEnabled"),
                                  FunctionTemplate::New(setBreakpointEnabled)->GetFunction());
    tpl->PrototypeTemplate()->Set(String::NewSymbol("getActorsWithBreakpoints"),
                                  FunctionTemplate::New(getActorsWithBreakpoints)->GetFunction());
    tpl->PrototypeTemplate()->Set(String::NewSymbol("setPaused"),
                                  FunctionTemplate::New(setPaused)->GetFunction());
    tpl->PrototypeTemplate()->Set(String::NewSymbol("isPaused"),
                                  FunctionTemplate::New(isPaused)->GetFunction());

    CONSTRUCTOR = Persistent<Function>::New(tpl->GetFunction());
}

Handle<Value> DebugStatusWrap::newInstance(int argc, const Handle<Value>* argv) {
    HandleScope scope;
    const int requiredArgumentCount = 1;
    if (!NodeApiUtils::isArgumentCountCorrect(argc, requiredArgumentCount)) {
        return scope.Close(Undefined());
    }
    Handle<Value> objectInitData[] = {argv[0]};
    Local<Object> instance = CONSTRUCTOR->NewInstance(requiredArgumentCount, objectInitData);
    return scope.Close(instance);
}

Handle<Value> DebugStatusWrap::newObject(const Arguments& args) {
    HandleScope scope;
    if (!NodeApiUtils::isArgumentCountCorrect(args.Length(), 1)) {
        return scope.Close(Undefined());
    }
    auto debugStatus = reinterpret_cast<WorkflowDebugStatus*>(args[0]->IntegerValue());
    DebugStatusWrap* obj = new DebugStatusWrap(debugStatus);
    obj->Wrap(args.This());
    return args.This();
}

Handle<Value> DebugStatusWrap::addBreakpointToActor(const Arguments& args) {
    HandleScope scope;
    if (!NodeApiUtils::isArgumentCountCorrect(args.Length(), 1)) {
        return scope.Close(Undefined());
    }
    if (!args[0]->IsString()) {
        ThrowException(Exception::TypeError(String::New(WRONG_ARGUMENT_TYPE_ERROR)));
        return scope.Close(Undefined());
    }
    DebugStatusWrap* obj = ObjectWrap::Unwrap<DebugStatusWrap>(args.This());

    const String::Utf8Value actorId(args[0]->ToString());
    obj->debugStatus->addBreakpointToActor(*actorId);
    return scope.Close(Undefined());
}

Handle<Value> DebugStatusWrap::removeBreakpointFromActor(const Arguments& args) {
    HandleScope scope;
    if (!NodeApiUtils::isArgumentCountCorrect(args.Length(), 1)) {
        return scope.Close(Undefined());
    }
    if (!args[0]->IsString()) {
        ThrowException(Exception::TypeError(String::New(WRONG_ARGUMENT_TYPE_ERROR)));
        return scope.Close(Undefined());
    }
    DebugStatusWrap* obj = ObjectWrap::Unwrap<DebugStatusWrap>(args.This());

    const String::Utf8Value actorId(args[0]->ToString());
    obj->debugStatus->removeBreakpointFromActor(*actorId);
    return scope.Close(Undefined());
}

Handle<Value> DebugStatusWrap::setBreakpointEnabled(const Arguments& args) {
    HandleScope scope;
    if (!NodeApiUtils::isArgumentCountCorrect(args.Length(), 2)) {
        return scope.Close(Undefined());
    }
    if (!args[0]->IsString() || !args[1]->IsBoolean()) {
        ThrowException(Exception::TypeError(String::New(WRONG_ARGUMENT_TYPE_ERROR)));
        return scope.Close(Undefined());
    }
    DebugStatusWrap* obj = ObjectWrap::Unwrap<DebugStatusWrap>(args.This());

    const String::Utf8Value actorId(args[0]->ToString());
    bool enabled = args[1]->ToBoolean()->Value();
    obj->debugStatus->setBreakpointEnabled(*actorId, enabled);
    return scope.Close(Undefined());
}

Handle<Value> DebugStatusWrap::getActorsWithBreakpoints(const Arguments& args) {
    HandleScope scope;
    if (!NodeApiUtils::isArgumentCountCorrect(args.Length(), 0)) {
        return scope.Close(Undefined());
    }
    DebugStatusWrap* obj = ObjectWrap::Unwrap<DebugStatusWrap>(args.This());
    QList<ActorId> actorIds = obj->debugStatus->getActorsWithBreakpoints();

    ScriptContext* scriptContext = AppContext::getScriptContext();
    Q_ASSERT(scriptContext != nullptr);

    Local<Object> actorList = Object::New();
    foreach (ActorId id, actorIds) {
        Actor* actor = scriptContext->getActorById(id);
        Handle<Value> actorInitData[] = {Int32::New(reinterpret_cast<int>(actor))};
        Handle<Value> wrappedActor = ActorWrap::newInstance(1, actorInitData);
        actorList->Set(String::NewSymbol(actor->getId().toLocal8Bit().constData()),
                       wrappedActor);
    }
    return scope.Close(actorList);
}

Handle<Value> DebugStatusWrap::setPaused(const Arguments& args) {
    HandleScope scope;
    if (!NodeApiUtils::isArgumentCountCorrect(args.Length(), 1)) {
        return scope.Close(Undefined());
    }
    if (!args[0]->IsBoolean()) {
        ThrowException(Exception::TypeError(String::New(WRONG_ARGUMENT_TYPE_ERROR)));
        return scope.Close(Undefined());
    }
    DebugStatusWrap* obj = ObjectWrap::Unwrap<DebugStatusWrap>(args.This());
    bool pause = args[0]->ToBoolean()->Value();
    if (pause) {
        obj->debugStatus->sl_pauseTriggerActivated();
    } else {
        obj->debugStatus->sl_resumeTriggerActivated();
    }
    return scope.Close(Undefined());
}

Handle<Value> DebugStatusWrap::isPaused(const Arguments& args) {
    HandleScope scope;
    if (!NodeApiUtils::isArgumentCountCorrect(args.Length(), 0)) {
        return scope.Close(Undefined());
    }
    DebugStatusWrap* obj = ObjectWrap::Unwrap<DebugStatusWrap>(args.This());
    return scope.Close(Boolean::New(obj->debugStatus->isPaused()));
}

}  // namespace Js

}  // namespace U2
