/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 "qpid/sys/Poller.h"
#include "qpid/sys/Mutex.h"
#include "qpid/sys/Dispatcher.h"

#include "qpid/sys/windows/AsynchIoResult.h"
#include "qpid/sys/windows/IoHandlePrivate.h"
#include "qpid/sys/windows/check.h"

#include <winsock2.h>
#include <windows.h>

#include <assert.h>
#include <vector>
#include <exception>

namespace qpid {
namespace sys {

class PollerHandlePrivate {
    friend class Poller;
    friend class PollerHandle;

    SOCKET fd;
    windows::AsynchIoResult::Completer cb;
    AsynchIO::RequestCallback cbRequest;

    PollerHandlePrivate(SOCKET f,
                        windows::AsynchIoResult::Completer cb0 = 0,
                        AsynchIO::RequestCallback rcb = 0)
      : fd(f), cb(cb0), cbRequest(rcb)
    {
    }
    
};

PollerHandle::PollerHandle(const IOHandle& h) :
  impl(new PollerHandlePrivate(toSocketHandle(static_cast<const Socket&>(h)), h.impl->event, h.impl->cbRequest))
{}

PollerHandle::~PollerHandle() {
    delete impl;
}

/**
 * Concrete implementation of Poller to use the Windows I/O Completion
 * port (IOCP) facility.
 */
class PollerPrivate {
    friend class Poller;
    
    const HANDLE iocp;

    // The number of threads running the event loop.
    volatile LONG threadsRunning;

    // Shutdown request is handled by setting isShutdown and injecting a
    // well-formed completion event into the iocp.
    bool isShutdown;

    PollerPrivate() :
        iocp(::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0)),
        threadsRunning(0),
        isShutdown(false) {
        QPID_WINDOWS_CHECK_NULL(iocp);
    }

    ~PollerPrivate() {
        // It's probably okay to ignore any errors here as there can't be
        // data loss
        ::CloseHandle(iocp);
    }
};

void Poller::shutdown() {
    // Allow sloppy code to shut us down more than once.
    if (impl->isShutdown)
        return;
    ULONG_PTR key = 1;    // Tell wait() it's a shutdown, not I/O
    PostQueuedCompletionStatus(impl->iocp, 0, key, 0);
}

bool Poller::hasShutdown()
{
    return impl->isShutdown;
}

bool Poller::interrupt(PollerHandle&) {
    return false;  // There's no concept of a registered handle.
}

void Poller::run() {
    do {
        Poller::Event event = this->wait();

        // Handle shutdown
        switch (event.type) {
        case Poller::SHUTDOWN:
            return;
            break;
        case Poller::INVALID:  // On any type of success or fail completion
            break;
        default:
          // This should be impossible
          assert(false);
        }
    } while (true);
}

void Poller::monitorHandle(PollerHandle& handle, Direction dir) {
    HANDLE h = (HANDLE)(handle.impl->fd);
    if (h != INVALID_HANDLE_VALUE) {
        HANDLE iocpHandle = ::CreateIoCompletionPort (h, impl->iocp, 0, 0);
        QPID_WINDOWS_CHECK_NULL(iocpHandle);
    }
    else {
        // INPUT is used to request a callback; OUTPUT to request a write
        assert(dir == Poller::INPUT || dir == Poller::OUTPUT);

        if (dir == Poller::OUTPUT) {
            windows::AsynchWriteWanted *result =
                new windows::AsynchWriteWanted(handle.impl->cb);
            PostQueuedCompletionStatus(impl->iocp, 0, 0, result->overlapped());
        }
        else {
            windows::AsynchCallbackRequest *result =
                new windows::AsynchCallbackRequest(handle.impl->cb,
                                                   handle.impl->cbRequest);
            PostQueuedCompletionStatus(impl->iocp, 0, 0, result->overlapped());
        }
    }
}

// All no-ops...
void Poller::unmonitorHandle(PollerHandle& /*handle*/, Direction /*dir*/) {}
void Poller::registerHandle(PollerHandle& /*handle*/) {}
void Poller::unregisterHandle(PollerHandle& /*handle*/) {}

Poller::Event Poller::wait(Duration timeout) {
    DWORD timeoutMs = 0;
    DWORD numTransferred = 0;
    ULONG_PTR completionKey = 0;
    OVERLAPPED *overlapped = 0;
    windows::AsynchResult *result = 0;

    // Wait for either an I/O operation to finish (thus signaling the
    // IOCP handle) or a shutdown request to be made (thus signaling the
    // shutdown event).
    if (timeout == TIME_INFINITE)
        timeoutMs = INFINITE;
    else
        timeoutMs = static_cast<DWORD>(timeout / TIME_MSEC);

    InterlockedIncrement(&impl->threadsRunning);
    bool goodOp = ::GetQueuedCompletionStatus (impl->iocp,
                                               &numTransferred,
                                               &completionKey,
                                               &overlapped,
                                               timeoutMs);
    LONG remainingThreads = InterlockedDecrement(&impl->threadsRunning);
    if (goodOp) {
        // Dequeued a successful completion. If it's a posted packet from
        // shutdown() the overlapped ptr is 0 and key is 1. Else downcast
        // the OVERLAPPED pointer to an AsynchIoResult and call the
        // completion handler.
        if (overlapped == 0 && completionKey == 1) {
            // If there are other threads still running this wait, re-post
            // the completion.
            if (remainingThreads > 0)
                PostQueuedCompletionStatus(impl->iocp, 0, completionKey, 0);
            return Event(0, SHUTDOWN);
        }

        result = windows::AsynchResult::from_overlapped(overlapped);
        result->success (static_cast<size_t>(numTransferred));
    }
    else {
      if (overlapped != 0) {
        // Dequeued a completion for a failed operation. Downcast back
        // to the result object and inform it that the operation failed.
        DWORD status = ::GetLastError();
        result = windows::AsynchResult::from_overlapped(overlapped);
        result->failure (static_cast<int>(status));
      }
    }
    return Event(0, INVALID);   // TODO - this may need to be changed.

}

// Concrete constructors
Poller::Poller() :
    impl(new PollerPrivate())
{}

Poller::~Poller() {
    delete impl;
}

}}
