import TradeEvent from "../../../apiwrapper/event/TradeEvent.js";
import UnitHelper from "../../util/UnitHelper.js";
import AggregatedDataPoint from "./AggregatedDataPoint.js";

export default class AssetDataAggregator {
    static #SLOT_1_SEC = 1000;
    static #SLOT_1_MIN = 60000;

    /**
     * @type {AggregatedDataPoint}
     */
    #currentDataPoint = null;

    /**
     * @type {AggregatedDataPoint[]}
     */
    #perSecondDataPoints = [];

    /**
     * @type {AggregatedDataPoint[]}
     */
    #perMinuteDataPoints = [];

    #lastOneMinUpdate = 0;

    #updateCurrentDataPoint(){
        const timestamp = Date.now();

        // not initialized yet
        if(this.#currentDataPoint == null){
            this.#currentDataPoint = new AggregatedDataPoint(timestamp, AssetDataAggregator.#SLOT_1_SEC);
            this.#lastOneMinUpdate = timestamp;
            return;
        }

        this.#updateOneSecSlot(timestamp);
        this.#updateOneMinSlot(timestamp);
    }

    #updateOneMinSlot(timestamp){
        const elapsedTime = timestamp - this.#lastOneMinUpdate;

        // timeslot not over or not enough data to create
        if(elapsedTime < AssetDataAggregator.#SLOT_1_MIN || this.#perSecondDataPoints.length < 60){
            return;
        }

        const tmpSlot = new AggregatedDataPoint(timestamp - AssetDataAggregator.#SLOT_1_MIN, AssetDataAggregator.#SLOT_1_MIN);
        
        for(let i = this.#perSecondDataPoints.length - 60; i >= 0; i--){
            const dp = this.#perSecondDataPoints[i];
            tmpSlot.pushAsk(dp.bestAsk);
            tmpSlot.pushBid(dp.bestBid);
            tmpSlot.addTrades(dp.tradeCount, dp.tradeVolume);
        }
        tmpSlot.end(timestamp + AssetDataAggregator.#SLOT_1_MIN);
        this.#lastOneMinUpdate = timestamp;

        this.#pushSlotAndFillUp(this.#perMinuteDataPoints, tmpSlot, AssetDataAggregator.#SLOT_1_MIN, timestamp);

        while(this.#perSecondDataPoints.length > 10080){
            this.#perSecondDataPoints.shift();
        }
    }

    #updateOneSecSlot(timestamp){
        const elapsedTime = timestamp - this.#currentDataPoint.startTime;

        // timeslot not over
        if(elapsedTime < AssetDataAggregator.#SLOT_1_SEC || (!this.#currentDataPoint.hasValues && this.#perSecondDataPoints.length < 1)){
            return;
        }

        const tmpSlot = this.#currentDataPoint;
        if(!tmpSlot.hasValues){
            tmpSlot = this.#perSecondDataPoints[this.#perSecondDataPoints.length - 1].clone();
            tmpSlot.startTime = timestamp - AssetDataAggregator.#SLOT_1_SEC;
        }
        this.#currentDataPoint = new AggregatedDataPoint(timestamp, AssetDataAggregator.#SLOT_1_SEC);
        tmpSlot.end(timestamp);

        this.#pushSlotAndFillUp(this.#perSecondDataPoints, tmpSlot, AssetDataAggregator.#SLOT_1_SEC, timestamp);

        while(this.#perSecondDataPoints.length > 60){
            this.#perSecondDataPoints.shift();
        }
    }

    /**
     * @param {AggregatedDataPoint[]} targetArr 
     * @param {AggregatedDataPoint} point 
     * @param {number} duration 
     * @param {number} timestamp
     */
    #pushSlotAndFillUp(targetArr, point, duration, timestamp){
        const elapsedTime = timestamp - point.startTime;
        const missedSteps = Math.floor(elapsedTime / duration) - 1;

        targetArr.push(point);

        for(let i = 0; i < missedSteps; i++){
            const newPoint = point.clone();
            newPoint.startTime = point.startTime + duration * (i + 1);
            newPoint.endTime = newPoint.startTime + duration;
            targetArr.push(newPoint);
        }
    }

    /**
     * Returns an array of aggregated data points.
     * If the duration is smaler then 1 min, datapoints per second will be returned, else datapoints per minute.
     * If the starttime is not it defaults to now() - duration;
     * If starttime is older then 1m, even with a duration less then 1m, only 1m data points will be returned.
     * 
     * If no data for given start timestamp and duration exists, an empty array will be returned! 
     * 
     * @param {string} duration chrono unit (eg. 1000 or 1s or 15m)
     * @param {number} start start timestamp
     * @returns {AggregatedDataPoint[]}
     */
    getData(duration, start = -1){
        const result = [];
        const durationInMs = UnitHelper.formatedDurationToMs(duration);

        // if seconds data required
        if(duration <= 60000 && start == -1){

        }

        return result;
    }

    /**
     * @param {number} time 
     * @param {number} price 
     * @param {number} quantity 
     */
    pushTrade(time, price, quantity) {
        this.#updateCurrentDataPoint();
        this.#currentDataPoint.pushTrade(/* TODO */);
    }

}