/*
 * This File is one of the examples from Java Object Oriented Programming
 * Do not reproduce this code for others or use it in a commercial setting without prior permission from the author.
 */
package salesApplication;

import datasource.DataSource;
import datasource.FileDataSource;
import java.util.Comparator;
import java.util.DoubleSummaryStatistics;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import products.*;
import static salesApplication.CompanyInfo.clearanceSale;

/**
 *
 * @author Matthew Gregory
 * @website www.matthewgregory-author.com.au
 *
 */
public class TestPriceList {

    public static void main(String[] args) {
        DataSource ds = new FileDataSource();
        PriceList demo = ds.retrieveList("Demo");
        List<Item> demoList = demo.getList();

        //Sorting
        //Print out the list
//        demoList.stream().forEach(p -> System.out.println(p));
        //Sorted
//        demoList.stream().sorted().forEach(p -> System.out.println(p));
        //Notice error.  
//        demoList.stream().sorted().forEach(p -> System.out.println(p));
        //or specify a Comparator as a parameter.
//        demoList.stream().sorted(new CompareByPrice()).forEach(p -> System.out.println(p));
        //Using Lambda to supply the comparator
//        demoList.stream()
//                .sorted((p1,p2)-> Double.compare(p1.getPrice(),p2.getPrice()))
//                .forEach(p -> System.out.println(p));
        //Examples Using the Comparator's comparing method.
//        demoList.stream()
//                .sorted(Comparator.comparing(Item::getName))
//                .forEach(p -> System.out.println(p));
//        demoList.stream()
//                .sorted(Comparator.comparingDouble(Item::getPrice))
//                .forEach(p -> System.out.println(p));
        //Sorting in reverse order (based on natural order)
//        demoList.stream()
//                .sorted(Comparator.reverseOrder())
//                .forEach(p -> System.out.println(p));        
        //Applied to previous example
//        demoList.stream()
//            .sorted(Comparator.comparingDouble(Item::getPrice).reversed())
//            .forEach(p -> System.out.println(p));
        //Sorting by more than one column
        //Sorting by price alone
//        demoList.stream()
//                .map((p)->{if(p.getName().equals("Basic Pencil Case")){p.setPrice(1.50);} return p;})
//                .sorted(Comparator.comparing(Item::getPrice))
//                .forEach(p -> System.out.println(p));
        //Sort by price and then by name
//        demoList.stream()
//                .map((p)->{if(p.getName().equals("Basic Pencil Case")){p.setPrice(1.50);} return p;})
//                .sorted(Comparator.comparing(Item::getPrice).thenComparing(Item::getName))
//                .forEach(p -> System.out.println(p));
        
        //Collecting - Already covered
//        List<Item> clearanceNotepadsList = demoList.stream()
//                                                   .filter(p->(p instanceof Notepad))
//                                                   .map(clearanceSale)
//                                                   .collect(Collectors.toList());
//        PriceList clearanceNotepads = new PriceList(clearanceNotepadsList);
//        clearanceNotepads.printList();

        //Test Combine lIsts method
//        PriceList newProductsList = new PriceList();
//        newProductsList.addProduct(new Ruler("New Ruler"
//                ,"A Brand new product about to be realeased."
//                ,1.00/*Cost*/,"30cm" /*length*/));
//        System.out.println("List of new Products.");
//        newProductsList.printList();
//        System.out.println("List of all products (with new ones added).");
//        demo.combine(newProductsList);
//        demo.printList();

        //Final code for custom collection      
//        PriceList clearanceNotepads = demoList.stream()
//                                              .filter(p->(p instanceof Notepad))
//                                              .map(clearanceSale)
//                                              .collect(
//                                                  //Supplier to create the collection
//                                                  ()->new PriceList()
//                                                  //BiConsumer to add elements on by one
//                                                  //to our collection
//                                                  ,(list,element)->list.addProduct(element)
//                                                  //BiConsumer to copy the final collection
//                                                  ,(list1,list2)->list1.combine(list2)
//                                              );
//        clearanceNotepads.printList();
        //Or more simply...
//        PriceList clearanceNotepads = 
//                demoList.stream().filter(p->(p instanceof Notepad))
//                                 .map(clearanceSale)
//                                 .collect(PriceList::new,PriceList::addProduct,PriceList::combine);
//        clearanceNotepads.printList();
        
        //Joiner
//        System.out.println(demoList.stream().map(Item::getName));
        //Collect?
//        System.out.println(demoList.stream().map(Item::getName).collect(Collectors.toList()));
        //But I want a CSV list with quotes around the contents
//        System.out.println(
//                demoList.stream().map(Item::getName)
//                        .collect(Collectors.joining("', '")));
        //But what about the missing ' at the begining and the end?
//        System.out.println(
//                demoList.stream().map(Item::getName)
//                        .collect(Collectors.joining("', '"//separate each element
//                                                    ,"'"//attach to start
//                                                    ,"'"//attach to end
//                                                    )));

        //Sumarising data 
//        System.out.println("Number of products: "+demoList.stream().mapToDouble(Item::getPrice).count());
//        System.out.println("Max Price: "+demoList.stream().mapToDouble(Item::getPrice).max().getAsDouble());
//        System.out.println("Min Price: "+demoList.stream().mapToDouble(Item::getPrice).min().getAsDouble());
//        System.out.println("Average Price: "+demoList.stream().mapToDouble(Item::getPrice).average().getAsDouble());
//        //The following doesn't make sense in our situation
//        //but it is included for the sake of compeleteness
//        System.out.println("Total Price: "+demoList.stream().mapToDouble(Item::getPrice).sum());
//
//        //Using the collect() method
//        DoubleSummaryStatistics stats = 
//                demoList.stream().collect(Collectors.summarizingDouble(Item::getPrice));
//        System.out.println("Number of products: "+stats.getCount());
//        System.out.println("Max Price: "+stats.getMax());
//        System.out.println("Min Price: "+stats.getMin());
//        System.out.println("Average Price: "+stats.getAverage());
//        System.out.println("Total Prices: "+stats.getSum());

        //Using the summaryStatistics() method
//        DoubleSummaryStatistics stats = 
//                demoList.stream().mapToDouble(Item::getPrice)
//                        .summaryStatistics();
//        //This give us a collection of useful statistics so we don't have to fetch them individually
//        System.out.println("Number of products: "+stats.getCount());
//        System.out.println("Max Price: "+stats.getMax());
//        System.out.println("Min Price: "+stats.getMin());
//        System.out.println("Average Price: "+stats.getAverage());
//        System.out.println("Total Prices: "+stats.getSum());

        //Partitioning
//        Map<Boolean, List<Item>> isExpensive = 
//            demoList.stream().collect(Collectors.partitioningBy(i -> i.getPrice()>=5));
//        System.out.println("Expensive Products: (Price >= $5)");
//        System.out.println(isExpensive.get(true));
//        System.out.println();
//        System.out.println("Inexpensive Products: (Price < $5)");
//        System.out.println(isExpensive.get(false));
        
    //Grouping
        Map<Character,List<String>> groupByFirstLetter = 
                demoList.stream().map(Item::getName).collect(Collectors.groupingBy(e -> new Character(e.charAt(0))));
//        System.out.println("Products by first letter: ");
//        System.out.println(groupByFirstLetter);
        //More user friendly way...
//        System.out.println("Products by first letter: ");
//        groupByFirstLetter.forEach(//c is the first character, /l is the list of product names for that character
//                                      (c,l)->{System.out.println(c);
//                                           l.forEach(n->System.out.println(n));
//                                           }
//                                  );
        }
}
