# PriceRangeIterator.java

    package com.university.bookstore.iterator;

    import java.util.ArrayList;
    import java.util.List;
    import java.util.NoSuchElementException;

    import com.university.bookstore.model.Material;

    /**
     * Iterator that filters materials by price range.
     * Only returns materials within the specified price range.
     *
     * @author Navid Mohaghegh
     * @version 3.0
     * @since 2024-09-15
     */
    public class PriceRangeIterator implements MaterialIterator {

        private final List<Material> materials;
        private final double minPrice;
        private final double maxPrice;
        private int currentIndex;
        private final int totalCount;

        /**
         * Creates a new price range iterator.
         *
         * @param materials the list of materials to iterate over
         * @param minPrice the minimum price (inclusive)
         * @param maxPrice the maximum price (inclusive)
         * @throws IllegalArgumentException if price range is invalid
         */
        public PriceRangeIterator(List<Material> materials, double minPrice, double maxPrice) {
            if (minPrice < 0 || maxPrice < 0 || minPrice > maxPrice) {
                throw new IllegalArgumentException("Invalid price range: " + minPrice + " to " + maxPrice);
            }

            this.materials = new ArrayList<>(materials);
            this.minPrice = minPrice;
            this.maxPrice = maxPrice;
            this.currentIndex = 0;
            this.totalCount = (int) materials.stream()
                .filter(m -> m.getPrice() >= minPrice && m.getPrice() <= maxPrice)
                .count();
            advanceToNext();
        }

        @Override
        public boolean hasNext() {
            return currentIndex < materials.size();
        }

        @Override
        public Material next() {
            if (!hasNext()) {
                throw new NoSuchElementException("No more elements");
            }

            Material current = materials.get(currentIndex);
            currentIndex++;
            advanceToNext();
            return current;
        }

        @Override
        public void reset() {
            currentIndex = 0;
            advanceToNext();
        }

        @Override
        public int getCurrentPosition() {
            return currentIndex;
        }

        @Override
        public int getTotalCount() {
            return totalCount;
        }

        @Override
        public int getRemainingCount() {
            return totalCount - getCurrentPosition();
        }

        @Override
        public boolean isAtBeginning() {
            return currentIndex == 0;
        }

        @Override
        public boolean isAtEnd() {
            return currentIndex >= materials.size();
        }

        /**
         * Gets the minimum price.
         *
         * @return the minimum price
         */
        public double getMinPrice() {
            return minPrice;
        }

        /**
         * Gets the maximum price.
         *
         * @return the maximum price
         */
        public double getMaxPrice() {
            return maxPrice;
        }

        /**
         * Gets the price range.
         *
         * @return the price range
         */
        public double getPriceRange() {
            return maxPrice - minPrice;
        }

        /**
         * Advances to the next material within the price range.
         */
        private void advanceToNext() {
            while (currentIndex < materials.size()) {
                Material material = materials.get(currentIndex);
                if (material.getPrice() >= minPrice && material.getPrice() <= maxPrice) {
                    break;
                }
                currentIndex++;
            }
        }

        @Override
        public String toString() {
            return String.format("PriceRangeIterator[Range=$%.2f-$%.2f, Position=%d/%d]",
                minPrice, maxPrice, getCurrentPosition(), getTotalCount());
        }
    }