
#include "LibLsp/lsp/general/exit.h"
#include "LibLsp/lsp/textDocument/declaration_definition.h"
#include "LibLsp/lsp/textDocument/signature_help.h"
#include "LibLsp/lsp/general/initialize.h"
#include "LibLsp/lsp/ProtocolJsonHandler.h"
#include "LibLsp/lsp/textDocument/typeHierarchy.h"
#include "LibLsp/lsp/AbsolutePath.h"
#include "LibLsp/lsp/textDocument/resolveCompletionItem.h"
#include <network/uri.hpp>


#include "LibLsp/JsonRpc/Endpoint.h"
#include "LibLsp/JsonRpc/stream.h"
#include "LibLsp/JsonRpc/TcpServer.h"
#include "LibLsp/lsp/textDocument/document_symbol.h"
#include "LibLsp/lsp/workspace/execute_command.h"

#include <boost/filesystem.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <thread>

using namespace boost::asio::ip;
using namespace std;
class DummyLog :public lsp::Log
{
public:

        void log(Level level, std::wstring&& msg)
        {
                std::wcout << msg << std::endl;
        };
        void log(Level level, const std::wstring& msg)
        {
                std::wcout << msg << std::endl;
        };
        void log(Level level, std::string&& msg)
        {
                std::cout << msg << std::endl;
        };
        void log(Level level, const std::string& msg)
        {
                std::cout << msg << std::endl;
        };
};

std::string _address = "127.0.0.1";
std::string _port = "9333";

class Server
{
public:


        Server():server(_address,_port,protocol_json_handler, endpoint, _log)
        {
                server.point.registerHandler(
                        [&](const td_initialize::request& req)
                          ->lsp::ResponseOrError< td_initialize::response >{

                                td_initialize::response rsp;
                                CodeLensOptions code_lens_options;
                                code_lens_options.resolveProvider = true;
                                rsp.result.capabilities.codeLensProvider = code_lens_options;

                                return rsp;
                        });
                server.point.registerHandler([&](const td_definition::request& req
                        ,const CancelMonitor& monitor)  -> lsp::ResponseOrError<td_definition::response>
                        {

                                std::this_thread::sleep_for(std::chrono::seconds(8));

                            if( monitor && monitor() )
                            {
                                        _log.info("textDocument/definition request had been cancel.");
                    Rsp_Error rsp;
                    rsp.error.code = lsErrorCodes::RequestCancelled;
                    rsp.error.message = "textDocument/definition request had been cancel.";
                    return  rsp;
                            }
                else
                {
                    td_definition::response rsp;
                    rsp.result.first= std::vector<lsLocation>();
                    return rsp;
                }

                        });

                server.point.registerHandler([=](Notify_Exit::notify& notify)
                        {
                                std::cout << notify.ToJson() << std::endl;
                        });
                std::thread([&]()
                        {
                                server.run();
                        }).detach();
        }
        ~Server()
        {
                server.stop();
        }
        std::shared_ptr < lsp::ProtocolJsonHandler >  protocol_json_handler = std::make_shared < lsp::ProtocolJsonHandler >();
        DummyLog _log;

        std::shared_ptr < GenericEndpoint >  endpoint = std::make_shared<GenericEndpoint>(_log);
        lsp::TcpServer server;

};

class Client
{
public:
        struct iostream :public lsp::base_iostream<tcp::iostream>
        {
                explicit iostream(boost::asio::basic_socket_iostream<tcp>& _t)
                        : base_iostream<boost::asio::basic_socket_iostream<tcp>>(_t)
                {
                }

                std::string what() override
                {
                        auto  msg = _impl.error().message();
                        return  msg;
                }

        };
        Client() :remote_end_point_(protocol_json_handler, endpoint, _log)
        {
                tcp::endpoint end_point( address::from_string(_address), 9333);

                socket_ = std::make_shared<tcp::iostream>();
                socket_->connect(end_point);
                if (!socket_)
                {
                        string temp = "Unable to connect: " + socket_->error().message();
                        std::cout << temp << std::endl;
                }

                vector<string> args;
                socket_proxy = std::make_shared<iostream>(*socket_.get());

                remote_end_point_.startProcessingMessages(socket_proxy, socket_proxy);
        }
        ~Client()
        {
        remote_end_point_.stop();
                std::this_thread::sleep_for(std::chrono::milliseconds (1000));
                socket_->close();
        }

        std::shared_ptr < lsp::ProtocolJsonHandler >  protocol_json_handler = std::make_shared< lsp::ProtocolJsonHandler>();
        DummyLog _log;

        std::shared_ptr<GenericEndpoint>  endpoint = std::make_shared<GenericEndpoint>(_log);

        std::shared_ptr < iostream> socket_proxy;
        std::shared_ptr<tcp::iostream>  socket_;
        RemoteEndPoint remote_end_point_;
};

int main()
{

        Server server;
        Client client;

        {
                td_initialize::request req;
                auto rsp = client.remote_end_point_.waitResponse(req);
                if (rsp)
                {
                        std::cout << rsp->response.ToJson() << std::endl;
                }
                else
                {
                        std::cout << "get initialze  response time out" << std::endl;
                }
        }
        {
                td_definition::request req;
                auto future_rsp = client.remote_end_point_.send(req);
        client.remote_end_point_.cancelRequest(req.id);

                auto state = future_rsp.wait_for(std::chrono::seconds(16));
                if (lsp::future_status::timeout == state)
                {
                        std::cout << "get textDocument/definition  response time out" << std::endl;
                        return 0;
                }
                auto rsp = future_rsp.get();
                if (rsp.is_error)
                {
                        std::cout << "get textDocument/definition  response error :" << rsp.ToJson() << std::endl;

                }
                else
                {
                        std::cout << rsp.response.ToJson() << std::endl;
                }
        }
        Notify_Exit::notify notify;
        client.remote_end_point_.send(notify);
        return 0;
}


