package trader.common.exchangeable;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

import trader.common.exchangeable.ExchangeContract.MarketTimeSegment;
import trader.common.util.DateUtil;

/**
 * 某个交易品种在某个具体交易日的交易时间信息
 */
public class ExchangeableTradingTimes {
    static class MarketTimeSegmentInfo{
        public final MarketTimeSegment segment;
        public final LocalDateTime[] marketTimes;
        public final int totalMillis;
        public final int[] marketTimeMillis;
        public MarketTimeSegmentInfo(MarketTimeSegment segment, LocalDateTime[] marketTimes) {
            this.segment = segment;
            this.marketTimes = marketTimes;
            int totalMillis=0;
            marketTimeMillis = new int[marketTimes.length/2];
            for(int i=0;i<marketTimes.length;i+=2) {
                LocalDateTime t0 = marketTimes[i], t1=marketTimes[i+1];
                Duration d = DateUtil.between(t0, t1);
                marketTimeMillis[i/2] = (int)d.getSeconds()*1000;
                totalMillis += d.getSeconds()*1000;
            }
            this.totalMillis = totalMillis;
        }
    }

    private Exchangeable instrument;
    private LocalDate tradingDay;
    private int totalTradingMillis;
    private LocalDateTime[] marketTimes;

    /**
     * 不同市场分段的开始时间
     */
    private List<MarketTimeSegmentInfo> segmentInfos;
    private int[] marketTimeMillis;

    ExchangeableTradingTimes(Exchangeable instrument, LocalDate tradingDay, LocalDateTime[] marketTimes, List<MarketTimeSegmentInfo> segmentInfos){
        this.instrument = instrument;
        this.tradingDay = tradingDay;
        this.segmentInfos = segmentInfos;
        this.marketTimes = marketTimes;
        this.marketTimeMillis = new int[marketTimes.length/2];
        totalTradingMillis = 0;
        for(int i=0;i<marketTimes.length;i+=2) {
            LocalDateTime marketTimeStageBegin = marketTimes[i];
            LocalDateTime marketTimeStageEnd = marketTimes[i+1];
            Duration d = DateUtil.between(marketTimeStageBegin, marketTimeStageEnd);
            marketTimeMillis[i/2] = (int)d.getSeconds()*1000;
            totalTradingMillis += (int)d.getSeconds()*1000;
        }
    }

    public Exchangeable getInstrument() {
        return instrument;
    }

    /**
     * 交易日
     */
    public LocalDate getTradingDay() {
        return tradingDay;
    }

    /**
     * 开市的总交易时长(毫秒)
     */
    public int getTotalTradingMillis() {
        return totalTradingMillis;
    }

    /**
     * 某个交易时间段(日市/夜市)的总时长(毫秒)
     */
    public int getTotalTradingMillisInSegment(MarketType marketType) {
        for(MarketTimeSegmentInfo segInfo:segmentInfos) {
            if ( segInfo.segment.marketType!=marketType) {
                continue;
            }
            return segInfo.totalMillis;
        }
        return -1;
    }

    /**
     * 交易时间段
     */
    public LocalDateTime[] getMarketTimes() {
        return marketTimes;
    };

    /**
     * 指定日市/夜市的交易时间段
     */
    public LocalDateTime[] getMarketTimes(MarketType marketType) {
        for(MarketTimeSegmentInfo info:segmentInfos) {
            if ( marketType==info.segment.marketType ) {
                return info.marketTimes;
            }
        }
        return null;
    }

    public LocalDateTime getMarketOpenTime() {
        return marketTimes[0];
    }

    public LocalDateTime getMarketCloseTime() {
        return marketTimes[marketTimes.length-1];
    }

    /**
     * 返回开市以来的时间(毫秒)
     */
    public int getTradingTime(LocalDateTime marketTime) {
        if ( marketTime.isBefore(marketTimes[0]) || compareTimeNoNanos(marketTime,marketTimes[marketTimes.length-1])>0) {
            return -1;
        }
        int result = 0;
        for(int i=0;i<marketTimes.length;i+=2) {
            LocalDateTime marketTimeStageBegin = marketTimes[i];
            LocalDateTime marketTimeStageEnd = marketTimes[i+1];
            if ( compareTimeNoNanos(marketTime, marketTimeStageBegin)<=0 ) {
                break;
            }
            Duration d = null;
            int compareResult = compareTimeNoNanos(marketTime, marketTimeStageEnd);
            if ( compareResult<=0 ) {
                d = DateUtil.between(marketTimeStageBegin, marketTime);
            }else {
                d = DateUtil.between(marketTimeStageBegin, marketTimeStageEnd);
            }
            long millis = d.getSeconds()*1000+d.getNano()/1000000;
            result += millis;
            if ( compareResult<=0 ) {
                break;
            }
        }
        return result;
    }

    /**
     * 返回当前的市场分段(日市/夜市)
     */
    public MarketType getSegmentType(LocalDateTime time) {
        for(MarketTimeSegmentInfo segInfo: this.segmentInfos) {
            LocalDateTime beginTime = segInfo.marketTimes[0].minusHours(1);
            LocalDateTime endTime = segInfo.marketTimes[segInfo.marketTimes.length-1];
            if ( time.compareTo(beginTime)>=0 && time.compareTo(endTime)<=0 ) {
                return segInfo.segment.marketType;
            }
        }
        return null;
    }

    /**
     * 返回市场分段(日市/夜市)的开始以来的时间(毫秒)
     */
    public int getTradingTimeInSegment(LocalDateTime time, MarketType marketType) {
        for(MarketTimeSegmentInfo segInfo: this.segmentInfos) {
            LocalDateTime beginTime = segInfo.marketTimes[0];
            LocalDateTime endTime = segInfo.marketTimes[segInfo.marketTimes.length-1];
            if ( time.compareTo(beginTime)>=0 && time.compareTo(endTime)<=0 && segInfo.segment.marketType==marketType) {
                LocalDateTime[] marketTimes = segInfo.marketTimes;
                int result=0;
                for(int i=0;i<marketTimes.length;i+=2) {
                    LocalDateTime t0 = marketTimes[i], t1=marketTimes[i+1];
                    if ( time.compareTo(t1)>=0 ) {
                        //如果超出当前交易时间小段
                        result += segInfo.marketTimeMillis[i/2];
                        continue;
                    }
                    if ( time.compareTo(t0)<=0 ) {
                        break;
                    }
                    if ( time.compareTo(t0)>=0 && time.compareTo(t1)<=0 ) {
                        Duration d = DateUtil.between(t0, time);
                        long millis = d.getSeconds()*1000+d.getNano()/1000000;
                        result += millis;
                        break;
                    }
                }
                return result;
            }
        }
        return -1;
    }

    /**
     * 市场时间段
     */
    public MarketTimeStage getTimeStage(LocalDateTime time) {
        for(int i=0;i<marketTimes.length;i+=2 ) {
            LocalDateTime frameBegin = marketTimes[i];
            LocalDateTime frameEnd = marketTimes[i+1];
            if ( isSegmentBeginTime(frameBegin) ) {
                LocalDateTime auctionTime = frameBegin.minusMinutes(5);
                LocalDateTime marketBeforeOpenTime = auctionTime.minusMinutes(55);

                if ( time.isBefore(marketBeforeOpenTime) ){
                    return MarketTimeStage.MarketClose;
                } else {
                    if ( time.isBefore(auctionTime) ) {
                        return MarketTimeStage.BeforeMarketOpen;
                    } else {
                        if ( time.isBefore(frameBegin)){
                            return MarketTimeStage.AggregateAuction;
                        }
                    }
                }
            } else {
                if ( time.isBefore(frameBegin) ) {
                    return MarketTimeStage.MarketBreak;
                }
            }
            if ( compareTimeNoNanos(time, frameBegin)>=0 && compareTimeNoNanos(time,frameEnd)<=0 ) {
                return MarketTimeStage.MarketOpen;
            }
        }
        return MarketTimeStage.MarketClose;
    }

    private boolean isSegmentBeginTime(LocalDateTime time) {
        for(MarketTimeSegmentInfo info:segmentInfos) {
            if ( info.marketTimes[0].equals(time)) {
                return true;
            }
        }
        return false;
    }

    private static int compareTimeNoNanos(LocalDateTime time1, LocalDateTime time2) {
        return time1.withNano(0).compareTo(time2.withNano(0));
    }

}