// Copyright (c) 2020 Mobvoi Inc (Binbin Zhang)
//               2022 PaddlePaddle Authors
// 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.

#include "websocket/websocket_client.h"

#include "boost/json/src.hpp"

namespace json = boost::json;

namespace ppspeech {

WebSocketClient::WebSocketClient(const std::string& host, int port)
    : host_(host), port_(port) {
    Connect();
    t_.reset(new std::thread(&WebSocketClient::ReadLoopFunc, this));
}

void WebSocketClient::Connect() {
    tcp::resolver resolver{ioc_};
    // Look up the domain name
    auto const results = resolver.resolve(host_, std::to_string(port_));
    // Make the connection on the IP address we get from a lookup
    auto ep = asio::connect(ws_.next_layer(), results);
    // Update the host_ string. This will provide the value of the
    // Host HTTP header during the WebSocket handshake.
    // See https://tools.ietf.org/html/rfc7230#section-5.4
    std::string host = host_ + ":" + std::to_string(ep.port());
    // Perform the websocket handshake
    ws_.handshake(host, "/");
}

void WebSocketClient::SendTextData(const std::string& data) {
    ws_.text(true);
    ws_.write(asio::buffer(data));
}

void WebSocketClient::SendBinaryData(const void* data, size_t size) {
    ws_.binary(true);
    ws_.write(asio::buffer(data, size));
}

void WebSocketClient::Close() { ws_.close(websocket::close_code::normal); }

void WebSocketClient::ReadLoopFunc() {
    try {
        while (true) {
            beast::flat_buffer buffer;
            ws_.read(buffer);
            std::string message = beast::buffers_to_string(buffer.data());
            LOG(INFO) << message;
            CHECK(ws_.got_text());
            json::object obj = json::parse(message).as_object();
            if (obj["status"] != "ok") {
                break;
            }
            if (obj["type"] == "final_result") {
                result_ = obj["result"].as_string().c_str();
            }
            if (obj["type"] == "partial_result") {
                partial_result_ = obj["result"].as_string().c_str();
            }
            if (obj["type"] == "speech_end") {
                done_ = true;
                break;
            }
        }
    } catch (beast::system_error const& se) {
        // This indicates that the session was closed
        if (se.code() != websocket::error::closed) {
            LOG(ERROR) << se.code().message();
        }
    } catch (std::exception const& e) {
        LOG(ERROR) << e.what();
    }
}

void WebSocketClient::Join() { t_->join(); }

void WebSocketClient::SendStartSignal() {
    json::value start_tag = {{"signal", "start"}};
    std::string start_message = json::serialize(start_tag);
    this->SendTextData(start_message);
}

void WebSocketClient::SendDataEnd() {
    json::value end_tag = {{"data", "end"}};
    std::string end_message = json::serialize(end_tag);
    this->SendTextData(end_message);
}

void WebSocketClient::SendEndSignal() {
    json::value end_tag = {{"signal", "end"}};
    std::string end_message = json::serialize(end_tag);
    this->SendTextData(end_message);
}

}  // namespace ppspeech
