/*
 * 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.hadoop.ozone.client;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * ObjectStore implementation with in-memory state.
 */
public class ObjectStoreStub extends ObjectStore {

  public ObjectStoreStub() {
    super();
  }

  private Map<String, OzoneVolumeStub> volumes = new HashMap<>();
  private Map<String, String> bucketVolumeMap = new HashMap<>();
  private Map<String, Boolean> bucketEmptyStatus = new HashMap<>();
  private Map<String, List<OzoneBucket>> userBuckets = new HashMap<>();

  @Override
  public void createVolume(String volumeName) throws IOException {
    createVolume(volumeName,
        VolumeArgs.newBuilder()
            .setAdmin("root")
            .setOwner("root")
            .setQuota("" + Integer.MAX_VALUE)
            .setAcls(new ArrayList<>()).build());
  }

  @Override
  public void createVolume(String volumeName, VolumeArgs volumeArgs)
      throws IOException {
    OzoneVolumeStub volume =
        new OzoneVolumeStub(volumeName,
            volumeArgs.getAdmin(),
            volumeArgs.getOwner(),
            Long.parseLong(volumeArgs.getQuota()),
            System.currentTimeMillis(),
            volumeArgs.getAcls());
    volumes.put(volumeName, volume);
  }

  @Override
  public OzoneVolume getVolume(String volumeName) throws IOException {
    if (volumes.containsKey(volumeName)) {
      return volumes.get(volumeName);
    } else {
      throw new IOException("VOLUME_NOT_FOUND");
    }
  }

  @Override
  public Iterator<? extends OzoneVolume> listVolumes(String volumePrefix)
      throws IOException {
    return volumes.values()
        .stream()
        .filter(volume -> volume.getName().startsWith(volumePrefix))
        .collect(Collectors.toList())
        .iterator();

  }

  @Override
  public Iterator<? extends OzoneVolume> listVolumes(String volumePrefix,
      String prevVolume) throws IOException {
    return volumes.values()
        .stream()
        .filter(volume -> volume.getName().compareTo(prevVolume) > 0)
        .filter(volume -> volume.getName().startsWith(volumePrefix))
        .collect(Collectors.toList())
        .iterator();
  }

  @Override
  public Iterator<? extends OzoneVolume> listVolumesByUser(String user,
      String volumePrefix, String prevVolume) throws IOException {
    return volumes.values()
        .stream()
        .filter(volume -> volume.getOwner().equals(user))
        .filter(volume -> volume.getName().compareTo(prevVolume) < 0)
        .filter(volume -> volume.getName().startsWith(volumePrefix))
        .collect(Collectors.toList())
        .iterator();
  }

  @Override
  public void deleteVolume(String volumeName) throws IOException {
    volumes.remove(volumeName);
  }

  @Override
  public void createS3Bucket(String userName, String s3BucketName) throws
      IOException {
    String volumeName = "s3" + userName;
    if (bucketVolumeMap.get(s3BucketName) == null) {
      bucketVolumeMap.put(s3BucketName, volumeName + "/" + s3BucketName);
      bucketEmptyStatus.put(s3BucketName, true);
      createVolume(volumeName);
      volumes.get(volumeName).createBucket(s3BucketName);
    } else {
      throw new IOException("BUCKET_ALREADY_EXISTS");
    }

    if (userBuckets.get(userName) == null) {
      List<OzoneBucket> ozoneBuckets = new ArrayList<>();
      ozoneBuckets.add(volumes.get(volumeName).getBucket(s3BucketName));
      userBuckets.put(userName, ozoneBuckets);
    } else {
      userBuckets.get(userName).add(volumes.get(volumeName).getBucket(
          s3BucketName));
    }
  }

  public Iterator<? extends OzoneBucket> listS3Buckets(String userName,
                                                       String bucketPrefix) {
    if (userBuckets.get(userName) == null) {
      return new ArrayList<OzoneBucket>().iterator();
    } else {
      return userBuckets.get(userName).parallelStream()
          .filter(ozoneBucket -> {
            if (bucketPrefix != null) {
              return ozoneBucket.getName().startsWith(bucketPrefix);
            } else {
              return true;
            }
          }).collect(Collectors.toList())
          .iterator();
    }
  }

  public Iterator<? extends OzoneBucket> listS3Buckets(String userName,
                                                       String bucketPrefix,
                                                       String prevBucket) {

    if (userBuckets.get(userName) == null) {
      return new ArrayList<OzoneBucket>().iterator();
    } else {
      //Sort buckets lexicographically
      userBuckets.get(userName).sort(
          (bucket1, bucket2) -> {
            int compare = bucket1.getName().compareTo(bucket2.getName());
            if (compare < 0) {
              return -1;
            } else if (compare == 0) {
              return 0;
            } else {
              return 1;
            }
          });
      return userBuckets.get(userName).stream()
          .filter(ozoneBucket -> {
            if (prevBucket != null) {
              return ozoneBucket.getName().compareTo(prevBucket) > 0;
            } else {
              return true;
            }
          })
          .filter(ozoneBucket -> {
            if (bucketPrefix != null) {
              return ozoneBucket.getName().startsWith(bucketPrefix);
            } else {
              return true;
            }
          }).collect(Collectors.toList())
          .iterator();
    }
  }

  @Override
  public void deleteS3Bucket(String s3BucketName) throws
      IOException {
    if (bucketVolumeMap.containsKey(s3BucketName)) {
      if (bucketEmptyStatus.get(s3BucketName)) {
        bucketVolumeMap.remove(s3BucketName);
      } else {
        throw new IOException("BUCKET_NOT_EMPTY");
      }
    } else {
      throw new IOException("BUCKET_NOT_FOUND");
    }
  }

  @Override
  public String getOzoneBucketMapping(String s3BucketName) throws IOException {
    if (bucketVolumeMap.get(s3BucketName) == null) {
      throw new IOException("S3_BUCKET_NOT_FOUND");
    }
    return bucketVolumeMap.get(s3BucketName);
  }

  @Override
  @SuppressWarnings("StringSplitter")
  public String getOzoneVolumeName(String s3BucketName) throws IOException {
    if (bucketVolumeMap.get(s3BucketName) == null) {
      throw new IOException("S3_BUCKET_NOT_FOUND");
    }
    return bucketVolumeMap.get(s3BucketName).split("/")[0];
  }

  @Override
  @SuppressWarnings("StringSplitter")
  public String getOzoneBucketName(String s3BucketName) throws IOException {
    if (bucketVolumeMap.get(s3BucketName) == null) {
      throw new IOException("S3_BUCKET_NOT_FOUND");
    }
    return bucketVolumeMap.get(s3BucketName).split("/")[1];
  }

  public void setBucketEmptyStatus(String bucketName, boolean status) {
    bucketEmptyStatus.put(bucketName, status);
  }
}
