/*
 * Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
 *
 * Licensed 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. See accompanying
 * LICENSE file.
 */
package sql.sqlutil;

import hydra.TestConfig;
import hydra.gemfirexd.FabricServerPrms;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import memscale.OffHeapHelper;
import util.TestException;

import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.internal.offheap.SimpleMemoryAllocatorImpl.Chunk;
import com.pivotal.gemfirexd.internal.engine.store.GemFireContainer;
import com.pivotal.gemfirexd.internal.engine.store.offheap.OffHeapRowWithLobs;

public class SqlOffHeapHelper {

  /** Return a List of off-heap memory Chunks where lob objects reside
   * 
   * @param offHeapValue The base row for the lobs (if any)
   * @param regionName The name of the region (table) storing the offHeapValue.
   * @param key The key for the offHeapValue.
   * @return A List of Chunks for the off-heap memory segments where the lobs reside.
   */
  public static List<Chunk> getLobChunks(Chunk aChunk, String regionName, Object key) {
    List<Chunk> aList = new ArrayList<Chunk>();
    //for gemfirexd tests, the value in off-heap memory (a row) can refer to other rows in off-heap memory
    // drill down to those other rows to consider those values in off-heap memory
    if (aChunk instanceof OffHeapRowWithLobs) {
      //Log.getLogWriter().info("xxx found OffHeapByteSource ");
      OffHeapRowWithLobs ohbs = (OffHeapRowWithLobs)aChunk;
      int numLobs = ohbs.readNumLobsColumns(false);
      //Log.getLogWriter().info("xxx numLobs is " + numLobs);
      if (numLobs <= 0) {
        throw new TestException("For key " + key + " + in region " + regionName + " the off-heap memory byte source " +
            ohbs + " has lobs " + true + ", but the number of lobs is " + numLobs);
      }
      for (int i = 1; i <= numLobs; i++) { // element 0 is the base row, so start with index 1
        Object byteSrc = ohbs.getGfxdByteSource(i);
        //Log.getLogWriter().info("xxx processing lob " + i);
        if (byteSrc instanceof Chunk) {
          //Log.getLogWriter().info("xxx lob is a chunk");
          Chunk lobChunk = (Chunk)byteSrc;
          aList.add(lobChunk);
        }
      }
    }
    return aList;
  }
  
  /** Return the off-heap memory size configured with FabricServerPrms.
   * 
   * @return The off-heap memory size or null if none.
   */
  public static String getOffHeapMemorySize() {
    String offHeapMemorySize = TestConfig.tab().stringAt(FabricServerPrms.offHeapMemorySize, null);
    return offHeapMemorySize;
  }
  
  /** Return whether the given Region is a global index table.
   * 
   * @param aRegion The region to test.
   * @return true if aRegion is a global index table, false otherwise.
   */
  private static boolean isGlobalIndexTable(Region aRegion) {
    Object attr = aRegion.getUserAttribute();
    if (attr != null) {
      if (attr instanceof GemFireContainer) {
        GemFireContainer container = (GemFireContainer)attr;
        return container.isGlobalIndex();
      }
    }
    return false;
  }
  
  /** Verify that all user tables (and excluding system tables) are enabled with
   *  off-heap memory.
   */
  public static void verifyUserTablesEnabledWithOffHeap() {
    Set<Region<?, ?>> allRegions = OffHeapHelper.getAllRegions();
    if (allRegions == null) {
      return;
    }
    List<String> userTablesList = new ArrayList<String>();
    for (Region aRegion: allRegions) {
      if (!aRegion.getAttributes().getDataPolicy().withStorage()) {
        continue;
      }
      if (aRegion.getAttributes().getDataPolicy().withPartitioning()) {
        if (aRegion.getAttributes().getPartitionAttributes().getLocalMaxMemory() == 0) {
          continue;
        }
      }
      String regionName = aRegion.getFullPath();
      boolean isSystemTable = regionName.startsWith("/SYS");
      boolean isSchema = (regionName.indexOf("/") == regionName.lastIndexOf("/"));
      boolean isGlobalIndexTable = isGlobalIndexTable(aRegion);
      boolean expectOffHeapEnabled = !isSystemTable & !isSchema & !isGlobalIndexTable;
      if (expectOffHeapEnabled) {
        userTablesList.add(regionName);
      }
    }
    OffHeapHelper.verifyRegionsEnabledWithOffHeap(userTablesList);
  }
  
}