define(['scripts/knockout-3.3.0','scripts/ACTComponent.js','scripts/css','scripts/dygraph-combined-dev','scripts/synchronizer.js'], function(ko, ACTComponent, css) {
	function DygraphSynchronizer(){
		this.availableGraphs_ = {};
		this.currentSynchronizers = {};
		this.synchronizerMaxId_ = 0;

		this.AddGraph = function (graph) {
			this.availableGraphs_[graph.Id] = graph;
		}

		this.SynchronizeGraphs = function(graphList, options) {
			if (!(graphList instanceof Array) || graphList.length < 2) {
				throw 'First argument must be an array of two or more DygraphComponentViewModel IDs.';
			}
			if (options == null) {
				options = {};
			}
			dygraphs = new Array();
			for (var i = 0; i < graphList.length; i++) {
				if (!this.availableGraphs_.hasOwnProperty(graphList[i])) {
					throw 'Unable to find DygraphComponentViewModel with Id ' + graph[i] +'.';
				}
				dygraphs[i] = this.availableGraphs_[graphList[i]].InnerDygraph;
			}
			var sync = Dygraph.synchronize(dygraphs, options);
			var newId = this.synchronizerMaxId_++;
			this.currentSynchronizers[newId] = sync;

			return newId;
		}

		this.Desynchronize = function(synchronizerId) {
			for (var id in this.currentSynchronizers) {
				if (id == synchronizerId &&
					this.currentSynchronizers.hasOwnProperty(id) &&
					this.currentSynchronizers[id] != undefined) {
					this.currentSynchronizers[id].detach();
					this.currentSynchronizers[id] = undefined;
				}
			}
		}
	}

	var dygraphSynchronizerForACT = new DygraphSynchronizer();

	function DygraphComponentViewModel(params, element){
		this.base = new ACTComponent($(element), params);
		this.element = element;

		this.title = ko.observable("");
		var graph;
		var graph2;
		var sync;

		this.base.onRefresh = function(obj){
			this.CreateDygraph(obj);
		}.bind(this);

		this.base.onMessage = function(name, obj) {
			if (name == "Synchronize"){
				this.SynchronizerId =
					dygraphSynchronizerForACT.SynchronizeGraphs(obj.SynchronizedGraphs, {range:false});
			}
			else if (name === "Detach"){
				dygraphSynchronizerForACT.Desynchronize(this.SynchronizerId);
			}
			else if (name === "UpdateOptions"){
				this.UpdateOptions(obj);
			}
			else if (name === "UpdateData"){
				this.UpdateData(obj);
			}
		}.bind(this)

		this.GetOptions = function(webObj) {
			// Global options
			var options = {
				title: webObj.Title,
				labels: webObj.Labels,
				ylabel:webObj.YLabel,
				y2label: webObj.Y2Label,
				legend: 'always',
				visibility: webObj.SeriesVisibility,
				showRangeSelector: webObj.ShowRangeSelector,
				rangeSelectorPlotFillColor: webObj.RangeSelectorPlotFillColor,
				rangeSelectorPlotStrokeColor: webObj.RangeSelectorPlotStrokeColor,
				series:{}
			};
			// Per series options.
			for (var i = 0; i < webObj.Series.length; i++) {
				options['series'][webObj.Series[i].Label] = {
					drawPoints: webObj.Series[i].DrawPoints,
					color: webObj.Series[i].Color
				}
			};
			// Per axis options.

			return options;
		}

		this.UpdateOptions = function(obj){
			if (this.InnerDygraph == null){
				this.CreateDygraph(obj);
				return;
			}
			this.InnerDygraph.updateOptions(this.GetOptions(obj));
		}

		this.UpdateData = function(obj){
			if (this.InnerDygraph == null){
				this.CreateDygraph(obj);
			}
			this.InnerDygraph.updateOptions({
				file: obj.Data,				
				visibility: obj.SeriesVisibility,
				labels:obj.Labels
			});
		}

		this.CreateDygraph = function(obj){
			var graphContainer = null;
			for (var i = 0; i < element.childNodes.length; i++) {
				if (element.childNodes[i].className === "graphContainer") {
					graphContainer = element.childNodes[i];
					break;
				}
			}
			if (graphContainer == null){
				throw 'Unable to find element of class graphContainer.';
			}
			if (obj.Data.length != 0)
			{
				var options = this.GetOptions(obj);
				this.InnerDygraph = new Dygraph(graphContainer,
					obj.Data, options);
				this.Id = obj.Id;
				dygraphSynchronizerForACT.AddGraph(this);
			}
		}

		this.base.initialize();
		params.ready(true);
	}

	return {
		createViewModel: function(params, componentInfo) {
			return new DygraphComponentViewModel(params, componentInfo.element);
		}
	};
});