import { inject } from 'aurelia-framework';
import { EventAggregator } from 'aurelia-event-aggregator';
import { PDFExport } from './pdfExport';

import { UpdateVisualisation } from './messages';

import { Sampler } from './sampler';
import { SamplingConfiguration } from './configuration';
import { VisualConfig } from './config_visual';

import * as d3 from './d3custom';

@inject(VisualConfig, EventAggregator)

export class App {

    riskOfOverflow = false;
    visibilityModal = 'hidden';
    visibility = 0;
    modal = undefined;

    soffsets = [];  // 'synchronised' visual / vertical offsets
    pdfDownloadURL = '';
    selectedPeriod = {};
    configuration = undefined;

    constructor(vconfig, eventaggregator) {

        // keep configuration in case to reset app to defaults
        this.configuration = new SamplingConfiguration();

        this.ea = eventaggregator;

        // visual configuration. bypass insane event massaging mess
        for (let key in vconfig) {
            this[key] = vconfig[key];
        }

        // general configuration
        for (let key in this.configuration) {
            this[key] = this.configuration[key];
        }

        this.modal = this.modalStates[this.visibility];
        console.log(this.modal);

        this.format = this.configuration.formats[this.formatId];
        this.selectedPeriod = this.periods[this.format.periodId];
        this.selectedOption1 = this.investmentOptions[this.format.investmentOption1Id];
        this.selectedOption2 = this.investmentOptions[this.format.investmentOption2Id];

        let option = window.location.search.substr(1, 4);
        if (option === 'lang') {
            this.lang = window.location.search.substr(6, window.location.search.length - 1);
        } else {
            this.lang = 'de';
        }

        // load new data
        this.investmentOptionChanged();
    }

    initialise() {

        this.samplers = [];

        for (let i = 0; i < this.nrSamplers; ++i) {
            let vo = [];
            for (let c = 1; c < this.classes[0].length; ++c) {  // no offset for the default event
                vo[c - 1] = 0;
            }

            let sampler = new Sampler(i, this.data, this.classes[0], this.investmentRatios[i], this.maxSamples, this.voffsets);
            sampler.voffsets = vo;
            this.samplers.push(sampler);
        }

        for (let i = 0; i < this.classes[0].length; ++i) {
            this.soffsets[i] = 0;
        }

        document.title = this.title[this.lang];  // not using aurelia-router only to set document title

        this.generatePDF();
        this.reset();
    }

    queryData() {
        let iopt1 = this.selectedPeriod.name + '_' + this.selectedOption1.name;
        let iopt2 = this.selectedPeriod.name + '_' + this.selectedOption2.name;
        this.filterData(this, 'data/data.tsv', iopt1, iopt2);
    }

    filterData(self, filepath, o1, o2) {

        function parseVal(val) {
            return parseFloat(val.replace(',', '.'));
        }

        d3.tsv(filepath, function(d) {
            if (d[o1] && d[o2]) {
                return {
                    year: d.date_of_investment,
                    values: [parseVal(d[o1]), parseVal(d[o2])]
                };
            }
        }).then(function(data) {
            self.data = data;
            self.initialise();
        });
    }

    zip(arrays) {
        return arrays[0].map(function(_, i) {
            return arrays.map(function(array) {
                return array[i];
            });
        });
    }

    toggleSource(e) {
        e.preventDefault();
        this.visibility = (this.visibility + 1) % 2;
        this.modal = this.modalStates[this.visibility];
    }

    sampleButtonClicked(e) {

        /* // automatic mode, not enabled
        if (this.format.modeId === 1) {
            if (!this.intervalId) {
                this.drawSamples();
                this.intervalId = setInterval(this.drawSamples, this.intervalSize, this);
                this.buttonLabel = this.buttonLabels[2];
            } else {
                this.stop();
                // console.log('stopping. proceeding to evaluation');
            }
            return;
        }*/

        this.drawSamples(this, true);  // manual, send message?
    }

    drawSamples(diz, visualisationShouldBeUpdated = true) {

        let self = diz || this; // hackety-hack for manual as well as automatic calls
        if (self.samplers[0].samples.length >= self.maxSamples) {
            // if (self.intervalId) {
            //     self.stop();
            // }
            return;
        }

        let groupSize = self.groupSizes[self.formats[self.formatId].sizeId].value;
        let remaining = self.samplers[0].maxSamples - self.samplers[0].samples.length;
        let amount = remaining >= groupSize ? groupSize : remaining;

        // let hblocks = self.blocks.horizontal.amount;
        let vblocks = self.blocks.vertical.amount;
        let hblocksize = self.blocks.horizontal.size;
        let vblocksize = self.blocks.vertical.size;

        for (let sampler of self.samplers) {
            let spc = sampler.samplesPerClass();
            for (let i = 0; i < sampler.classes.length; ++i) {
                let amountBefore = spc[i].length;
                let amountAfter = spc[i].length + groupSize;
                if (spc[i].length > 0 &&
                    Math.ceil(amountBefore / (vblocks * vblocksize)) === self.soffsets[i] * hblocksize &&
                    Math.ceil(amountBefore / (vblocks * vblocksize)) < Math.ceil(amountAfter / (vblocks * vblocksize))) {
                    self.riskOfOverflow = true;
                    return;
                }
            }
        }

        for (let i = 0; i < amount; ++i) {
            // (importance?) sampling. As all data points are now equally probable, this implementation is a bit of an overkill.
            let cumulatedProbability = 0.0;
            let rval = Math.random();
            let probability = 1.0 / self.data.length;

            let idx = 0;
            for (; idx < self.data.length; ++idx) {
                cumulatedProbability += probability;

                if (rval < cumulatedProbability) {
                    break;
                }
            }

            for (let sampler of self.samplers) {
                sampler.drawSample(self.data[idx]);
            }
        }

        for (let sampler of self.samplers) {
            sampler.updateOffsets(self.blocks * self.blocksize * self.blocksize);
        }

        for (let cidx = 0; cidx < self.classes[0].length; ++cidx) {
            self.soffsets[cidx] = Math.max(self.samplers[0].voffsets[cidx], self.samplers[1].voffsets[cidx]);
        }

        if (visualisationShouldBeUpdated) {
            self.ea.publish(new UpdateVisualisation());
        }
    }

    // this function should always be called immediately after initialisation / reset
    generatePDF() {

        for (let i = 0; i < 1000; ++i) {
            this.drawSamples(this, false);
        }

        this.pdfx = new PDFExport(
            this,
            {
                title: this.title[this.lang],
                description: this.description[this.lang] + ' ' + this.selectedPeriod.value + ' ' + this.labels.period[this.lang],
                referenceClasses: [
                    this.referenceClasses[0],
                    this.referenceClasses[1]
                ],
                author: this.author,
                subject: this.subject[this.lang],
                classes: this.classes[0],
                colours: this.colours,
                context: this.context,
                fontRatios: this.fontRatios,
                iconArray: {
                    blocks: this.blocks,
                    // blocksize: this.blocksize,
                    // blockspacingRatio: this.blockspacingRatio,
                    dotspacingRatio: this.dotspacingRatio,
                    radius: this.radius
                },
                lang: this.lang,
                labels: this.labels,
                maxSamples: this.maxSamples,
                samples: [this.samplers[0].samples, this.samplers[1].samples],
                samplers: this.samplers,
                soffsets: this.soffsets,
                reference: this.reference
            }
        );
    }

    logAction(property, value) {

        let timestamp = new Date().toISOString();
        let msg = {
            'time': timestamp,
            'property': property,
            'value': value
        };

        // this.logger.info(msg);
        return JSON.stringify(msg);
    }

    updatePDFLink(url) {
        this.pdfDownloadURL = url;
        this.pdfFilename = this.title[this.lang] + '.pdf';
    }

    clearSamples() {
        for (let sampler of this.samplers) {
            sampler.reset();
        }
        this.riskOfOverflow = false;
        this.ea.publish(new UpdateVisualisation());
    }

    groupSizeChanged() {
        this.riskOfOverflow = false;
    }

    investmentOptionChanged() {
        this.queryData();
    }

    periodChanged() {
        this.queryData();
    }

    ratioChanged() {
        this.generatePDF();
        this.clearSamples();
    }

    get createVisualisation() {
        if (this.formatId === 0) {  // 'seqs': text only
            return false;
        }

        return true;
    }

    // disable option if already selected in comparative selection
    // idx: index of option (select)
    // oid: index of respective selected Option
    isComparism(idx, oid) {
        let coptid;
        if (oid === 0) {
            coptid = this.selectedOption2.id;
        } else if (oid === 1) {
            coptid = this.selectedOption1.id;
        }

        return idx === coptid;
    }

    get resetEnabled() {
        return this.samplers[0].samples.length === 0;
    }

    get maximumSamplesDrawn() {
        return this.riskOfOverflow;
    }

    reset() {
        // this.stop();
        this.format = this.configuration.formats[this.formatId];
        this.selectedPeriod = this.periods[this.format.periodId];
        // this.selectedOption1 = this.investmentOptions[this.format.investmentOption1Id];
        // this.selectedOption2 = this.investmentOptions[this.format.investmentOption2Id];

        if (this.samplers) {
            this.clearSamples();
        }

        this.riskOfOverflow = false;
    }
}
