#include "tinypb_dispatcher.hpp"
#include "logger.hpp"
#include "tinypb_data.hpp"
#include "error_code.hpp"
#include <cstddef>
#include <memory>
#include <sstream>

namespace tinyrpc {
    TinypbDispatcher::TinypbDispatcher() {}
        
    TinypbDispatcher::~TinypbDispatcher() {

    }
    void TinypbDispatcher::dispatcher(TcpConnection& conn, AbstractData& data, AbstractData& resp) {
        logger() << "dispatcher";
        TinypbData& pbdata = dynamic_cast<TinypbData&>(data);
        TinypbData& respond = dynamic_cast<TinypbData&>(resp);
        std::string service_name;
        std::string method_name;

        respond.service_full_name = pbdata.service_full_name;
        respond.msg_req = pbdata.msg_req;
        bool ret = parseServiceFullName(pbdata.service_full_name, service_name, method_name);
        if(ret == false) {
            respond.err_code = ERROR_PARSE_SERVICE_NAME;
            std::stringstream ss;
            ss << "not found service_name:[" << service_name << "]"; 
            respond.err_info = ss.str();
            return;
        }

        auto it = m_service_map.find(service_name);

        if (it == m_service_map.end() || !(it->second)) {
            respond.err_code = ERROR_SERVICE_NOT_FOUND;
            std::stringstream ss;
            ss << "not found service_name:[" << service_name << "]"; 
            respond.err_info = ss.str();
            return;

        }

        Service* service = it->second;

        const Method* method = service->GetDescriptor()->FindMethodByName(method_name);
        // const Method* method = nullptr;
        if(method == nullptr) {
            respond.err_code = ERROR_METHOD_NOT_FOUND;
            std::stringstream ss;
            ss << "not found service_name:[" << service_name << "]"; 
            respond.err_info = ss.str();
            return;
        }
        std::unique_ptr<Message> requestMsg (service->GetRequestPrototype(method).New());

        ret = requestMsg->ParseFromString(pbdata.pb_data);

        if(ret == false) {
            respond.err_code = ERROR_FAILED_SERIALIZE;
            std::stringstream ss;
            ss << "faild to parse request data, request.name:[" << requestMsg->GetDescriptor()->full_name() << "]";
            respond.err_info = ss.str();
            return;
        }

        std::unique_ptr<Message> respondMsg (service->GetRequestPrototype(method).New());

        auto callback = [&respond, &respondMsg] {
            if(!respondMsg->SerializePartialToString(&respond.pb_data)) {
                respond.pb_data = "";
                logger() << respond.msg_req << "|reply error! encode reply package error";
                respond.err_code = ERROR_FAILED_SERIALIZE;
                respond.err_info = "failed to serilize relpy data";
            } else {
                logger() <<  respond.msg_req << "|Set server response data:" << respondMsg->ShortDebugString();
            }
        };

        service->CallMethod(method, nullptr, requestMsg.get(), respondMsg.get(), nullptr /* callback */);

    }

    bool TinypbDispatcher::parseServiceFullName(const std::string& name, std::string& serviceName, std::string& methodName)  {
        if(name.empty()) return false;

        auto pos = name.find(".");
        if(pos == std::string::npos) return false;
        serviceName = name.substr(0, pos);
        methodName = name.substr(pos + 1);

        logger() << "serviceName=" << serviceName;
        logger() << "methodName=" << methodName;
        return true;
    }

}

