/*
 * 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 "fw_dunit.hpp"
#include "ThinClientHelper.hpp"

using apache::geode::client::Exception;
using apache::geode::client::NoAvailableLocatorsException;
using apache::geode::client::NotConnectedException;

bool isLocalServer = false;
bool isLocator = false;

const std::string locHostPort1 =
    CacheHelper::getLocatorHostPort(isLocator, isLocalServer, 1);
const std::string locHostPort2 =
    CacheHelper::getLocatorHostPort(isLocator, isLocalServer, 2);
#define CLIENT1 s1p1
#define CLIENT2 s1p2
#define LOCATORS s2p1
#define SERVERS s2p2

DUNIT_TASK(CLIENT1, SetupClient1_NoLocators_At_Init)
  {
    initClient(true);
    createPooledRegion(regionNames[0], USE_ACK, locHostPort1, "Pool1");

    try {
      createEntry(regionNames[0], keys[0], vals[0]);
    } catch (NoAvailableLocatorsException &) {
      LOG("Caught expected exception.");
    } catch (...) {
      FAIL("NoAvailableLocatorsException was not thrown.");
    }
    LOG("SetupClient1 complete.");
  }
END_TASK(SetupClient1)

DUNIT_TASK(LOCATORS, CreateLocators)
  {
    if (isLocator) CacheHelper::initLocator(1);

    LOG("Locator1 started");

    if (isLocator) CacheHelper::initLocator(2);
  }
END_TASK(CreateLocators)

DUNIT_TASK(SERVERS, CreateServer1)
  {
    // starting servers
    if (isLocalServer) CacheHelper::initServer(1, nullptr, locHostPort1);
    LOG("Server 1 started");
  }
END_TASK(CreateServer1)

DUNIT_TASK(CLIENT2, SetupClient2)
  {
    // starting client 1
    initClient(true);
    createPooledRegion(regionNames[0], USE_ACK, locHostPort1, "Pool2");

    LOG("SetupClient2 complete.");
  }
END_TASK(SetupClient2)

DUNIT_TASK(CLIENT1, ConnectC1)
  {
    createEntry(regionNames[0], keys[0], vals[0]);
    LOG("ConnectC1 complete.");
  }
END_TASK(ConnectC1)

DUNIT_TASK(CLIENT2, ConnectC2)
  {
    doNetsearch(regionNames[0], keys[0], vals[0]);
    createEntry(regionNames[0], keys[1], vals[1]);
    LOG("ConnectC2 complete.");
  }
END_TASK(ConnectC2)

DUNIT_TASK(SERVERS, CreateServer2)
  {
    // starting servers
    if (isLocalServer) CacheHelper::initServer(2, nullptr, locHostPort2);
    LOG("Server 2 started");
  }
END_TASK(CreateServer2)

DUNIT_TASK(SERVERS, CloseServer1)
  {
    if (isLocalServer) {
      CacheHelper::closeServer(1);
      LOG("SERVER1 stopped");
    }
  }
END_TASK(CloseServer1)

DUNIT_TASK(CLIENT1, FailoverC1)
  {
    doNetsearch(regionNames[0], keys[1], vals[1]);
    updateEntry(regionNames[0], keys[0], nvals[0]);
    LOG("FailoverC1 complete.");
  }
END_TASK(FailoverC1)

DUNIT_TASK(CLIENT2, FailoverC2)
  {
    invalidateEntry(regionNames[0], keys[0]);
    doNetsearch(regionNames[0], keys[0], nvals[0], false);
    updateEntry(regionNames[0], keys[1], nvals[1]);
    LOG("FailoverC2 complete.");
  }
END_TASK(FailoverC2)

DUNIT_TASK(LOCATORS, CloseLocator1)
  {
    // stop locator
    if (isLocator) {
      CacheHelper::closeLocator(1);
      LOG("Locator1 stopped");
    }
  }
END_TASK(CloseLocator1)

DUNIT_TASK(SERVERS, ReStartS1)
  {
    // Re- starting servers
    if (isLocalServer) CacheHelper::initServer(1, nullptr, locHostPort1);
    LOG("Server 1 started");
  }
END_TASK(ReStartS1)

DUNIT_TASK(SERVERS, CloseServer2)
  {
    if (isLocalServer) {
      CacheHelper::closeServer(2);
      LOG("SERVER2 stopped");
    }
  }
END_TASK(CloseServer2)

DUNIT_TASK(CLIENT1, AgainFailoverC1)
  {
    try {
      invalidateEntry(regionNames[0], keys[1]);
      doNetsearch(regionNames[0], keys[1], nvals[1], false);
      updateEntry(regionNames[0], keys[0], vals[0]);
      FAIL("Client Failover Should Fail");
    } catch (const NotConnectedException &) {
      LOG("Expected exception NotConnectedException "
          "got");
    } catch (const Exception &excp) {
      LOG(excp.getName());
      LOG(excp.what());
      FAIL(
          "Unexpected expection - only "
          "NotConnectedException expected");
    } catch (...) {
      FAIL(
          "Unknown expection - only "
          "NotConnectedException expected");
    }
    LOG("AgainFailoverC1 complete.");
  }
END_TASK(AgainFailoverC1)

DUNIT_TASK(LOCATORS, CloseLocator2)
  {
    // stop locator
    if (isLocator) {
      CacheHelper::closeLocator(2);
      LOG("Locator2 stopped");
    }
  }
END_TASK(CloseLocator2)

DUNIT_TASK(CLIENT2, AgainFailoverC2)
  {
    try {
      invalidateEntry(regionNames[0], keys[0]);
      doNetsearch(regionNames[0], keys[0], vals[0], false);
      updateEntry(regionNames[0], keys[1], vals[1]);
      FAIL("Client Failover Should Fail");
    } catch (const NotConnectedException &ex) {
      LOG(ex.getName());
      LOG(ex.what());
      FAIL(
          "Unexpected expection - only "
          "NoAvailableLocatorsException "
          "expected");
    } catch (const Exception &excp) {
      LOG(excp.getName());
      LOG(excp.what());
      FAIL(
          "Unexpected expection - only "
          "NoAvailableLocatorsException "
          "expected");
    } catch (...) {
      FAIL(
          "Unexpected expection - only "
          "NotConnectedException with cause "
          "NoAvailableLocatorsException "
          "expected");
    }
    LOG("AgainFailoverC2 complete.");
  }
END_TASK(AgainFailoverC2)
DUNIT_TASK(CLIENT1, CloseCache1)
  { cleanProc(); }
END_TASK(CloseCache1)

DUNIT_TASK(CLIENT2, CloseCache2)
  { cleanProc(); }
END_TASK(CloseCache2)

DUNIT_TASK(SERVERS, AgainCloseServer1)
  {
    // stop servers
    if (isLocalServer) {
      CacheHelper::closeServer(1);
      LOG("SERVER1 stopped");
    }
  }
END_TASK(AgainCloseServer1)
