package be.iminds.ilabt.jfed.highlevel.stitcher;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * StitchingTestSetupConfigBuilder
 */
public class StitchingTestSetupConfigBuilder {
    public enum AmVersion { V2, V3, V2AND3 }

    private int amCount = 2;
    private final Set<StitchingTestSetupConfig.SpecialCase> specialCases;
    private final List<StitchingTestSetupAmConfig> amConfigs;

    public StitchingTestSetupConfigBuilder() {
        amConfigs = new ArrayList<>();
        specialCases = new HashSet<>();
        for (int amIndex = 0; amIndex < 3; amIndex++)
            amConfigs.add(new StitchingTestSetupAmConfig(amIndex));
    }

    /** constructor based on config (sort of copy constructor, but not quite) */
    public StitchingTestSetupConfigBuilder(StitchingTestSetupConfig baseConfig) {
        amCount = baseConfig.amCount();
        amConfigs = new ArrayList<>();
        for (int amIndex = 0; amIndex < amCount; amIndex++)
            amConfigs.add(new StitchingTestSetupAmConfig(amIndex, baseConfig.am(amIndex+1)));
        specialCases = new HashSet<>(baseConfig.specialCases);
    }

    private static class StitchingTestSetupAmConfig {
        private final int amIndex;

        private boolean hasV2 = false;
        private boolean hasV3 = true;
        private boolean supportsAny = true;
        private boolean noStitching = false;
        private boolean advUpToDate = false;

        public final Set<StitchingTestSetupConfig.SpecialReply> specialReplies = new HashSet<>();

        public StitchingTestSetupAmConfig(int amIndex) {
            this.amIndex = amIndex;
        }

        public StitchingTestSetupAmConfig(int amIndex, StitchingTestSetupConfig.StitchingTestSetupAmConfig amConfig) {
            this.amIndex = amIndex;
            hasV2 = amConfig.hasV2;
            hasV3 = amConfig.hasV3;
            supportsAny = amConfig.supportsAny;
            noStitching = amConfig.noStitching;
            specialReplies.addAll(amConfig.specialReplies);
        }

        public void setAmVersion(AmVersion v) {
            this.hasV2 = v == AmVersion.V2 || v == AmVersion.V2AND3;
            this.hasV3 = v == AmVersion.V3 || v == AmVersion.V2AND3;
        }

        public void setSupportsAny(boolean supportsAny) {
            this.supportsAny = supportsAny;
        }
        public void setNoStitching(boolean noStitching) {
            this.noStitching = noStitching;
        }
        public void setAdvertisementUpToDate(boolean advUpToDate) {
            this.advUpToDate = advUpToDate;
        }

        public void addSpecialReply(StitchingTestSetupConfig.SpecialReply specialReply) {
            specialReplies.add(specialReply);
        }
        public boolean hasSpecialReply(StitchingTestSetupConfig.SpecialReply specialReply) {
            return specialReplies.contains(specialReply);
        }
    }

    public void setAmVersion(int amIndex, AmVersion v) {
        assert amIndex > 0;
        assert amIndex <= amCount;
        amConfigs.get(amIndex-1).setAmVersion(v);
    }

    public void setSupportsAny(int amIndex, boolean amSupportsAny) {
        assert amIndex > 0;
        assert amIndex <= amCount;
        amConfigs.get(amIndex-1).setSupportsAny(amSupportsAny);
    }

    public void setNoStitching(int amIndex, boolean noStitching) {
        assert amIndex > 0;
        assert amIndex <= amCount;
        amConfigs.get(amIndex-1).setNoStitching(noStitching);
    }

    public void setAdvertisementUpToDate(int amIndex, boolean advUpToDate) {
        assert amIndex > 0;
        assert amIndex <= amCount;
        amConfigs.get(amIndex-1).setAdvertisementUpToDate(advUpToDate);
    }

    public void addSpecialReply(int amIndex, StitchingTestSetupConfig.SpecialReply specialReply) {
        assert amIndex > 0;
        assert amIndex <= amCount;
        amConfigs.get(amIndex-1).addSpecialReply(specialReply);
    }

    public boolean hasSpecialReply(int amIndex, StitchingTestSetupConfig.SpecialReply specialReply) {
        assert amIndex > 0;
        assert amIndex <= amCount;
        return amConfigs.get(amIndex-1).hasSpecialReply(specialReply);
    }

    public void use3Ams(boolean use3) {
        amCount = use3 ? 3 : 2;
    }

    public void setAm2DependsOnAm1(boolean am2DependsOnAm1) {
        if (am2DependsOnAm1)
            this.specialCases.add(StitchingTestSetupConfig.SpecialCase.am2DependsOnAm1);
        else
            this.specialCases.remove(StitchingTestSetupConfig.SpecialCase.am2DependsOnAm1);
    }
    public void setHasFakeAm3(boolean hasFakeAm3) {
        if (hasFakeAm3)
            this.specialCases.add(StitchingTestSetupConfig.SpecialCase.hasFakeAm3);
        else
            this.specialCases.remove(StitchingTestSetupConfig.SpecialCase.hasFakeAm3);
    }
    public void setSpecialCase(StitchingTestSetupConfig.SpecialCase specialCase) {
        this.specialCases.add(specialCase);
    }

    public StitchingTestSetupConfig build() {
        List<StitchingTestSetupConfig.StitchingTestSetupAmConfig> resAmConfigs = new ArrayList<>();
        for (int amIndex = 0; amIndex < amCount; amIndex++) {
            StitchingTestSetupAmConfig conf = amConfigs.get(amIndex);
            resAmConfigs.add(new StitchingTestSetupConfig.StitchingTestSetupAmConfig(
                    conf.amIndex, conf.hasV2, conf.hasV3, conf.supportsAny, conf.noStitching, conf.advUpToDate, conf.specialReplies
            ));
        }

        StitchingTestSetupConfig res = new StitchingTestSetupConfig(resAmConfigs, specialCases);
        return res;
    }
}
