define([
        'jquery', 'knockout', 'text!./Properties.html', 'flot', 'flotresize', 'flottime'
], function(jquery, ko, htmlString) {

    function SampleSource(prop, source, params) {
        params = params || {};

        this.source = source;
        this.path = prop.path;
        this.maxSamples = params.maxSamples || 100;

        this.samples = [];
        this.sample = function(timeStamp) {
            while (this.samples.length >= this.maxSamples) {
                this.samples.shift();
            }
            this.samples.push([
                    timeStamp, this.source()
            ]);
        }
    }

    function PropertySampler(params) {

        params = params || {};

        this.sources = {};
        this.sampleInterval = params.sampleInterval || 1000;

        this.start = function() {
            this.update(++this.updateId);
            return this;
        }

        this.stop = function() {
            ++this.updateId;
            return this;
        }

        this.addSource = function(source) {
            this.sources[source.path] = source;
            return this;
        }

        this.removeSource = function(source) {
            var s = this.sources[source].source;
            delete this.sources[source];
            return s;
        }

        this.containsSource = function(source) {
            return source in this.sources;
        }

        this.updateId = 0;
        this.update = function(id) {
            if (id != this.updateId)
                return;

            var now = Date.now();
            for ( var key in this.sources) {
                this.sources[key].sample(now);
            }

            var self = this;
            setTimeout(function() {
                self.update(id);
            }, self.sampleInterval);
        }

    }

    function PropertyViewModel(propertyPlotter) {
        var self = this;

        function load() {
            jquery.get('/json' + self.path, null, function(data) {
                self.hasChildren = data.nChildren > 0;
                self.index = data.index;
                self.type = data.type;
                if (typeof (data.value) != 'undefined') {
                    self.value(data.value);
                    self.hasValue = true;
                } else {
                    self.value('');
                    self.hasValue = false;
                }

                var a = [];
                if (data.children) {
                    data.children.forEach(function(prop) {
                        var p = new PropertyViewModel(propertyPlotter);
                        p.name = prop.name;
                        p.path = prop.path;
                        p.index = prop.index;
                        p.type = prop.type;
                        p.hasChildren = prop.nChildren > 0;
                        if (typeof (prop.value) != 'undefined') {
                            p.value(prop.value);
                            p.hasValue = true;
                        } else {
                            p.hasValue = false;
                        }
                        a.push(p);
                    });
                    self.children(a.sort(function(a, b) {
                        if (a.name == b.name) {
                            return a.index - b.index;
                        }
                        return a.name.localeCompare(b.name);
                    }));
                }

            });
        }
        self.name = '';
        self.value = ko.observable('');
        self.children = ko.observableArray([]);
        self.index = 0;
        self.path = '';
        self.hasChildren = false;
        self.hasValue = false;
        self.type = '';

        self.indexedName = ko.pureComputed(function() {
            if (0 == self.index)
                return self.name;
            return self.name + "[" + self.index + "]";
        });

        self.isExpanded = ko.observable(false);
        self.isExpanded.subscribe(function(newValue) {
            if (newValue) {
                load();
            } else {
                self.children.removeAll();
            }
        });

        self.isPlottable = ko.pureComputed(function() {
            return [
                    "double", "float", "int"
            ].indexOf(self.type) != -1;
        });

        self.toggle = function() {
            if (self.hasChildren) {
                self.isExpanded(!self.isExpanded());
            } else {
                load();
            }
        }

        self.togglePlot = function(prop, evt) {
            propertyPlotter.toggleProp(prop);
        }

        self.valueEdit = function(prop, evt) {
            var inplaceEditor = jquery(jquery('#inplace-editor-template').html());

            var elem = jquery(evt.target);
            elem.hide();
            elem.after(inplaceEditor);
            inplaceEditor.val(elem.text());
            inplaceEditor.focus();

            function endEdit(val) {
                inplaceEditor.remove();
                elem.show();

                if (typeof (val) === 'undefined')
                    return;
                var val = val.trim();
                elem.text(val);

                jquery.post('/json' + self.path, JSON.stringify({
                    value : val
                }));
            }

            inplaceEditor.on('keyup', function(evt) {
                switch (evt.keyCode) {
                case 27:
                    endEdit();
                    break;
                case 13:
                    endEdit(inplaceEditor.val());
                    break;
                }
            });

            inplaceEditor.blur(function() {
                endEdit(inplaceEditor.val());
            });
        }
    }

    function ViewModel(params) {
        var self = this;

        self.root = new PropertyViewModel(self);
        self.root.name = "root";
        self.root.path = "/";
        self.root.isExpanded(true);
        self.properties = self.root.children;

       self.startLabel = ko.pureComputed(function() {
            return self.running() ? "Pause" : "Start";
        });
    
        self.startIcons = ko.pureComputed(function() {
            return self.running() ? {
                primary : 'ui-icon-pause'
            } : {
                primary : 'ui-icon-play'
            };
        });

        self.settings = function() {
        }

        self.running = ko.observable(false);
        self.startPause = function() {
          if( self.running() ) {
            self.stop();
          } else {
            self.start();
          }
        }

        self.flotOptions = ko.observable({
            xaxes : [
                {
                    mode : "time"
                }
            ],
            yaxes : [
                    {
                        position : "right"
                    }, {
                        position : "left"
                    }

            ],
            legend : {
                show : true,
                labelFormatter: null,
                backgroundOpacity: 0.5,
                sorted: "ascending",
            },
            grid : {
                hoverable : false,
                backgroundColor: { colors: ["#eee", "#888"] }
            }
        });

        self.flotData = ko.observableArray([]);

        self.graphHover = function() {
        }

        self.hasGraphItems = ko.pureComputed(function() {
            return self.flotData().length > 0;
        });

        self.propertySampler = new PropertySampler({
            sampleInterval : 100,
        });

        self.propertySampler.start();
        self.running(true);

        self.toggleProp = function(prop) {

            if (self.propertySampler.containsSource(prop.path)) {
                var obs = self.propertySampler.removeSource(prop.path);
                ko.utils.knockprops.removeListener(prop.path, obs);
                return;
            }

            var obs = ko.observable(0);
            ko.utils.knockprops.addListener(prop.path, obs);
            self.propertySampler.addSource(new SampleSource(prop, obs, {
                maxSamples : 300,
            }));
        }

        self.updateId = 0;
        self.update = function(id) {

            if (self.updateId != id)
                return;

            var sources = self.propertySampler.sources;
            var data = [];

            var i = 1;
            for ( var key in sources) {
                var source = sources[key];
                data.push({
                    // color : 'rgb(192, 128, 0)',
                    data : source.samples,
                    label : key,
                    lines : {
                        show : true
                    },
                    points : {
                        show : false
                    },
                    bars : {
                        show : false
                    },
                    shadowSize : 0,
                    yaxis : i++,
                });
            }

            self.flotData(data);

            setTimeout(function() {
                self.update(id);
            }, 100);
        }

        self.start = function() {
          self.update(++self.updateId);
          self.propertySampler.start();
          self.running(true);
        }

        self.stop = function() {
          self.updateId++;
          self.propertySampler.stop();
          self.running(false);
        }

        self.start();

    }

    ViewModel.prototype.dispose = function() {
        console.log("disposing pal");
        this.propertySampler.stop();
        this.updateId++;
    }

    // Return component definition
    return {
        viewModel : ViewModel,
        template : htmlString
    };
});