public class GameState {
    public final static int MARKET_WARMUP_ITERATIONS = 4000;
    private final GameSaver gameSaver = new GameSaver(this);
    public RxBus bus = new RxBus();
    private final MarketSystem system;

    public Engine getEntityEngine() {
        return entityEngine;
    }

    Engine entityEngine = new Engine();
    long last_time = System.currentTimeMillis();
    Executor executorService = Executors.newSingleThreadExecutor();
    private volatile boolean running = false;
    private int gameSpeed = 1;
    Random random;

    public static String[] names = new String[6];


    public GameState(int seed, int max_iterations) {
        random = new Random(seed);
        entityEngine.addEntity(new PlayerEntity("Player", "adamhammer2@gmail.com", "test"));
        entityEngine.addEntity(new MarketItemEntity(names[0] = getRandomStockName(),
                500,
                100,
                1));
        entityEngine.addEntity(new MarketItemEntity(names[1] = getRandomStockName(),
                500,
                100,
                2));
        entityEngine.addEntity(new MarketItemEntity(names[2] = getRandomStockName(),
                500,
                100,
                3));

        entityEngine.addEntity(new MarketItemEntity(names[3] = getRandomStockName(),
                500,
                100,
                4));

        entityEngine.addEntity(new MarketItemEntity(names[4] = getRandomStockName(),
                500,
                100,
                5));

        entityEngine.addEntity(new MarketItemEntity(names[5] = getRandomStockName(),
                500,
                100,
                6));


        entityEngine.addEntity(new GameStateEntity());
        entityEngine.addSystem(system = new MarketSystem(bus, seed, max_iterations + MARKET_WARMUP_ITERATIONS));
        entityEngine.update(MARKET_WARMUP_ITERATIONS);
    }


    private void iterate() {
        long current_time = System.currentTimeMillis();
        long delta_time = current_time - last_time;
        last_time = current_time;
        delta_time *= gameSpeed;
        entityEngine.update(delta_time / 1000f);

    }

    public void printMarketPrices() {
        Family market = Family.all(MarketItemComponent.class, PriceComponent.class, NameComponent.class).get();
        ImmutableArray<Entity> entities = entityEngine.getEntitiesFor(market);
        System.out.println("Current Prices: ");
        for (Entity e : entities) {
            PriceComponent p = e.getComponent(PriceComponent.class);
            NameComponent n = e.getComponent(NameComponent.class);
            System.out.println("Name: " + n.name + " Price: " + p.price);
        }
    }

    public void startThread() {
        running = true;
        last_time = System.currentTimeMillis(); //Always reset the timer
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                while (running) {
                    iterate();

                }
            }
        });
    }

    public void stopThread() {
        running = false;
    }

    public void printUserData() {
        Family market = Family.all(MarketItemComponent.class, PriceComponent.class, NameComponent.class, PriceHistoryComponent.class).get();
        ImmutableArray<Entity> marketItemEntities = entityEngine.getEntitiesFor(market);

        Family players = Family.all(WalletComponent.class, NameComponent.class, InventoryComponent.class).get();
        for (Entity player : entityEngine.getEntitiesFor(players)) {
            NameComponent nc = player.getComponent(NameComponent.class);
            WalletComponent wc = player.getComponent(WalletComponent.class);
            InventoryComponent ic = player.getComponent(InventoryComponent.class);
            System.out.println("Name: " + nc.name);
            ic.printInventory(marketItemEntities);
            long value = ic.getInventoryValue(marketItemEntities);
            System.out.println("Inventory Value: $" + value);
            System.out.println(" Wallet Balance: $" + wc.balance);
            System.out.println("    Total Value: $" + (value + wc.balance));
        }
    }

    public void printMarketHistory() {
        Family market = Family.all(MarketItemComponent.class, PriceComponent.class, NameComponent.class, PriceHistoryComponent.class).get();
        ImmutableArray<Entity> entities = entityEngine.getEntitiesFor(market);

        System.out.println("Current Prices: ");
        for (Entity e : entities) {
            PriceComponent p = e.getComponent(PriceComponent.class);
            NameComponent n = e.getComponent(NameComponent.class);
            PriceHistoryComponent history = e.getComponent(PriceHistoryComponent.class);

            Iterator itr;

            System.out.print(n.name + " Resolution 1 \n");
            itr = history.getPriceHistory().iterator();
            while (itr.hasNext()) System.out.print((Integer) itr.next() + "\n");
            System.out.print(n.name + " Resolution 5\n");
            itr = history.getPriceHistorySkipFive().iterator();
            while (itr.hasNext()) System.out.print((Integer) itr.next() + "\n");

            System.out.print(n.name + " Resolution 15\n");
            itr = history.getPriceHistorySkipFifteen().iterator();
            while (itr.hasNext()) System.out.print((Integer) itr.next() + "\n");

        }

    }

    public PlayerEntity getCurrentPlayer() {
        Family players = Family.all(WalletComponent.class, NameComponent.class, InventoryComponent.class).get();
        return (PlayerEntity) entityEngine.getEntitiesFor(players).first();
    }

    public MarketItemEntity getMarketItemByName(String item) {
        Family marketItems = Family.all(MarketItemComponent.class, NameComponent.class).get();
        for (Entity e : entityEngine.getEntitiesFor(marketItems)) {
            if (e.getComponent(NameComponent.class).name.equalsIgnoreCase(item)) {
                return (MarketItemEntity) e;
            }
        }
        return null;

    }

    public List<MarketItemEntity> getMarketItems() {

        ArrayList marketItems = new ArrayList<MarketItemEntity>();
        Family marketItemFamily = Family.all(MarketItemComponent.class).get();
        for (Entity e : entityEngine.getEntitiesFor(marketItemFamily)) {
            if (e instanceof MarketItemEntity) marketItems.add(e);
        }
        return marketItems;


    }


    public ImmutableArray<Entity> getMarketItemsRaw() {

        ArrayList marketItems = new ArrayList<MarketItemEntity>();
        Family marketItemFamily = Family.all(MarketItemComponent.class).get();
        return entityEngine.getEntitiesFor(marketItemFamily);


    }


    public int getIteration() {
        return system.getInterval();
    }

    public long getInventoryCount(MarketItemEntity item) {
        return getCurrentPlayer()
                .getComponent(InventoryComponent.class)
                .getInventoryQuantity(item.getComponent(NameComponent.class));
    }

    public int getGameSpeed() {
        return gameSpeed;
    }

    public void toggleGameSpeed() {
        gameSpeed *= 2;
        if (gameSpeed > 32) {
            gameSpeed = 1;
        }

    }

    public int getMaxIntervals() {
        return system.getMaxIntevals() - MARKET_WARMUP_ITERATIONS;
    }


    public String saveToJson() {
        return gameSaver.saveToJson();
    }

    public void loadFromJson(String json) {
        gameSaver.loadFromJson(json);
    }


    private String getRandomStockName() {
        int length = random.nextInt(2) + 2;
        if (random.nextBoolean()) {
            return ("" + randomLetter() + randomLetter() + randomLetter());
        } else {
            return ("" + randomLetter() + randomLetter() + randomLetter() + randomLetter());
        }
    }

    private char randomLetter() {
        return (char) (random.nextInt(26) + 'a');
    }


} public class RxBus {
    //private final PublishSubject<Object> _bus = PublishSubject.create();

    // If multiple threads are going to emit events to this
    // then it must be made thread-safe like this instead
    private final Subject<Object, Object> _bus = new SerializedSubject(PublishSubject.create());

    public void post(Object o) {
        _bus.onNext(o);
    }

    public Observable<Object> toObserverable() {
        return _bus;
    }

} public class GameStateComponent extends Component{
    public GameStateComponent(){}
    public int market_interval = 0;

} public class InventoryComponent extends Component {
    public Map<NameComponent, Integer> inventory = new HashMap<NameComponent, Integer>();

    public InventoryComponent() {
    }

    public void printInventory(ImmutableArray<Entity> marketItems) {
        for (Entity e:marketItems) {
            NameComponent nc = e.getComponent(NameComponent.class);
            if (inventory.containsKey(nc)) {
                PriceComponent pc = e.getComponent(PriceComponent.class);
                System.out.println(nc.name + " : "+inventory.get(nc) + " = $" + inventory.get(nc)*pc.price);
            }
        }
    }


    public int getInventoryValue(ImmutableArray<Entity> marketItems) {
        int value = 0;
        for (Entity e:marketItems) {
            NameComponent nc = e.getComponent(NameComponent.class);
            if (inventory.containsKey(nc)) {
                PriceComponent pc = e.getComponent(PriceComponent.class);
                value+= inventory.get(nc)*pc.price;
            }
        }
        return value;
    }

    public long getInventoryQuantity(NameComponent nc) {
        if (inventory.containsKey(nc))
            return inventory.get(nc);
        else return 0;
    }



} public class MarketDemandComponent extends Component {
    public double supply = 0;
    public double demand = 0;
    public double supplyFalloff = 0.99;
    public double demandFalloff = 0.99;
} public class MarketItemComponent extends Component {
} public class MarketVolatilityComponent extends Component{
    public final int min_price;
    public final int volatility;

    public MarketVolatilityComponent(int min_price, int volatility) {
        this.min_price = min_price;
        this.volatility = volatility;

    }
} public class NameComponent extends Component {
    public String name;

    public NameComponent() {}

    public NameComponent(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        NameComponent that = (NameComponent) o;
        if (name != null ? !name.equals(that.name) : that.name != null) return false;
        return true;
    }

    @Override
    public int hashCode() {
        return name != null ? name.hashCode() : 0;
    }

    @Override
    public String toString() {
        return "NameComponent{" +
                "name='" + name + '\'' +
                '}';
    }
} public class PriceComponent extends Component {
    /**
     * Price in pennies
     */
    public int price;

    public PriceComponent(int price) {
        this.price = price;
    }
} public class PriceHistoryComponent extends Component {
    private static final int MAX_SIZE = 100;
    private final List<Integer> priceHistory = new CopyOnWriteArrayList<Integer>();
    private final List<Integer> priceHistorySkipFive = new CopyOnWriteArrayList();
    private final List<Integer> priceHistorySkipFifteen = new CopyOnWriteArrayList();
    private final List<Integer> priceHistorySkipOneHundred = new CopyOnWriteArrayList();

    public void registerPrice(PriceComponent pc, int iteration) {
        synchronized (priceHistory) {
            priceHistory.add(pc.price);
            if (priceHistory.size() > MAX_SIZE) priceHistory.remove(0);
        }

        synchronized (priceHistorySkipFive) {
            if (iteration % 5 == 0) {
                priceHistorySkipFive.add(pc.price);
                if (priceHistorySkipFive.size() > MAX_SIZE) priceHistorySkipFive.remove(0);
            }
        }

        synchronized (priceHistorySkipFifteen) {
            if (iteration % 15 == 0) {
                priceHistorySkipFifteen.add(pc.price);
                if (priceHistorySkipFifteen.size() > MAX_SIZE) priceHistorySkipFifteen.remove(0);
            }
        }

        synchronized (priceHistorySkipOneHundred) {
            if (iteration % 100 == 0) {
                priceHistorySkipOneHundred.add(pc.price);
            }
        }
    }

    public List<Integer> getPriceHistory() {
        return Collections.synchronizedList(priceHistory);
    }

    public List<Integer> getPriceHistorySkipFive() {
        return Collections.synchronizedList(priceHistorySkipFive);
    }

    public List<Integer> getPriceHistorySkipFifteen() {
        return Collections.synchronizedList(priceHistorySkipFifteen);
    }


    public List<Integer> getPriceHistorySkipOneHundred() {
        return Collections.synchronizedList(priceHistorySkipOneHundred);
    }
} public class StockImpactEventComponent extends Component{

    public final String message;
    public final int iteration;
    private final int[] demand_impact;
    private final String[] market_names;
    private final int[] supply_impact;

    ///new vars


    String toString="nope";
    private static int integer=1;
    private static String displayData="";

    public static String name1;
    public static String name2;
    public static String name3;
    public static String name4;
    public static String name5;

    public StockImpactEventComponent(String message, int iteration, String[] market_names, int[] supply_impact, int[] demand_impact) {

        integer++;
        this.message = message;
        this.iteration = iteration;
        this.market_names = market_names;
        this.supply_impact = supply_impact;
        this.demand_impact = demand_impact;
        assert (market_names.length == supply_impact.length);
        assert (market_names.length == demand_impact.length);

    }


    public boolean appliesTo(String name) {

        for (String market_name:market_names) {
            if (market_name.equalsIgnoreCase(name))
                return true;
        }
        return false;
    }
    public  void setNames(String name1, String name2,String name3,String name4, String name5){
        this.name1=name1;
        this.name2=name2;
        this.name3=name3;
        this.name4=name4;
        this.name5=name5;

    }

    public void apply(NameComponent nc, MarketDemandComponent demandComponent) {
        int index = 0;
        String supplyHolder="";
        String demandHolder="";
        String demandViewData="\nDemand:   ";
        String supplyViewData="Supply:   ";
        int counter=0;

        for (String market_name:market_names) {

            demandComponent.demand += demand_impact[index];
            demandComponent.supply += supply_impact[index];

            demandHolder=(" "+market_name+":"+String.valueOf(demand_impact[index]));
            demandViewData+=demandHolder;
            supplyHolder=(" "+market_name+":"+String.valueOf(supply_impact[index]));
            supplyViewData+=supplyHolder;

            System.out.println(String.valueOf(counter));
            counter++;



            index++;

        }

        displayData=supplyViewData+demandViewData;

        integer++;

    }
    @Override
    public String toString(){

        return displayData;
    }


} public class WalletComponent extends Component {
    public int balance = 100000;
}
import com.badlogic.ashley.core.Engine;
import com.badlogic.ashley.core.Entity;
import com.badlogic.ashley.core.Family;
import com.badlogic.ashley.systems.IntervalSystem;
import com.badlogic.ashley.utils.ImmutableArray;
import com.metalrain.stocksimulator.state.GameState;
import com.metalrain.stocksimulator.state.RxBus;
import com.metalrain.stocksimulator.state.components.GameStateComponent;
import com.metalrain.stocksimulator.state.components.InventoryComponent;
import com.metalrain.stocksimulator.state.components.MarketDemandComponent;
import com.metalrain.stocksimulator.state.components.MarketItemComponent;
import com.metalrain.stocksimulator.state.components.MarketVolatilityComponent;
import com.metalrain.stocksimulator.state.components.NameComponent;
import com.metalrain.stocksimulator.state.components.PriceComponent;
import com.metalrain.stocksimulator.state.components.PriceHistoryComponent;
import com.metalrain.stocksimulator.state.components.StockImpactEventComponent;
import com.metalrain.stocksimulator.state.components.WalletComponent;
import java.util.Random;
package com.metalrain.stocksimulator.state.systems;





/**
 * Created by Adam Hammer on 6/22/2015.
 */
public class MarketSystem extends IntervalSystem {
    private final RxBus bus;
    private final Random random;
    private final int maxIntevals;

    public int getMaxIntevals() {
        return maxIntevals;
    }

    int interval = 0;
    private Family gameStateFamily;
    private Family marketItemFamily;
    private Family stockImpactEventComponent;
    private Family playerFamily;
    private Engine engine;

    public int getInterval() {
        return interval;
    }

    public MarketSystem(RxBus bus, int seed, int max_intervals) {
        super(1f);
        random = new Random(seed);
        this.bus = bus;
        this.maxIntevals = max_intervals;
    }

    @Override
    public void addedToEngine(Engine engine) {
        this.engine = engine;
        this.marketItemFamily = Family.all(MarketItemComponent.class, MarketVolatilityComponent.class, PriceComponent.class, PriceHistoryComponent.class).get();
        playerFamily = Family.all(WalletComponent.class, NameComponent.class, InventoryComponent.class).get();

        this.gameStateFamily = Family.all(GameStateComponent.class).get();
        this.stockImpactEventComponent = Family.all(StockImpactEventComponent.class).get();
    }


    @Override
    public void updateInterval() {
        if (interval >= maxIntevals) return;
        interval++;
        StockImpactEventComponent component = injectMarketEvents();
        performMarketCalculations(component);
        bus.post(new MarketUpdatedMessage());
        if (interval == maxIntevals)bus.post(new TimesUpMessage());

    }

    private void performMarketCalculations(StockImpactEventComponent current_event) {
        try {

            Entity gameStateEntity = engine.getEntitiesFor(gameStateFamily).first();
            GameStateComponent gameStateComponent = gameStateEntity.getComponent(GameStateComponent.class);
            gameStateComponent.market_interval++;

            //Grab a list of
            ImmutableArray<Entity> entities = engine.getEntitiesFor(marketItemFamily);


            for (Entity e : entities) {
                PriceComponent pc = e.getComponent(PriceComponent.class);
                NameComponent nc = e.getComponent(NameComponent.class);
                MarketVolatilityComponent volatilityComponent = e.getComponent(MarketVolatilityComponent.class);
                MarketDemandComponent demandComponent = e.getComponent(MarketDemandComponent.class);
                PriceHistoryComponent priceHistoryComponent = e.getComponent(PriceHistoryComponent.class);
                priceHistoryComponent.registerPrice(pc, gameStateComponent.market_interval);


                if (current_event != null && current_event.appliesTo(nc.name)) current_event.apply(nc, demandComponent);

                //Clamp price in range
                if (pc.price < volatilityComponent.min_price) {
                    demandComponent.demand = demandComponent.supply + 1;
                    pc.price = volatilityComponent.min_price;
                }

                //Market Crash!!
                if (random.nextDouble() < (volatilityComponent.volatility/10000f))
                { demandComponent.demand = -(random.nextDouble()*volatilityComponent.volatility * 500); }

                //Regular market activity
                int delta = (int) (random.nextDouble() * ((demandComponent.demand - demandComponent.supply)/50f) * (volatilityComponent.volatility));
                pc.price += delta;

                demandComponent.demand *= demandComponent.demandFalloff;
                demandComponent.supply *= demandComponent.supplyFalloff;

                //Bonus demand to keep things looking on up
                demandComponent.demand += 1;
            }

            updatePlayerValues(entities);
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    private void updatePlayerValues(ImmutableArray<Entity> entities) {
        ImmutableArray<Entity> players = engine.getEntitiesFor(playerFamily);
        for (Entity p:players) {
            int wallet_amount = p.getComponent(WalletComponent.class).balance;
            int investment_amount = p.getComponent(InventoryComponent.class).getInventoryValue(entities);
            p.getComponent(PriceHistoryComponent.class).registerPrice(new PriceComponent(investment_amount+wallet_amount),interval);
        }


    }

    private StockImpactEventComponent generateRandom() {
        if (random.nextBoolean()) {
            return new StockImpactEventComponent("Demand UP!", interval, new String[]{GameState.names[0], GameState.names[1], GameState.names[2], GameState.names[3], GameState.names[4], GameState.names[5]},