(function(jQuery) {
  jQuery.fn.reportGraph = function(options) {
    var defaults = {
      'graph': 'lines',
      'stack' : false,
      'legendLoc' : 'ne',
      'legendXOffset' : 0,
      'legendYOffset' : 0,
      'xaxisType' : 'string',
      'xaxisMin' : null,
      'xaxisMax' : null,
      'yaxisMin' : null,
      'yaxisMax' : null,
      'xaxisFormatString' : null,
      'yaxisFormatString' : null
    };

    var setting = jQuery.extend(defaults, options);
    var id = this.attr('id');
    
    var graph = _getGraph(setting);
    graph.draw(id);
    
    return this;
  };

  // Factory
  function _getGraph(setting) {
    if (setting.graph == 'lines') {
      return new LineGraph(setting);
    }
    if (setting.graph == 'bars') {
      return new BarGraph(setting);
    }
    if (setting.graph == 'pie') {
      return new PieGraph(setting);
    }
  }
  
  /**
   * Class: Graph
   */
  function Graph(setting) {
    this.setting = setting;
    
    this.data = this.getData();
    this.title = setting.title;
    this.stack = setting.stack;
    this.seriesDefaults = this.getSeriesDefaults(setting);
    this.series = this.getSeries(setting);
    this.legendOption = {
        show: true,
        location: setting.legendLoc,
        xoffset: setting.legendXOffset,
        yoffset: setting.legendYOffset
    };
    this.xaxisOption = {
        renderer: this.getXAxisRenderer(setting),
        min: this.getXAxisMin(setting),
        max: this.getXAxisMax(setting),
        ticks: this.getTicks(setting),
        numberTicks: this.getNumberTicks(setting),
        tickOptions: {formatString: setting.xaxisFormatString}
    };
    this.yaxisOption = {
        min: setting.yaxisMin,
        max: setting.yaxisMax,
        tickOptions: {formatString: setting.yaxisFormatString}
    };
  }
  
  Graph.prototype.draw = function(id) {
    jQuery.jqplot(id, this.data, {
      legend: this.legendOption,
      title: this.title,
      stackSeries: this.stack,
      seriesDefaults: this.seriesDefaults,
      series: this.series,
      axes: {
        xaxis: this.xaxisOption,
        yaxis: this.yaxisOption
      },
      highlighter: {
        sizeAdjust: 10
      },
      cursor: {show: true}
    });
  };
  
  Graph.prototype.getSeriesDefaults = function() {
    var seriesDefaults = {};
    seriesDefaults = {
      renderer: this.getRenderer(this.setting),
      rendererOptions:{
        barWidth: 10
      },
      fill: this.setting.stack && this.setting.graph == 'lines'
    }
    return seriesDefaults;
  };
  
  Graph.prototype.getRenderer = function() {
    return jQuery.jqplot.LineRenderer;
  };
  
  Graph.prototype.getData = function() {
    if (this.setting.xaxisType == 'date' || this.setting.xaxisType == 'number') {
      return _getData(this.setting);
    }
    return this.setting.data;
  };
  
  Graph.prototype.getTicks = function() {
    if (this.setting.xaxisType == 'date' || this.setting.xaxisType == 'number') {
      return []
    }
    return this.setting.ticks;
  };
  
  Graph.prototype.getNumberTicks = function() {
    var number =  this.setting.ticks.length;
    return number;
  };
  
  Graph.prototype.getXAxisRenderer = function() {
    if (this.setting.xaxisType == 'number') {
      return jQuery.jqplot.LinerAxisRenderer;
    }
    if (this.setting.xaxisType == 'date') {
      return jQuery.jqplot.DateAxisRenderer;
    }
    // string
    return jQuery.jqplot.CategoryAxisRenderer;
  };
  
  Graph.prototype.getXAxisMin = function() {
    if (this.setting.xaxisMin == null) {
      return this.setting.ticks[0];
    }
    return this.setting.xaxisMin;
  };
  
  Graph.prototype.getXAxisMax = function() {
    if (this.setting.xaxisMax == null) {
      return this.setting.ticks[this.setting.ticks.length-1];
    }
    return this.setting.xaxisMax;
  };
  
  Graph.prototype.getSeries = function() {
    var series = [];
    jQuery.each(this.setting.seriesLabel, function(index, value){
      // use default renderer.
      series.push({label: value})
    });
    return series;
  };
  
  /**
   * Class: BarGraph
   */
  function BarGraph(setting) {
    Graph.apply(this, arguments);
  }
  
  // extend Graph
  jQuery.extend(BarGraph.prototype, Graph.prototype);
 
  BarGraph.prototype.getRenderer = function() {
    return jQuery.jqplot.BarRenderer;
  };

  BarGraph.prototype.getSeriesDefaults = function() {
    var seriesDefaults = Graph.prototype.getSeriesDefaults.apply(this);
    seriesDefaults.rendererOptions = {
      barWidth: this.setting.barWidth
    };
    return seriesDefaults;
  };
  
  /**
   * Class: LineGraph
   */
  function LineGraph(setting) {
    Graph.apply(this, arguments);
  }
  
  // extend Graph
  jQuery.extend(LineGraph.prototype, Graph.prototype);

  LineGraph.prototype.getRenderer = function() {
    return jQuery.jqplot.LineRenderer;
  };
  
  /**
   * Class: PieGraph
   */
  function PieGraph(setting) {
    Graph.apply(this, arguments);
  }
  
  // extend Graph
  jQuery.extend(PieGraph.prototype, Graph.prototype);

  PieGraph.prototype.getRenderer = function() {
    return jQuery.jqplot.PieRenderer;
  };
  
  PieGraph.prototype.getData = function() {
    return _getData(this.setting);
  };
  
  PieGraph.prototype.getTicks = function() {
    return []
  };
  
  PieGraph.prototype.getXAxisRenderer = function() {
    return jQuery.jqplot.LinerAxisRenderer;
  };
  
  // Utility
  function _getData(setting) {
    var rtn = [];
    jQuery.each(setting.data, function(seriesIndex, seriesValue) {
      var newSeries = [];
      
      // caluculate sum for pie.
      var sum = 0;
      if (setting.graph == 'pie') {
        jQuery.each(seriesValue, function(dataIndex, dataValue) {
          sum += dataValue;
        });
      }
    
      // create series.
      jQuery.each(seriesValue, function(dataIndex, dataValue) {
        var newTick = setting.ticks[dataIndex];
        if (setting.graph == 'pie') {
          newTick += ': ' + dataValue + ' (' + Math.round((dataValue/sum)*100*100)/100 + '%)';
        }
        newSeries.push([newTick, dataValue])
      });
      rtn.push(newSeries);
    });
    return rtn;
  }
  
})(jQuery);

