/**
 * 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.blur.store.blockcache_v2;

import java.io.IOException;
import java.util.Collection;
import java.util.Set;

import org.apache.blur.store.blockcache.LastModified;
import org.apache.blur.store.hdfs.DirectoryDecorator;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.IOContext;
import org.apache.lucene.store.IndexInput;
import org.apache.lucene.store.IndexOutput;
import org.apache.lucene.store.Lock;
import org.apache.lucene.store.LockFactory;

public class CacheDirectory extends Directory implements DirectoryDecorator, LastModified {

  private final Directory _internal;
  private final String _directoryName;
  private final Cache _cache;
  private final String _shard;
  private final String _table;
  private final Set<String> _tableBlockCacheFileTypes;

  public CacheDirectory(String table, String shard, Directory directory, Cache cache,
      Set<String> tableBlockCacheFileTypes) {
    if (!(directory instanceof LastModified)) {
      throw new RuntimeException("Directory [" + directory + "] does not implement '" + LastModified.class.toString()
          + "'");
    }
    _table = table;
    _shard = shard;
    _directoryName = notNull(table + "_" + shard);
    _internal = notNull(directory);
    _cache = notNull(cache);
    _tableBlockCacheFileTypes = tableBlockCacheFileTypes;
  }

  public String getShard() {
    return _shard;
  }

  public String getTable() {
    return _table;
  }

  public IndexInput openInput(String name, IOContext context) throws IOException {
    IndexInput indexInput = _internal.openInput(name, context);
    if (_cache.cacheFileForReading(this, name, context) || (_tableBlockCacheFileTypes != null && isCachableFile(name))) {
      return new CacheIndexInput(this, name, indexInput, _cache);
    }
    return indexInput;
  }

  private boolean isCachableFile(String name) {
    if (_tableBlockCacheFileTypes == null) {
      return true;
    } else if (_tableBlockCacheFileTypes.isEmpty()) {
      return false;
    }
    for (String ext : _tableBlockCacheFileTypes) {
      if (name.endsWith(ext)) {
        return true;
      }
    }
    return false;
  }

  public IndexOutput createOutput(String name, IOContext context) throws IOException {
    if (_cache.cacheFileForWriting(this, name, context) || (_tableBlockCacheFileTypes != null && isCachableFile(name))) {
      return new CacheIndexOutput(this, name, _cache, _internal, context);
    }
    return _internal.createOutput(name, context);
  }

  public void deleteFile(String name) throws IOException {
    _cache.removeFile(this, name);
    _internal.deleteFile(name);
  }

  public String[] listAll() throws IOException {
    return _internal.listAll();
  }

  public boolean fileExists(String name) throws IOException {
    return _internal.fileExists(name);
  }

  public long fileLength(String name) throws IOException {
    return _internal.fileLength(name);
  }

  public void sync(Collection<String> names) throws IOException {
    _internal.sync(names);
  }

  public Lock makeLock(String name) {
    return _internal.makeLock(name);
  }

  public void clearLock(String name) throws IOException {
    _internal.clearLock(name);
  }

  public void close() throws IOException {
    _cache.releaseDirectory(this);
    _internal.close();
  }

  public void setLockFactory(LockFactory lockFactory) throws IOException {
    _internal.setLockFactory(lockFactory);
  }

  public LockFactory getLockFactory() {
    return _internal.getLockFactory();
  }

  public String getLockID() {
    return _internal.getLockID();
  }

  public String toString() {
    return _internal.toString();
  }

  public void copy(Directory to, String src, String dest, IOContext context) throws IOException {
    _internal.copy(to, src, dest, context);
  }

  public IndexInputSlicer createSlicer(String name, IOContext context) throws IOException {
    return _internal.createSlicer(name, context);
  }

  public String getDirectoryName() {
    return _directoryName;
  }

  @Override
  public long getFileModified(String name) throws IOException {
    return ((LastModified) _internal).getFileModified(name);
  }

  @Override
  public Directory getOriginalDirectory() {
    return _internal;
  }

  private static <T> T notNull(T t) {
    if (t == null) {
      throw new IllegalArgumentException("Cannot be null");
    }
    return t;
  }

}