package com.bernard.greposimu.source;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import com.bernard.greposimu.GrepoSimu;
import com.bernard.greposimu.model.game.Building;
import com.bernard.greposimu.model.game.GameConfig;
import com.bernard.greposimu.model.game.God;
import com.bernard.greposimu.model.game.Resources;
import com.bernard.greposimu.model.game.UnitResources;
import com.bernard.greposimu.model.game.powers.Power;
import com.bernard.greposimu.model.game.queues.BuildingQueueItem;
import com.bernard.greposimu.model.game.queues.RecruitmentQueueItem;
import com.bernard.greposimu.model.game.queues.RecruitmentQueueItem.RecruitmentKind;
import com.bernard.greposimu.model.game.queues.ResearchQueueItem;
import com.bernard.greposimu.model.game.researches.Research;
import com.bernard.greposimu.model.game.units.Unit;
import com.bernard.greposimu.model.runtime.Timestamp;
import com.bernard.greposimu.model.simulator.Joueureuse;
import com.bernard.greposimu.model.simulator.SimulatorData;
import com.bernard.greposimu.model.simulator.Troupes;
import com.bernard.greposimu.model.simulator.Ville;
import com.bernard.greposimu.model.simulator.Ville.TradeOrder;
import com.fasterxml.jackson.core.exc.StreamReadException;
import com.fasterxml.jackson.databind.DatabindException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class JSONSourcer {

	public static final void readSource(File file) {
		ObjectMapper om = new ObjectMapper();
		try {
			SourcedData sd = om.readValue(file, SourcedData.class);
			System.out.println(sd);
		} catch (StreamReadException e) {
			e.printStackTrace();
		} catch (DatabindException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static final Troupes getTroupes(GameConfig gc, Map<String,Integer> count) {
		Map<Unit,Integer> troup = new HashMap<>();
		for(String u : count.keySet())troup.put(gc.getUnit(u), count.get(u));
		return new Troupes(troup);
	}
	public static final Resources getResources(SourcedRessources res) {
		return new Resources(res.wood, res.stone, res.iron);
	}
	public static final UnitResources getUnitResources(God god, SourcedFavRessources res) {
		return new UnitResources(res.wood, res.stone, res.iron,god,res.favor);
	}
	
	public static final SimulatorData makeSimulationData(File file, GameConfig gc) {
		ObjectMapper om = new ObjectMapper();
		try {
			SourcedData sd = om.readValue(file, SourcedData.class);
			
			Map<String,Ville> villes = new HashMap<String, Ville>();
			for(String idS : sd.towns.keySet()) {
				Integer id = Integer.parseInt(idS);
				SourcedTown st = sd.getTowns().get(idS);
				
				Map<Building,Integer> buildings = new HashMap<>();
				for(String bid : st.buildings.keySet())buildings.put(gc.getBuilding(bid), st.buildings.get(bid));
				Set<Research> researches = new HashSet<>();
				for(String rid : st.researches.keySet())if(st.researches.get(rid))researches.add(gc.getResearch(rid));
				Map<Integer,Troupes> soutiens = new HashMap<Integer, Troupes>();
				Map<Integer,Troupes> soutenus = new HashMap<Integer, Troupes>();
				for(SourcedUnits uts : sd.units) {
					if(uts.orig == id && uts.curr != id)
						soutiens.put(uts.curr, getTroupes(gc, uts.getUnits()));
					if(uts.curr == id && uts.orig != id)
						soutenus.put(uts.orig, getTroupes(gc, uts.getUnits()));
				}
				
				List<BuildingQueueItem> buildingQueue = new ArrayList<>();
				for(SourcedBuildingOrder sbo : st.buildingOrders)
					buildingQueue.add(new BuildingQueueItem(
						sbo.getBuildingTime(),
						gc.getBuilding(sbo.building),
						sbo.isTearingDown(),
						new Timestamp(sbo.getBeginTime()),
						new Timestamp(sbo.getEndTime()),
						getResources(sbo.getRefund()),
						getResources(sbo.getCost())
						));
				List<RecruitmentQueueItem> terrestrialQueue = new ArrayList<>();
				List<RecruitmentQueueItem> navalQueue = new ArrayList<>();
				for(SourcedRecruitmentOrder sro : st.recruitingOrders) {
					Unit u = gc.getUnit(sro.unit);
					RecruitmentQueueItem rqi = new RecruitmentQueueItem(
							sro.getKind().equals("ground")?RecruitmentKind.GROUND:RecruitmentKind.NAVAL,
							u,
							sro.getCount(),
							sro.getDone(),
							new Timestamp(sro.getBeginTime()),
							new Timestamp(sro.getEndTime()),
							getUnitResources(u.isMythological()?u.getGod():null, sro.getRefund()),
							getUnitResources(null, sro.getCost())
						);
					if(sro.kind.equals("ground")) 
						terrestrialQueue.add(rqi);
					else if(sro.kind.equals("naval"))
						navalQueue.add(rqi);
				}
				List<ResearchQueueItem> researchQueue = new ArrayList<>();
				for(SourcedResearchOrder sro : st.researchOrders)
					researchQueue.add(new ResearchQueueItem(
						gc.getResearch(sro.getResearch()),
						new Timestamp(sro.getBeginTime()),
						new Timestamp(sro.getEndTime()),
						getResources(sro.getRefund())
						));
				Map<Power,Timestamp> sortileges = new HashMap<>();
				for(SourcedCastedPower sp : st.castedPowers) {
					//TODO
				}
				Set<TradeOrder> incomingTrade = new HashSet<>();
				Set<TradeOrder> outgoingTrade = new HashSet<>();
				for(SourcedTrades str : sd.trades) {
					//TODO support farmtowns
					if(str.destType.equals("town_trade") && str.origType.equals("town_trade")){
						if(Integer.parseInt(str.dest) == id)
							incomingTrade.add(new TradeOrder(new Resources(str.getWood(), str.getStone(), str.getIron()), Integer.parseInt(str.orig)));
						if(Integer.parseInt(str.orig) == id)
							incomingTrade.add(new TradeOrder(new Resources(str.getWood(), str.getStone(), str.getIron()), Integer.parseInt(str.dest)));
					}
				}
				
				Set<SourcedCelebration> townCele = sd.celebrations.stream().filter(c -> c.getTown() == id).collect(Collectors.toSet());
				villes.put(st.name, new Ville(
					id,
					st.name, 
					buildings,
					getTroupes(gc, st.getUnitsTotal()),
					soutiens,
					soutenus,
					sortileges,
					
					townCele.stream().filter(c -> c.type.equals("party")).map(SourcedCelebration::getEnd).map(Timestamp::new).findAny().orElse(null),
					townCele.stream().filter(c -> c.type.equals("games")).map(SourcedCelebration::getEnd).map(Timestamp::new).findAny().orElse(null),
					townCele.stream().filter(c -> c.type.equals("triumph")).map(SourcedCelebration::getEnd).map(Timestamp::new).findAny().orElse(null),
					townCele.stream().filter(c -> c.type.equals("theater")).map(SourcedCelebration::getEnd).map(Timestamp::new).findAny().orElse(null),
					
					researches,
					researchQueue,
					buildingQueue,
					st.getMilitia()==null?null:new Timestamp(st.getMilitia().getEnd()),
					new Resources(st.getResources().wood, st.getResources().stone, st.getResources().iron),
					terrestrialQueue,
					gc.getGod(st.god),
					navalQueue,
					st.espstorage,
					null,//ordresMilitaires,
					incomingTrade,
					outgoingTrade));
						
			}
			Joueureuse joueureuse = new Joueureuse(null, 0, 0, null, null, null, null, null, null, null, null, null);
			
			return new SimulatorData(villes, joueureuse);
		} catch (StreamReadException e) {
			e.printStackTrace();
		} catch (DatabindException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;	
	}
	
}
