function drawIndicatorBand(chart, offset, begin, end, i, j) {
    var c = chart._context;
    c.fillStyle = chart._parent._style.bandColor[i<chart._parent._style.bandColor.length?i:0];
    c.beginPath();
    
    var start = begin;
    var cur = start;
    // line 1
    while(cur && cur != end.next) {
        if(cur.indicator[i] !== null) {
            x = pc(chart._getX(cur.index - offset.index));
            y = pc(chart._getY(cur.indicator[i]));
            
            if(cur == start) {
                c.moveTo(x,y);
            } else {
                c.lineTo(x,y);
            }
        } else {
            start = cur.next;
        }
        cur = cur.next;
    }
    // line 2 backwards
    if(!cur) {
        cur = end;
    }
    while(cur && cur != begin.prev) {
        x = pc(chart._getX(cur.index - offset.index));
        y = pc(chart._getY(cur.indicator[j]));
        
        c.lineTo(x,y);
        cur = cur.prev;
    }
    // back to start
    x = pc(chart._getX(start.index - offset.index));
    y = pc(chart._getY(start.indicator[i]));
    c.lineTo(x,y);
    
    c.fill();
};

function drawIndicatorLine(chart, offset, begin, end, i) {
    var c = chart._context;
    c.strokeStyle = chart._parent._style.indicatorColor[i<chart._parent._style.indicatorColor.length?i:0];
    c.beginPath();
    
    var start = begin;
    var cur = start;
    while(cur && cur != end.next) {
        if(cur.indicator[i] !== null) {
            x = pc(chart._getX(cur.index - offset.index));
            y = pc(chart._getY(cur.indicator[i]));
            
            if(cur == start) {
                c.moveTo(x,y);
            } else {
                c.lineTo(x,y);
            }
        } else {
            start = cur.next;
        }
        cur = cur.next;
    }
    
    c.stroke();
};

/**
* SMA
* simple moving average
*/
function IndicatorSMA() {
    this.values = 1;
    this.style = 'line';
    
    this.register = function() {
        this.param1 = parseInt(this.param1, 10);
        if(isNaN(this.param1) || this.param1 <= 0) {
            this.param1 = 20;
        }
    };
    
    this.unregister = function() {
    };
    
    this.calculate = function(value) {
        if(value.index < this.param1 - 1) {
            value.indicator[this.id[0]] = null;
            return;                            
        }
        
        var sum = 0;
        var cur = value;
        for(var i=0;i<this.param1;i++) {
            sum += cur.close;
            cur = cur.prev;
        }
        value.indicator[this.id[0]] = sum / this.param1;
    };
    
    this.draw = function(chart, offset, begin, end) {
        drawIndicatorLine(chart, offset, begin, end, this.id[0]);
    };
};

/**
* EMA
* exponential moving average
*/
function IndicatorEMA() {
    this.values = 1;
    this.style = 'line';
    
    this.wf = null;
    
    this.register = function() {
        this.param1 = parseInt(this.param1, 10);
        if(isNaN(this.param1) || this.param1 <= 0) {
            this.param1 = 12;
        }
        this.wf = 2/(this.param1+1);
    };
    this.unregister = function() {
    };
    
    this.calculate = function(value) {
        var res = 0;
        if(value.index == 0) {
            res = value.close;
        } else {
            res = value.prev.indicator[this.id] + this.wf * (value.close - value.prev.indicator[this.id]);
        }
        value.indicator[this.id[0]] = res;
    };
    
    this.draw = function(chart, offset, begin, end) {
        drawIndicatorLine(chart, offset, begin, end, this.id[0]);     
    };
};

/**
* TMA
* triple moving average
*/
function IndicatorTMA() {
    this.values = 1;
    this.style = 'line';
    
    this.register = function(source) {
        this.param1 = parseInt(this.param1, 10);
        if(isNaN(this.param1) || this.param1 <= 0) {
            this.param1 = 38;
        }
        
        this.m = Math.floor(this.param1 / 2);  
        this.n = this.m + (this.param1%2);
    };
    this.unregister = function() {
    };
    
    this.calculate = function(value) {
        if(value.index < this.m + this.n) {
            value.indicator[this.id[0]] = null;
            return;                            
        }
        
        var cur = value,cur2;
        var sum1,sum2;
        sum1 = 0;
        for(var i=0;i<this.n;i++) {
            
            cur2 = cur;
            sum2 = 0;
            for(var j=0;j<this.m;j++) {
                sum2 += cur2.close;
                cur2 = cur2.prev;
            }
            
            sum1 += sum2 / this.m;
            cur = cur.prev;
        }
        sum1 = sum1 / this.n;
        
        value.indicator[this.id[0]] = sum1;    
    };
    
    this.draw = function(chart, offset, begin, end) {
        drawIndicatorLine(chart, offset, begin, end, this.id[0]);
    };
};

/**
* WMA
* weighted moving average
*/
function IndicatorWMA() {
    this.values = 1;
    this.style = 'line';
    
    this.register = function() {
        this.param1 = parseInt(this.param1, 10);
        if(isNaN(this.param1) || this.param1 <= 0) {
            this.param1 = 10;
        }
    };
    this.unregister = function() {
    };
    
    this.calculate = function(value) {
        if(value.index < this.param1 - 1) {
            value.indicator[this.id[0]] = null;
            return;                            
        }
        
        var sum1 = 0,sum2 = 0;
        var cur = value;
        for(var i=0;i<this.param1;i++) {
            sum1 += cur.close * (this.param1-i);
            sum2 += (this.param1-i);
            cur = cur.prev;
        }
        
        value.indicator[this.id[0]] = sum1 / sum2;
    };
    
    this.draw = function(chart, offset, begin, end) {
        drawIndicatorLine(chart, offset, begin, end, this.id[0]);
    };
}; 

/**
* BB
* BollingerBand
*/
function IndicatorBB() {
    this.values = 2;
    this.style = 'band';
    
    this.sma = null;
    
    this.register = function(source) {
        this.param1 = parseInt(this.param1, 10);
        if(isNaN(this.param1) || this.param1 <= 0) {
            this.param1 = 10;
        }
        this.param2 = parseInt(this.param2, 10);
        if(isNaN(this.param2) || this.param2 <= 0) {
            this.param2 = 2;
        }
        this.param3 = parseInt(this.param3, 10);
        if(isNaN(this.param3) || this.param3 <= 0) {
            this.param3 = 2;
        }
        
        this.sma = source.addIndicator('sma', 10);
    };
    
    this.unregister = function(source) {
        source.removeIndicator(this.sma);
    };
    
    this.calculate = function(value) {
        if(value.index < this.param1 - 1) {
            value.indicator[this.id[0]] = null;    
            value.indicator[this.id[1]] = null;
            return;
        }
        
        var sma = value.indicator[this.sma.id];
        
        var sum = 0;
        var cur = value;            
        for(var j=0;j<this.param1;j++) {
            sum += Math.pow(cur.close-sma,this.param2);
            cur = cur.prev;
        }
            
        sum = Math.pow(sum / this.param1, 1/this.param2);
        
        value.indicator[this.id[0]] = sma - this.param3 * sum; 
        value.indicator[this.id[1]] = sma + this.param3 * sum;
    }
    
    this.draw = function(chart, offset, begin, end) {
        // draw sma
        drawIndicatorLine(chart, offset, begin, end, this.sma.id[0]);
        
        // draw band
        drawIndicatorBand(chart, offset, begin, end, this.id[0], this.id[1]);
    };
};