/////////////////////////////////////////////////////////////////////////////
// Name:        server.cpp
// Purpose:     IPC sample: server
// Author:      Julian Smart
// Modified by: Jurgen Doornik
// Created:     25/01/99
// Copyright:   (c) Julian Smart
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

// ============================================================================
// declarations
// ============================================================================

// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------

// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"


#ifndef WX_PRECOMP
    #include "wx/wx.h"
#endif

// Settings common to both executables: determines whether
// we're using TCP/IP or real DDE.
#include "ipcsetup.h"

#ifndef wxHAS_IMAGES_IN_RESOURCES
    #include "../sample.xpm"
#endif

#include "server.h"
#include "wx/textdlg.h"
#include "wx/datetime.h"

// ----------------------------------------------------------------------------
// wxWin macros
// ----------------------------------------------------------------------------

wxIMPLEMENT_APP(MyApp);

wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
    EVT_CLOSE( MyFrame::OnClose )

    EVT_BUTTON( ID_START,          MyFrame::OnStart )
    EVT_CHOICE( ID_SERVERNAME,     MyFrame::OnServerName )
    EVT_BUTTON( ID_DISCONNECT,     MyFrame::OnDisconnect )
    EVT_BUTTON( ID_ADVISE,         MyFrame::OnAdvise )
wxEND_EVENT_TABLE()


// ============================================================================
// implementation
// ============================================================================

// ----------------------------------------------------------------------------
// MyApp
// ----------------------------------------------------------------------------

bool MyApp::OnInit()
{
    if ( !wxApp::OnInit() )
        return false;

    // Create the main frame window
    m_frame = new MyFrame(nullptr, "Server");
    m_frame->Show(true);

    return true;
}

// ----------------------------------------------------------------------------
// MyFrame
// ----------------------------------------------------------------------------

// Define my frame constructor
MyFrame::MyFrame(wxFrame *frame, const wxString& title)
       : wxFrame(frame, wxID_ANY, title, wxDefaultPosition, wxSize(400, 300))
{
#if wxUSE_STATUSBAR
    CreateStatusBar();
#endif // wxUSE_STATUSBAR

    SetIcon(wxICON(sample));

    m_server = nullptr;

    wxPanel * const panel = new wxPanel(this);

    wxBoxSizer * const sizerMain = new wxBoxSizer( wxVERTICAL );

    wxFlexGridSizer * const sizerCmds = new wxFlexGridSizer( 2, 0, 0 );
    sizerCmds->AddGrowableCol( 1 );

    wxButton *btn;

    btn = new wxButton(panel, ID_START, "&Start Server");
    sizerCmds->Add(btn, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5);

    const wxString choices[] = { IPC_SERVICE, "..." };
    wxChoice * const choice = new wxChoice
                                  (
                                    panel,
                                    ID_SERVERNAME,
                                    wxDefaultPosition, wxSize(100, -1),
                                    WXSIZEOF(choices), choices
                                  );
    sizerCmds->Add(choice, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5);

    btn = new wxButton(panel, ID_DISCONNECT, "&Disconnect Client");
    sizerCmds->Add(btn, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5);
    sizerCmds->AddSpacer(20);

    btn = new wxButton( panel, ID_ADVISE, "&Advise");
    sizerCmds->Add(btn, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5);
    sizerCmds->AddSpacer(20);

    sizerMain->Add(sizerCmds, wxSizerFlags().Expand().Border(wxALL, 5));

    wxStaticBoxSizer * const
        sizerLog = new wxStaticBoxSizer(wxVERTICAL, panel, "Server &log");

    wxTextCtrl * const textLog = new wxTextCtrl
                                 (
                                    panel,
                                    wxID_ANY,
                                    "",
                                    wxDefaultPosition, wxSize(500, 140),
                                    wxTE_MULTILINE
                                 );
    sizerLog->Add(textLog, wxSizerFlags(1).Expand().Border(wxALL, 5));

    sizerMain->Add(sizerLog, wxSizerFlags(1).Expand().Border(wxALL, 5));

    panel->SetSizer(sizerMain);
    sizerMain->SetSizeHints(panel);
    SetClientSize(panel->GetSize());

    GetServername()->SetSelection(0);
    wxLogTextCtrl *logWindow = new wxLogTextCtrl(textLog);
    delete wxLog::SetActiveTarget(logWindow);
    wxLogMessage("Click on Start to start the server");
    UpdateUI();
}

void MyFrame::UpdateUI()
{
    GetStart()->Enable(m_server == nullptr);
    GetServername()->Enable(m_server == nullptr);
    GetAdvise()->Enable(m_server && m_server->CanAdvise());
    GetDisconnect()->Enable(m_server && m_server->IsConnected());
}

void MyFrame::OnClose(wxCloseEvent& event)
{
    wxDELETE(m_server);
    event.Skip();
}

void MyFrame::OnStart(wxCommandEvent& WXUNUSED(event))
{
    // Create a new server
    m_server = new MyServer;
    wxString servername = GetServername()->GetStringSelection();
    if (m_server->Create(servername))
    {
        wxLogMessage("Server %s started", servername);
  #if wxUSE_DDE_FOR_IPC
        wxLogMessage("Server uses DDE");
  #else // !wxUSE_DDE_FOR_IPC
        wxLogMessage("Server uses TCP");
  #endif // wxUSE_DDE_FOR_IPC/!wxUSE_DDE_FOR_IPC
    }
    else
    {
        wxLogMessage("Server %s failed to start", servername);
        wxDELETE(m_server);
    }
    UpdateUI();
}

void MyFrame::OnServerName( wxCommandEvent& WXUNUSED(event) )
{
    if ( GetServername()->GetStringSelection() == "..." )
    {
        wxString s = wxGetTextFromUser
                     (
                        "Specify the name of the server",
                        "Server Name",
                        "",
                        this
                     );

        if ( !s.empty() && s != IPC_SERVICE )
        {
            GetServername()->Insert(s, 0);
            GetServername()->SetSelection(0);
        }
    }
}

void MyFrame::Disconnect()
{
    m_server->Disconnect();
    UpdateUI();
}

void MyFrame::OnDisconnect(wxCommandEvent& WXUNUSED(event))
{
    Disconnect();
}

void MyFrame::OnAdvise(wxCommandEvent& WXUNUSED(event))
{
    m_server->Advise();
}

// ----------------------------------------------------------------------------
// MyServer
// ----------------------------------------------------------------------------

MyServer::MyServer() : wxServer()
{
    m_connection = nullptr;
}

MyServer::~MyServer()
{
    Disconnect();
}

wxConnectionBase *MyServer::OnAcceptConnection(const wxString& topic)
{
    wxLogMessage("OnAcceptConnection(\"%s\")", topic);

    if ( topic == IPC_TOPIC )
    {
        m_connection = new MyConnection();
        wxGetApp().GetFrame()->UpdateUI();
        wxLogMessage("Connection accepted");
        return m_connection;
    }
    //else: unknown topic

    wxLogMessage("Unknown topic, connection refused");
    return nullptr;
}

void MyServer::Disconnect()
{
    if ( m_connection )
    {
        wxDELETE(m_connection);
        wxGetApp().GetFrame()->UpdateUI();
        wxLogMessage("Disconnected client");
    }
}

void MyServer::Advise()
{
    if ( CanAdvise() )
    {
        const wxDateTime now = wxDateTime::Now();

        wxString str = wxString::FromUTF8("\xd0\x9f\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82");
        m_connection->Advise(m_connection->m_advise, str + " (using UTF-8)");

        str += " (using wchar_t)";
        m_connection->Advise(m_connection->m_advise,
                             str.wc_str(), (str.length() + 1)*sizeof(wchar_t),
                             wxIPC_UNICODETEXT);

        // This one uses wxIPC_TEXT by default.
        const wxString s = now.FormatTime() + " " + now.FormatDate();
        m_connection->Advise(m_connection->m_advise, s.mb_str(), wxNO_LEN);

        char bytes[3] = { '1', '2', '3' };
        m_connection->Advise(m_connection->m_advise, bytes, 3, wxIPC_PRIVATE);
    }
}

// ----------------------------------------------------------------------------
// MyConnection
// ----------------------------------------------------------------------------

bool
MyConnection::OnExecute(const wxString& topic,
                        const void *data,
                        size_t size,
                        wxIPCFormat format)
{
    Log("OnExecute", topic, "", data, size, format);
    return true;
}

bool
MyConnection::OnPoke(const wxString& topic,
                     const wxString& item,
                     const void *data,
                     size_t size,
                     wxIPCFormat format)
{
    Log("OnPoke", topic, item, data, size, format);
    return wxConnection::OnPoke(topic, item, data, size, format);
}

const void *
MyConnection::OnRequest(const wxString& topic,
                        const wxString& item,
                        size_t *size,
                        wxIPCFormat format)
{
    *size = 0;

    wxString s,
             afterDate;
    if ( item.StartsWith("Date", &afterDate) )
    {
        const wxDateTime now = wxDateTime::Now();

        if ( afterDate.empty() )
        {
            s = now.Format();
            *size = wxNO_LEN;
        }
        else if ( afterDate == "+len" )
        {
            s = now.FormatTime() + " " + now.FormatDate();
            *size = strlen(s.mb_str()) + 1;
        }
    }
    else if ( item == "bytes[3]" )
    {
        s = "123";
        *size = 3;
    }

    if ( !*size )
    {
        wxLogMessage("Unknown request for \"%s\"", item);
        return nullptr;
    }

    // store the data pointer to which we return in a member variable to ensure
    // that the pointer remains valid even after we return
    m_requestData = s.mb_str();
    const void * const data = m_requestData;
    Log("OnRequest", topic, item, data, *size, format);
    return data;
}

bool MyConnection::OnStartAdvise(const wxString& topic, const wxString& item)
{
    wxLogMessage("OnStartAdvise(\"%s\", \"%s\")", topic, item);
    wxLogMessage("Returning true");
    m_advise = item;
    wxGetApp().GetFrame()->UpdateUI();
    return true;
}

bool MyConnection::OnStopAdvise(const wxString& topic, const wxString& item)
{
    wxLogMessage("OnStopAdvise(\"%s\",\"%s\")", topic, item);
    wxLogMessage("Returning true");
    m_advise.clear();
    wxGetApp().GetFrame()->UpdateUI();
    return true;
}

bool
MyConnection::DoAdvise(const wxString& item,
                       const void *data,
                       size_t size,
                       wxIPCFormat format)
{
    Log("Advise", "", item, data, size, format);
    return wxConnection::DoAdvise(item, data, size, format);
}

bool MyConnection::OnDisconnect()
{
    wxLogMessage("OnDisconnect()");
    wxGetApp().GetFrame()->Disconnect();
    return true;
}
