package com.jiayue.insu.incloud.utils;

import lombok.extern.slf4j.Slf4j;

import java.util.Collection;
import java.util.LinkedList;
import java.util.stream.Collectors;

@Slf4j
public class FixedCapacityList<E> extends LinkedList<E> {

  int capacity;
  int index = 0;

  /**
   * Constructs a list containing the elements of the specified
   * collection, in the order they are returned by the collection's
   * iterator.
   *
   * @param c the collection whose elements are to be placed into this list
   * @throws NullPointerException if the specified collection is null
   */
  private FixedCapacityList(Collection<? extends E> c) {
    super(c);
  }

  public FixedCapacityList(int size) {
    super();
    this.capacity = size;
  }

  /**
   * Constructs an empty list.
   */
  private FixedCapacityList() {
    super();
  }

  /**
   * Inserts the specified element at the beginning of this list.
   *
   * @param e the element to add
   */
  @Override
  public void addFirst(E e) {
    if ((super.size() + 1) > capacity) {
      super.removeLast();
    }
    super.addFirst(e);
  }

  /**
   * Appends the specified element to the end of this list.
   *
   * <p>This method is equivalent to {@link #add}.
   *
   * @param e the element to add
   */
  @Override
  public void addLast(E e) {
    if ((super.size() + 1) > this.capacity) {
      super.removeFirst();
    }
    super.addLast(e);
  }

  /**
   * Appends the specified element to the end of this list.
   *
   * <p>This method is equivalent to {@link #addLast}.
   *
   * @param e element to be appended to this list
   * @return {@code true} (as specified by {@link Collection#add})
   */
  @Override
  public boolean add(E e) {
    if ((super.size() + 1) > this.capacity) {
      super.removeFirst();
    }
    return super.add(e);
  }

  /**
   * Appends all of the elements in the specified collection to the end of
   * this list, in the order that they are returned by the specified
   * collection's iterator.  The behavior of this operation is undefined if
   * the specified collection is modified while the operation is in
   * progress.  (Note that this will occur if the specified collection is
   * this list, and it's nonempty.)
   *
   * @param c collection containing elements to be added to this list
   * @return {@code true} if this list changed as a result of the call
   * @throws NullPointerException if the specified collection is null
   */
  @Override
  public boolean addAll(Collection<? extends E> c) {
    int length = this.capacity - super.size();
    return super.addAll(c.stream().limit(length).collect(Collectors.toList()));
  }

  /**
   * Inserts all of the elements in the specified collection into this
   * list, starting at the specified position.  Shifts the element
   * currently at that position (if any) and any subsequent elements to
   * the right (increases their indices).  The new elements will appear
   * in the list in the order that they are returned by the
   * specified collection's iterator.
   *
   * @param index index at which to insert the first element
   *              from the specified collection
   * @param c     collection containing elements to be added to this list
   * @return {@code true} if this list changed as a result of the call
   * @throws IndexOutOfBoundsException {@inheritDoc}
   * @throws NullPointerException      if the specified collection is null
   */
  @Override
  public boolean addAll(int index, Collection<? extends E> c) {
    if (index < (this.capacity - 1)) {
      int length = this.capacity - index;
      return super.addAll(index, c.stream().limit(length).collect(Collectors.toList()));
    }
    return false;
  }

  /**
   * Replaces the element at the specified position in this list with the
   * specified element.
   *
   * @param index   index of the element to replace
   * @param element element to be stored at the specified position
   * @return the element previously at the specified position
   * @throws IndexOutOfBoundsException {@inheritDoc}
   */
  @Override
  public E set(int index, E element) {
    return super.set(index, element);
  }

  /**
   * Inserts the specified element at the specified position in this list.
   * Shifts the element currently at that position (if any) and any
   * subsequent elements to the right (adds one to their indices).
   *
   * @param index   index at which the specified element is to be inserted
   * @param element element to be inserted
   * @throws IndexOutOfBoundsException {@inheritDoc}
   */
  @Override
  public void add(int index, E element) {
    if (index < (this.capacity - 1)) {
      super.remove(index);
      super.add(index, element);
    }
  }

  /**
   * Pushes an element onto the stack represented by this list.  In other
   * words, inserts the element at the front of this list.
   *
   * <p>This method is equivalent to {@link #addFirst}.
   *
   * @param e the element to push
   * @since 1.6
   */
  @Override
  public void push(E e) {
    if ((super.size() + 1) > this.capacity) {
      super.pop();
    }
    super.push(e);
  }
}
