import OrderBookUpdateEvent from "../event/OrderBookUpdateEvent.js";
import OrderBookEntry from "./OrderBookEntry.js";

export default class OrderBook{
    /**
     * @type {Map<number, OrderBookEntry>}
     */
    bids = new Map();

    /**
     * @type {Map<number, OrderBookEntry>}
     */
    asks = new Map();

    /**
     * @type {number}
     */
    updateId = 0;

    /**
     * Amount of bids or asks to store (default: 10)
     * @type {number}
     */
    maxEntries = 10;

    /**
     * @param {OrderBookUpdateEvent} updateEvent 
     */
    set(updateEvent){
        this.updateId = updateEvent.updateId;
        
        this.asks.clear();
        updateEvent.asks.forEach(a => this.asks.set(a.price, a));
        
        this.bids.clear();
        updateEvent.bids.forEach(b => this.bids.set(b.price, b));
    }

    /**
     * @param {OrderBookUpdateEvent} updateEvent 
     */
    update(updateEvent){
        if(updateEvent.isReset){
            this.set(updateEvent);
            return;
        }

        if(this.updateId > updateEvent.updateId){
            return;
        }

        this.updateId = updateEvent.updateId;
        updateEvent.bids.forEach((bid) => {
            if(bid.quantity == 0){
                this.bids.delete(bid.price);
            } else {
                this.bids.set(bid.price, bid);
            }
        });
        updateEvent.asks.forEach((ask) => {
            if(ask.quantity == 0){
                this.asks.delete(ask.price);
            } else {
                this.asks.set(ask.price, ask);
            }
        });

        this.#fixOrderMaps();
    }

    #fixOrderMaps(){
        const bids = this.getBids();
        for(let i = this.maxEntries; i < bids.length; i++){
            this.bids.delete(bids[i].price);
        }

        const asks = this.getAsks();
        for(let i = this.maxEntries; i < asks.length; i++){
            this.asks.delete(asks[i].price);
        }
    }

    /**
     * @returns {OrderBookEntry[]}
     */
    getBids(){
        return Array.from(this.bids.values()).sort((v1, v2) => v2.price - v1.price);
    }

    /**
     * @returns {OrderBookEntry[]}
     */
    getAsks(){
        return Array.from(this.asks.values()).sort((v1, v2) => v1.price - v2.price);
    }

    /**
     * @returns {OrderBookEntry}
     */
    getBestBid(){
        const bids = this.getBids();
        if(bids.length > 0){
            return bids[0];
        }

        return null;
    }

    /**
     * @returns {OrderBookEntry}
     */
    getBestAsk(){
        const asks = this.getAsks();
        if(asks.length > 0){
            return asks[0];
        }

        return null;
    }
}