package utils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ReflectionExample {
	
	private Map<String, BonusColumn<? extends Object>> bonusColumns = new HashMap<>();

	public static void main(String[] args) throws IOException {
		ReflectionExample foo = new ReflectionExample();
		foo.start();
		

	}
	
	public void start() throws IOException {
		
		// add bonus column, store type info:
		addBonusColumn("A", Integer.class);
		addBonusColumn("B", String.class);
		addBonusColumn("C", Date.class);
		
		 BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		System.out.println("Please, select column A/B/C");
		while(true) {
			 String choice = br.readLine();
			 if (bonusColumns.containsKey(choice)) {
				 BonusColumn<? extends Object> column = bonusColumns.get(choice);
				 Class<? extends Object> typeInfo = column.typeInfo;
				 // now what?
				 // now what?
				 
				 // 1.)) old good case expression:
				 switch(typeInfo.getCanonicalName()) {
				 case "java.lang.Integer" :
					 @SuppressWarnings("unchecked")
					BonusColumn<Integer> typeCasted = (BonusColumn<Integer>) column; // now we can use type cast
					 typeCasted.addValue(34);
					 Integer i = typeCasted.getHeadValue();
					 System.out.println("Pain in the butt: " + i);
					 break;
				 case "java.lang.String" :
					 break;
				 case "java.util.Date" :
					 break;
				 }
				 // 2.)) virtual method (dispatches dynamically), not really helpful here
				 // 3.)) there's no third choice
			 } else {
				 System.out.println("no such column " + choice);
			 }
		}
	}
	
	public <T> void addBonusColumn(String name,Class<T> clazz){ 
        BonusColumn<T> bonusColumnWithType
               = new BonusColumn<>(clazz); // it doesn't matter if it were new BonusColumn<Object>, it is the same as new BonusColumn<Int> !!!!!
        // it matters only compile-time!!!
       this.bonusColumns.put(name,bonusColumnWithType);
   }
	
   
	
	

}

// now remember that value for T is lost during erasure)
class BonusColumn<T> {
	public final Class<T> typeInfo;
	private List<T> values = new ArrayList<>(); // or collection of values, doesn't matter
	
	public BonusColumn(Class<T> typeInfo) {
		this.typeInfo = typeInfo;
	}
	
	//public static <R> >
	
	public void addValue(T value) {
		values.add(value);
	}
	
	public T getHeadValue() {
		return values.get(0);
	}
}