/*
 * 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.
 */
package org.apache.geode.cache.query.partitioned;

import static org.apache.geode.cache.query.Utils.createPortfoliosAndPositions;
import static org.junit.Assert.*;

import java.io.File;

import org.junit.Ignore;
import org.junit.Test;
import org.junit.experimental.categories.Category;

import org.apache.geode.cache.CacheException;
import org.apache.geode.cache.query.data.Portfolio;
import org.apache.geode.cache.query.internal.index.IndexManager.TestHook;
import org.apache.geode.cache.query.internal.index.IndexUtils;
import org.apache.geode.cache30.CacheSerializableRunnable;
import org.apache.geode.internal.cache.GemFireCacheImpl;
import org.apache.geode.internal.cache.PartitionedRegionDUnitTestCase;
import org.apache.geode.test.dunit.AsyncInvocation;
import org.apache.geode.test.dunit.Host;
import org.apache.geode.test.dunit.ThreadUtils;
import org.apache.geode.test.dunit.VM;
import org.apache.geode.test.dunit.Wait;
import org.apache.geode.test.dunit.cache.internal.JUnit4CacheTestCase;
import org.apache.geode.test.dunit.internal.JUnit4DistributedTestCase;
import org.apache.geode.test.junit.categories.DistributedTest;

/**
 *
 */
@Category(DistributedTest.class)
public class PRBasicIndexCreationDeadlockDUnitTest extends PartitionedRegionDUnitTestCase

{
  /**
   * constructor
   *
   * @param name
   */

  public PRBasicIndexCreationDeadlockDUnitTest() {
    super();
  }

  public void setCacheInVMs(VM... vms) {
    for (VM vm : vms) {
      vm.invoke(() -> PRQueryDUnitHelper.setCache(getCache()));
    }
  }

  public void setCacheInVMsUsingXML(String xmlFile, VM... vms) {
    for (VM vm : vms) {
      vm.invoke(() -> GemFireCacheImpl.testCacheXml = PRQHelp.findFile(xmlFile));
      vm.invoke(() -> PRQueryDUnitHelper.setCache(getCache()));
    }
  }

  PRQueryDUnitHelper PRQHelp = new PRQueryDUnitHelper();

  final String name = "PartionedPortfolios";

  final String localName = "LocalPortfolios";

  final int cnt = 0, cntDest = 1003;

  final int redundancy = 0;

  public static volatile boolean hook_vm1, hook_vm2;

  @Ignore
  @Test
  public void testIndexCreationMessageDiskRecoveryDeadLock() {
    Host host = Host.getHost(0);
    VM vm0 = host.getVM(0);
    VM vm1 = host.getVM(1);
    setCacheInVMs(vm0, vm1);
    final String fileName1 = "PRPersistentIndexCreation_1.xml";
    final String fileName2 = "PRPersistentIndexCreation_2.xml";
    setCacheInVMsUsingXML(fileName1, vm0);
    setCacheInVMsUsingXML(fileName1, vm1);
    final File dir1 = new File("overflowData1");
    final File dir2 = new File("overflowData2");

    AsyncInvocation[] asyns = new AsyncInvocation[2];

    try {
      vm0.invoke(new CacheSerializableRunnable("Create disk store directories") {


        @Override
        public void run2() throws CacheException {
          boolean success = (dir1).mkdir();
          success = (dir2).mkdir();
        }
      });

      vm0.invoke(PRQHelp.getCacheSerializableRunnableForPRCreate(name));
      vm1.invoke(PRQHelp.getCacheSerializableRunnableForPRCreate(name));

      final Portfolio[] portfoliosAndPositions = createPortfoliosAndPositions(100);

      // Putting the data into the PR's created
      vm0.invoke(PRQHelp.getCacheSerializableRunnableForPRPutsKeyValue(name, portfoliosAndPositions,
          0, 100));

      vm0.invoke(new CacheSerializableRunnable("Close VM0 cache") {

        @Override
        public void run2() throws CacheException {
          GemFireCacheImpl.getInstance().close();
        }
      });

      vm1.invoke(new CacheSerializableRunnable("Close VM1 cache") {

        @Override
        public void run2() throws CacheException {
          GemFireCacheImpl.getInstance().close();
        }
      });

      // Restart the caches with testHook.
      asyns[0] = vm0.invokeAsync(new CacheSerializableRunnable("Restart VM0 cache") {

        @Override
        public void run2() throws CacheException {
          GemFireCacheImpl.testCacheXml = PRQHelp.findFile(fileName1);
          IndexUtils.testHook = new IndexUtilTestHook();
          getCache();
        }
      });

      // asyns[1] =
      vm0.invoke(new CacheSerializableRunnable("Checking hook in VM0 cache") {

        @Override
        public void run2() throws CacheException {
          IndexUtilTestHook hook = (IndexUtilTestHook) IndexUtils.testHook;
          while (hook == null) {
            hook = (IndexUtilTestHook) IndexUtils.testHook;
            Wait.pause(20);
          }
          while (!hook.isHooked()) {
            Wait.pause(30);
          }
          // hook.setHooked(false);
          hook_vm1 = true;
          /*
           * while (!hook_vm2) { pause(40); } hook.setHooked(false);
           */
        }
      });

      asyns[1] = vm1.invokeAsync(new CacheSerializableRunnable("Restart VM1 cache") {

        @Override
        public void run2() throws CacheException {
          GemFireCacheImpl.testCacheXml = PRQHelp.findFile(fileName2);
          getCache();
        }
      });

      Wait.pause(2000);

      vm0.invoke(new CacheSerializableRunnable("Checking hook in VM0 cache again") {

        @Override
        public void run2() throws CacheException {
          IndexUtilTestHook hook = (IndexUtilTestHook) IndexUtils.testHook;
          while (hook == null) {
            hook = (IndexUtilTestHook) IndexUtils.testHook;
            Wait.pause(20);
          }
          while (!hook.isHooked()) {
            Wait.pause(30);
          }
          hook.setHooked(false);
          hook_vm1 = false;
        }
      });

      for (AsyncInvocation async : asyns) {
        ThreadUtils.join(async, 10000);
      }
    } finally {

      vm0.invoke(new CacheSerializableRunnable("Close VM0 cache") {

        @Override
        public void run2() throws CacheException {
          dir1.delete();
          dir2.delete();
          IndexUtilTestHook hook = (IndexUtilTestHook) IndexUtils.testHook;
          if (hook != null) {
            hook.setHooked(true);
            IndexUtils.testHook = null;
          }
        }
      });
    }
  }

  public class IndexUtilTestHook implements TestHook {

    private volatile boolean hooked = false;

    public void setHooked(boolean hooked) {
      this.hooked = hooked;
    }

    public boolean isHooked() {
      return hooked;
    }

    @Override
    public synchronized void hook(int spot) throws RuntimeException {
      GemFireCacheImpl.getInstance().getLogger().fine("IndexUtilTestHook is set");
      switch (spot) {
        case 0:
          hooked = true;
          while (hooked) {
            Wait.pause(300);
          }
          break;

        default:
          break;
      }
    }
  }
}
