/*    Copyright 2012 10gen Inc.
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *    As a special exception, the copyright holders give permission to link the
 *    code of portions of this program with the OpenSSL library under certain
 *    conditions as described in each individual source file and distribute
 *    linked combinations including the program with the OpenSSL library. You
 *    must comply with the GNU Affero General Public License in all respects
 *    for all of the code used other than as permitted herein. If you modify
 *    file(s) with this exception, you may extend this exception to your
 *    version of the file(s), but you are not obligated to do so. If you do not
 *    wish to do so, delete this exception statement from your version. If you
 *    delete this exception statement from all source files in the program,
 *    then also delete it in the license file.
 */

#include "mongo/dbtests/mock/mock_dbclient_connection.h"

#include "mongo/dbtests/mock/mock_dbclient_cursor.h"
#include "mongo/util/net/sock.h"
#include "mongo/util/time_support.h"

using mongo::BSONObj;

using std::string;
using std::vector;

namespace mongo {
    MockDBClientConnection::MockDBClientConnection(MockRemoteDBServer* remoteServer,
            bool autoReconnect):
            _remoteServerInstanceID(remoteServer->getInstanceID()),
            _remoteServer(remoteServer),
            _isFailed(false),
            _sockCreationTime(mongo::curTimeMicros64()),
            _autoReconnect(autoReconnect) {
    }

    MockDBClientConnection::~MockDBClientConnection() {
    }

    bool MockDBClientConnection::connect(const char* hostName, std::string& errmsg) {
        if (_remoteServer->isRunning()) {
            _remoteServerInstanceID = _remoteServer->getInstanceID();
            return true;
        }

        errmsg.assign("cannot connect to " + _remoteServer->getServerAddress());
        return false;
    }

    bool MockDBClientConnection::runCommand(const string& dbname, const BSONObj& cmdObj,
            BSONObj &info, int options) {
        checkConnection();

        try {
            return _remoteServer->runCommand(_remoteServerInstanceID, dbname, cmdObj,
                    info, options);
        }
        catch (const mongo::SocketException&) {
            _isFailed = true;
            throw;
        }

        return false;
    }

    std::auto_ptr<mongo::DBClientCursor> MockDBClientConnection::query(const string& ns,
            mongo::Query query,
            int nToReturn,
            int nToSkip,
            const BSONObj* fieldsToReturn,
            int queryOptions,
            int batchSize) {
        checkConnection();

        try {
            mongo::BSONArray result(_remoteServer->query(_remoteServerInstanceID, ns, query,
                    nToReturn, nToSkip, fieldsToReturn, queryOptions, batchSize));

            std::auto_ptr<mongo::DBClientCursor> cursor;
            cursor.reset(new MockDBClientCursor(this, result));
            return cursor;
        }
        catch (const mongo::SocketException&) {
            _isFailed = true;
            throw;
        }

        std::auto_ptr<mongo::DBClientCursor> nullPtr;
        return nullPtr;
    }

    mongo::ConnectionString::ConnectionType MockDBClientConnection::type() const {
        return mongo::ConnectionString::CUSTOM;
    }

    bool MockDBClientConnection::isFailed() const {
        return _isFailed;
    }

    string MockDBClientConnection::getServerAddress() const {
        return _remoteServer->getServerAddress();
    }

    string MockDBClientConnection::toString() const {
        return _remoteServer->toString();
    }

    unsigned long long MockDBClientConnection::query(stdx::function<void(const BSONObj&)> f,
                    const string& ns,
                    mongo::Query query,
                    const BSONObj* fieldsToReturn,
                    int queryOptions) {
        verify(false);
        return 0;
    }

    unsigned long long MockDBClientConnection::query(stdx::function<void(
            mongo::DBClientCursorBatchIterator&)> f,
            const std::string& ns,
            mongo::Query query,
            const mongo::BSONObj* fieldsToReturn,
            int queryOptions) {
        verify(false);
        return 0;
    }

    uint64_t MockDBClientConnection::getSockCreationMicroSec() const {
        return _sockCreationTime;
    }

    void MockDBClientConnection::insert(const string &ns, BSONObj obj, int flags) {
        _remoteServer->insert(ns, obj, flags);
    }

    void MockDBClientConnection::insert(const string &ns,
            const vector<BSONObj>& objList,
            int flags) {
        for (vector<BSONObj>::const_iterator iter = objList.begin();
                iter != objList.end(); ++iter) {
            insert(ns, *iter, flags);
        }
    }

    void MockDBClientConnection::remove(const string& ns, Query query, bool justOne) {
        remove(ns, query, (justOne ? RemoveOption_JustOne : 0));
    }

    void MockDBClientConnection::remove(const string& ns, Query query, int flags) {
        _remoteServer->remove(ns, query, flags);
    }

    void MockDBClientConnection::killCursor(long long cursorID) {
        verify(false); // unimplemented
    }

    bool MockDBClientConnection::callRead(mongo::Message& toSend , mongo::Message& response) {
        verify(false); // unimplemented
        return false;
    }

    bool MockDBClientConnection::call(mongo::Message& toSend,
            mongo::Message& response,
            bool assertOk,
            string* actualServer)  {
        verify(false); // unimplemented
        return false;
    }

    void MockDBClientConnection::say(mongo::Message& toSend, bool isRetry, string* actualServer) {
        verify(false); // unimplemented
    }

    void MockDBClientConnection::sayPiggyBack(mongo::Message& toSend) {
        verify(false); // unimplemented
    }

    bool MockDBClientConnection::lazySupported() const {
        verify(false); // unimplemented
        return false;
    }

    double MockDBClientConnection::getSoTimeout() const {
        return 0;
    }

    void MockDBClientConnection::checkConnection() {
        if (_isFailed && _autoReconnect) {
            _remoteServerInstanceID = _remoteServer->getInstanceID();
        }
    }
}
