package space.animefu.starfallhaven;

import org.junit.Test;
import java.io.*;
import java.util.*;
import static org.junit.Assert.*;
import static space.animefu.starfallhaven.ColonySystems.ResourceType;
import static space.animefu.starfallhaven.SocietySystems.*;

public class SocietySystemsTest {
    @Test public void caravanOffersAreSeededPricedAndInventoryBounded() {
        Faction faction=new Faction("aurora-guild","Aurora Guild",12);
        Caravan first=Caravan.create("caravan-7",faction,77L,4);
        Caravan again=Caravan.create("caravan-7",new Faction("aurora-guild","Aurora Guild",12),77L,4);
        assertEquals(first.offers,again.offers);
        assertFalse(first.offers.isEmpty());
        for(TradeOffer offer:first.offers){
            assertTrue(offer.quantity>0); assertTrue(offer.unitPrice>0);
            assertTrue(offer.quantity<=first.inventory.amount(offer.resource));
        }
        TradeOffer offer=first.offers.get(0); int stock=first.inventory.amount(offer.resource);
        Inventory haven=new Inventory(); haven.add(ResourceType.CRYSTAL,500);
        TradeReceipt receipt=first.buyFromCaravan(offer.id,2,haven,ResourceType.CRYSTAL);
        assertTrue(receipt.accepted); assertEquals(2,haven.amount(offer.resource));
        assertEquals(stock-2,first.inventory.amount(offer.resource));
        assertEquals(14,faction.reputation);
    }

    @Test public void reputationChangesPricesAndDiplomacyHasBoundedEffects() {
        Faction wary=new Faction("orbitals","Orbital Kin",-20),trusted=new Faction("orbitals","Orbital Kin",60);
        assertTrue(TradePricing.buyPrice(20,wary.reputation)>TradePricing.buyPrice(20,trusted.reputation));
        DiplomacyResult aid=Diplomacy.offerAid(wary,18);
        assertEquals(RelationState.NEUTRAL,aid.after); assertEquals(0,wary.reputation);
        DiplomacyResult pact=Diplomacy.signAccord(wary);
        assertFalse(pact.accepted);
        wary.changeReputation(55);
        assertTrue(Diplomacy.signAccord(wary).accepted); assertEquals(RelationState.ALLIED,wary.relation());
        wary.changeReputation(500); assertEquals(100,wary.reputation);
    }

    @Test public void familyFormationRequiresAdultsAndBirthIsNonExplicit() {
        FamilyRegistry registry=new FamilyRegistry();
        Person adultA=new Person("a","Ari",AgeStage.ADULT,24*YEAR_DAYS);
        Person adultB=new Person("b","Bela",AgeStage.ADULT,27*YEAR_DAYS);
        Person child=new Person("c","Cai",AgeStage.CHILD,7*YEAR_DAYS);
        registry.add(adultA);registry.add(adultB);registry.add(child);
        assertFalse(registry.formPartnership("a","c").accepted);
        assertTrue(registry.formPartnership("a","b").accepted);
        assertEquals("b",adultA.partnerId); assertEquals(adultA.householdId,adultB.householdId);
        assertFalse(registry.beginPregnancy("c","a",10).accepted);
        assertTrue(registry.beginPregnancy("a","b",10).accepted);
        assertTrue(registry.advanceDay(9).isEmpty());
        List<FamilyEvent> events=registry.advanceDay(10);
        assertEquals(FamilyEvent.Type.BIRTH,events.get(0).type);
        Person infant=registry.person(events.get(0).subjectId);
        assertEquals(AgeStage.INFANT,infant.stage); assertEquals(Arrays.asList("a","b"),infant.parentIds);
        assertTrue(infant.care.foodNeed>0); assertFalse(infant.canWorkOrFight());
    }

    @Test public void caregiverChoiceIsDeterministicAndChildrenRemainNoncombatants() {
        FamilyRegistry registry=new FamilyRegistry();
        Person child=new Person("kid","Nova",AgeStage.INFANT,20);
        child.parentIds.add("parent-z"); child.parentIds.add("parent-a");
        Person z=new Person("parent-z","Zed",AgeStage.ADULT,25*YEAR_DAYS);z.availableForCare=true;
        Person a=new Person("parent-a","Asha",AgeStage.ADULT,25*YEAR_DAYS);a.availableForCare=true;
        Person other=new Person("helper","Bo",AgeStage.ADULT,25*YEAR_DAYS);other.availableForCare=true;
        registry.add(other);registry.add(z);registry.add(child);registry.add(a);
        CareAssignment assignment=Caregiving.chooseCaregiver(registry,"kid");
        assertEquals("parent-a",assignment.caregiverId);
        child.care.foodNeed=80; child.care.comfortNeed=70;
        assertTrue(Caregiving.provideCare(registry,assignment,30).accepted);
        assertTrue(child.care.foodNeed<80); assertTrue(child.care.comfortNeed<70);
        GameWorld world=new GameWorld(9); GameWorld.Colonist minor=world.addDependent("Little Sol",AgeStage.CHILD,8*YEAR_DAYS,20,20);
        assertFalse(world.setDrafted(minor.id,true));
        world.enemies.add(GameWorld.enemyForTest(minor.x+.1f,minor.y,42));
        float health=minor.health; for(int i=0;i<10;i++)world.update(.1f);
        assertEquals(health,minor.health,0f);
    }

    @Test public void societyContractsAreJavaSerializable() throws Exception {
        SocietyState state=new SocietyState();
        state.factions.add(new Faction("f","Far Lanterns",4));
        state.families.add(new Person("p","Pax",AgeStage.ADULT,22*YEAR_DAYS));
        state.caravans.add(Caravan.create("c",state.factions.get(0),3,2));
        ByteArrayOutputStream bytes=new ByteArrayOutputStream();
        new ObjectOutputStream(bytes).writeObject(state);
        SocietyState restored=(SocietyState)new ObjectInputStream(new ByteArrayInputStream(bytes.toByteArray())).readObject();
        assertEquals("Far Lanterns",restored.factions.get(0).name);
        assertEquals(state.caravans.get(0).offers,restored.caravans.get(0).offers);
    }
}
