import java.util.*;
import java.util.stream.Collectors;

public class Ch5_5 {
    
    public void all() {
        ex1();
        ex2();
        ex3();
        ex4();
        ex5();
        ex6();
        ex7();
        ex8();
    }

    private void ex8() {
        //Find the transaction with the smallest value
        List<Transaction> transactions = createTransactions();
        //My try:
        Optional<Transaction> tr1 = transactions.stream()
                .sorted(Comparator.comparing(Transaction::getValue))
                .findFirst();
        System.out.println("The transaction with the smallest value is "  + tr1.get());

        //Their first, bad try:
        Optional<Transaction> tr2 = transactions.stream()
                .reduce((t1,t2)->t1.getValue()<t2.getValue()?t1:t2);
        System.out.println("The transaction with the smallest value is "  + tr2.get());

        //Their second, good try:
        Optional<Transaction> tr3 = transactions.stream()
                .min(Comparator.comparing(Transaction::getValue));

        System.out.println("The transaction with the smallest value is "  + tr3.get());
    }


    private void ex7() {
        //What's the highest value of all the transactions?
        List<Transaction> transactions = createTransactions();

        Optional<Integer> maxValue = transactions.stream()
                .map(Transaction::getValue)
                .reduce(Integer::max);

        System.out.println("Max value is " + maxValue.get());

        OptionalInt maxValue2 = transactions.stream()
                .mapToInt(Transaction::getValue)
                .max();
        System.out.println("Max value using mapToInt: " + maxValue2.getAsInt());
    }

    private void ex6() {
        //Print all transactions values from the traders living in Cambridge
        List<Transaction> transactions = createTransactions();

        transactions.stream()
                .filter(t->t.getTrader().getCity().equals("Cambridge"))
                .map(Transaction::getValue)
                .forEach(System.out::println);

    }

    private void ex5() {
        //Are there any traders in Milan?
        List<Transaction> transactions = createTransactions();

        boolean b = transactions.stream()
                .map(t->t.getTrader().getCity())
                .anyMatch(s->s.equals("Milan"));

        System.out.println("Are there any traders in Milan? " + b);

        //Or better:
        boolean milanBased = transactions.stream()
                .anyMatch(t->t.getTrader().getCity().equals("Milan"));

        System.out.println("Milan based traders? " + milanBased);
    }

    private void ex4() {
        //Return a string of all traders' names sorted alphabetically
        List<Transaction> transactions = createTransactions();

        String names = transactions.stream()
                .map(Transaction::getTrader)
                .map(Trader::getName)
                .distinct()
                .sorted()
                .reduce("",(n1,n2)-> n1+ " " + n2);
        System.out.println("Names are " + names);

        //Two better things:
        // One map instead of two.
        // Use joining instead of creating new Strings over and over:

        String names2 = transactions.stream()
                .map(t->t.getTrader().getName())
                .distinct()
                .sorted()
                .collect(Collectors.joining());
        System.out.println("New try: " + names2);
    }

    private void ex3() {
        //Find all traders from Cambridge and sort them by name
        List<Transaction> transactions = createTransactions();

        transactions.stream()
                .map(Transaction::getTrader)
                .filter(t->t.getCity().equals("Cambridge"))
                .distinct()
                .sorted(Comparator.comparing(Trader::getName))
                .forEach(System.out::println);
    }

    private void ex2() {
        //What are the unique cities where the traders work?
        List<Transaction> transactions = createTransactions();

        transactions.stream()
                .map(t->t.getTrader().getCity())
                .distinct()
                .forEach(System.out::println);
    }

    private void ex1() {
        //Find all transactions in year 2011 and sort them by value (small to high)
        List<Transaction> transactions = createTransactions();

        transactions.stream()
                .filter(t->t.getYear()==2011)
                .sorted(Comparator.comparing(Transaction::getValue))
                .forEach(System.out::println);
    }



    List<Transaction> createTransactions() {
        Trader raol = new Trader("Raol", "Cambridge");
        Trader mario = new Trader("Mario", "Milan");
        Trader alan = new Trader("Alan", "Cambridge");
        Trader brian = new Trader("Brian", "Cambridge");

        return Arrays.asList(
                new Transaction(brian, 2011, 300),
                new Transaction(raol, 2012, 1000),
                new Transaction(raol, 2011, 400),
                new Transaction(mario, 2012, 710),
                new Transaction(mario, 2012, 700),
                new Transaction(alan, 2012, 950)
        );
    }

    public class Trader {
        private final String name;
        private final String city;

        public Trader(String n, String c ) {
            this.name = n;
            this.city = c;
        }

        public String getName() {
            return name;
        }

        public String getCity() {
            return city;
        }

        public String toString() {
            return "Trader:" + this.name + " in " + this.city;
        }
    }

    public class Transaction {
        private final Trader trader;
        private final int year;
        private final int value;

        public Transaction(Trader trader, int year, int value){
            this.trader = trader;
            this.year = year;
            this.value = value;
        }


        public Trader getTrader() {
            return trader;
        }

        public int getYear() {
            return year;
        }

        public int getValue() {
            return value;
        }

        public String toString() {
            return "{" + this.trader + "," +
                    "year: " + this.year + "," +
                    "value: " + this.value + "}";
        }
    }


}
