
angular.module('Patients', [])
.service('Patients', ['$http', '$rootScope', 'DataFiltering',
function($http, $rootScope, DataFiltering) {
    var self = this;

    this.data;      // all data
    this.filtered;  // filtered

    // min/max of all data
    this.dataSrcMinEpoch;
    this.dataSrcMaxEpoch;

    // local min/max
    this.minTimestamp;
    this.maxTimestamp;

    // filters applied to data
    this.filters = {};

    this.specimenTypes;
    this.institutes;
    this.vendors;
    this.stages;
    this.status;


    this.getPatients = function() {
        return $http.get('../data/patient-samples.json').then(res => {
            var d = res.data;

            // filter any nonsense
            var data = d.rows.filter(row => row.vendor_sample_number != '-')

            self.data = data;
            self.filtered = data;

            // get initial col infos
            self.specimenTypes = getColStats(data, 'cancer_diagnosis')
            self.institutes = getColStats(data, 'institution')
            self.vendors = self.getVendorList(data)
            self.stages = getColStats(data, 'diagnosis')
            self.status = getColStats(data, 'adequate_for_analysis')

            // get initial min/max times
            var minMax = self.getMinMaxTimes(data)
            self.dataSrcMinEpoch = minMax.min
            self.dataSrcMaxEpoch = minMax.max
            self.minTimestamp = minMax.min
            self.maxTimestamp = minMax.max

            // update data
            $rootScope.$broadcast('PatientView.filterEvent', self.data.length)
            return data;
        })
    }


    // does the magic
    this.applyFilters = () => {
        let data = angular.copy(self.data);

        // filter by all queries
        for (var key in self.filters) {
            data = data.filter(row => self.filters[key].indexOf(row[key]) != -1)
        }

        // filter by any time restrictions
        data = self.filterByTime(data, self.minTimestamp, self.maxTimestamp);

        self.filtered = data;
        $rootScope.$broadcast('Patients.filtered', data);
    }

    // adds a new filter
    this.addFilter = (colName, val) => {
        self.addFilters(colName, [val]);
    }

    // adds multiple filters
    this.addFilters = (colName, vals) => {
        if (colName in self.filters ) {
            self.filters[colName] = _.union(vals, self.filters[colName]);
        } else {
            self.filters[colName] = vals;
        }

        console.log('filters after add:', self.filters)
        self.applyFilters()
    }

    this.removeFilter = (colName, val) => {
        self.removeFilters(colName, [val]);
    }

    this.removeFilters = (colName, collection) => {
        if (!collection) {
            delete self.filters[colName];
            self.applyFilters()
            return;
        }


        var vals = self.filters[colName];
        var i = vals.length;
        while (i--) {
            if (collection.indexOf(vals[i]) != -1 ) {
                self.filters[colName].splice(i, 1);
            }
        }

        if (vals.length === 0 ) delete self.filters[colName];

        console.log('filters after remove:', self.filters)
        self.applyFilters()
    }


    this.getAllColStats = () => {
        return {
            types: getColStats(self.filtered, 'cancer_diagnosis'),
            institutes: getColStats(self.filtered, 'institution'),
            vendors: self.getVendorList(self.filtered),
            stages: getColStats(self.filtered, 'diagnosis'),
            status: getColStats(self.filtered, 'adequate_for_analysis')
        }
    }


    function getColStats(rows, col) {
        var hash = {};
        rows.forEach(row => {
            var item = row[col];

            if (item in hash) {
                hash[item]++;
            }
            else hash[item] = 1;
        })

        // get list of objects
        var list = [];
        for (var key in hash) {
            list.push({
                name: key,
                count: hash[key]
            })
        }

        return list;
    }

    this.getMinMaxTimes = (rows) => {
        var x = rows.map((s) => {
            var dateMDY = s.specimen_ship_date.split('/');
            var m = parseInt(dateMDY[0]) - 1,
                d =  parseInt(dateMDY[1]) - 1,
                y = parseInt(dateMDY[2]),
                timestamp = new Date(y, m, d).getTime();

            return timestamp;
        });

        return {
            min: Math.min.apply(Math, x),
            max: Math.max.apply(Math, x)
        }
    }

    /**
     * get hash of initutions and corresponding counts
     */
    this.getVendorList = (rows) => {
        var hash = {
            PGDX: 0,
            Personalis: 0
        };
        rows.forEach(row => {
            var id = row.vendor_sample_number;

            if (id.slice(0, 4) === "PGDX") hash.PGDX++;
            else hash.Personalis++;
        })

        // get list of objects
        var vendorList = [];
        for (var key in hash) {
            vendorList.push({
                name: key,
                count: hash[key]
            })
        }

        return vendorList;
    }



    this.getSamplesOverTime = () => {

        // get list of timestamps and sample counts
        var x = [], y = [], count = 0;

        var samples = []
        self.filtered.forEach(row => {

            //var dateMDY = row.specimen_date_of_arrival.split('/');
            var dateMDY = row.specimen_ship_date.split('/');
            var month = dateMDY[0] - 1,
                day = dateMDY[1] - 1,
                year =  dateMDY[2];
            var s = new Date(year, month, day).getTime();

            var dateMDY = row.date_results_returned_to_institution.split('/');
            var month = dateMDY[0] - 1,
                day = dateMDY[1] - 1,
                year =  dateMDY[2];
            var e = new Date(year, month, day).getTime();


            var state = row.adequate_for_analysis;
            var status;
            if (state === '-' || state === 'Y' || state === 'N')
                status = state;
            else
                alert('unknown option in "Adequate for Analysis":', state)

            samples.push({
                id: row.vendor_sample_number,
                timestamp: s,
                institution: row.institution,
                status: status,
                turnaround: e ? (e - s) : null
            })
        })

        // sort timestamps
        samples.sort((a, b) =>  a.timestamp - b.timestamp)

        return {data: samples};
    }

    this.setTimeFilters = (start, end) => {
        console.log('setting time filters:', start, end)
        self.minTimestamp = start;
        self.maxTimestamp = end;

        self.applyFilters();
    }


    /**
     * returns subset of this.data between start/end
     */
    this.filterByTime = (data, start, end) => {
        if (!start && !end)
            console.error('No filter times provided to Patients.filterByTime!');

        var s = Number.isInteger(start) ? start : Date.parse(start),
            e = Number.isInteger(end) ? end : Date.parse(end);

        var data = data.filter(row => {
            //var dateMDY = row.specimen_date_of_arrival.split('/');
            var dateMDY = row.specimen_ship_date.split('/');
            var m = parseInt(dateMDY[0]) - 1,
                d =  parseInt(dateMDY[1]) - 1,
                y = parseInt(dateMDY[2]),
                timestamp = new Date(y, m, d).getTime();

            if (s && e)
                return timestamp >= s  && timestamp <= e;
            else if (s)
                return timestamp > s;
            else if (e)
                return timestamp < e;
        })

        return data;
    }

    this.filterByInstitute = (instituteList) => {
        if (!instituteList.length) return self.data;

        var data = self.data.filter(row => instituteList.indexOf(row.institution) !== -1)

        return data;
    }


    this.filterByType = (rows, specimenList) => {
        if (!specimenList.length) return rows;

        var data = rows.filter(row => specimenList.indexOf(row.cancer_diagnosis) !== -1)

        return data;
    }


    this.filterByVendor = (rows, vendorList) => {
        if (!vendorList.length) return rows;

        var data = rows.filter(row => {
            if (vendorList.indexOf("PGDX") != -1 && row.vendor_sample_number.slice(0, 4) == 'PGDX')
                return true;
            if (vendorList.indexOf("Personalis") != -1 && row.vendor_sample_number.slice(0, 4) != 'PGDX')
                return true;
        })

        return data;
    }



    this.filterByStage = (rows, stageList) => {
        if (!stageList.length) return rows;

        var data = rows.filter(row => stageList.indexOf(row.diagnosis) !== -1)
        return data;
    }


    this.filterByStatus = (rows, status) => {


        var data = rows.filter(row => {
            var state = row.adequate_for_analysis;
            if (status == 'all') return true;
            else if (status == "adequate") return state == "Y";
            else if (status == "inadequate") return state == "N";
            else if (status == "outstanding") return state == "-";
        })

        return data;
    }


    this.getPatient = (id) => {
        return self.data.filter(row => row.vendor_sample_number.indexOf(id) !== -1)[0]
    }


    /**
     * Accross View Filtering
     */

    $rootScope.$on('Event.filteredAllViews', (e, ids) => {
        self.filterByIds(ids);
    })


    this.filterByIds = (ids) => {
        self.filtered = self.data.filter(row => ids.indexOf(row.vendor_sample_number) !== -1);
        $rootScope.$broadcast('PatientView.filterEvent', self.filtered.length)
    }

    this.clearFilters = () => {
        self.filters = {};
        self.filtered = self.data;
        $rootScope.$broadcast('PatientView.filterEvent', self.filtered.length)
    }

}])
