var ColorWheel = function(parent, size) {
    var rgb = {
        red: 0,
        green: 0,
        blue: 0
    };
    var hsb = rgb2hsb(rgb.red, rgb.green, rgb.blue);
    var onChange = function(r,g,b,dragging) {};

    var canvasBg = document.createElement("canvas");
    $(canvasBg).attr("width", size);
    $(canvasBg).attr("height", size);
    var context = canvasBg.getContext('2d');
    if(context != null) {
        $(parent).append(canvasBg);

        var steps = 98;
        var barWidth = (size / steps) * 4;
        var barHeight = size * 0.11;
        for(var alpha = 0; alpha < 360; alpha += 360 / steps) {
            context.save();

            context.translate(size / 2, size / 2);
            context.rotate(toRadians(alpha));
            var rgb = hsb2rgb(alpha, 100, 80);
            context.fillStyle = "rgb("+rgb.red+","+rgb.green+","+rgb.blue+")";
            context.fillRect(-barWidth, -size / 2, barWidth, barHeight);

            context.restore();
        }
    }

    var canvasFg = document.createElement("canvas");
    $(canvasFg).css({
            position: "absolute",
            top: 0,
            left: 0
        });
    $(canvasFg).attr("width", size);
    $(canvasFg).attr("height", size);
    var contextFg = canvasFg.getContext('2d');
    if(contextFg != null) {
        $(parent).append(canvasFg);
    }

    var paintFg = function() {
        if(contextFg == null) return;
        contextFg.clearRect(0, 0, size, size);

        var x = size * 0.25 + ((size * 0.5) / 100) * hsb.saturation;
        var y = size * 0.25 + ((size * 0.5) / 100) * (100 - hsb.brightness);

        contextFg.moveTo(x, y);
        contextFg.beginPath();
        contextFg.arc(x, y, 2, 0, Math.PI*2, true);
        contextFg.strokeStyle = "#000";
        contextFg.stroke();

        contextFg.moveTo(x, y);
        contextFg.beginPath();
        contextFg.arc(x, y, 3, 0, Math.PI*2, true);
        contextFg.strokeStyle = "#fff";
        contextFg.stroke();

        var radius = size * 0.43;
        var alpha = hsb.hue;
        var beta = toRadians(90);
        var gamma = toRadians(180 - 90 - alpha);
        switch(alpha) {
        case 0:
          x = 0;
          y = -radius;
          break;
        case 90:
          x = radius;
          y = 0;
          break;
        case 180:
          x = 0;
          y = radius;
          break;
        case 270:
          x = -radius;
          y = 0;
          break;
        default:
          x = (radius * Math.sin(toRadians(alpha)) / Math.sin(beta));
          y = -(radius * Math.sin(gamma) / Math.sin(beta));
          break;
        }

        x += size / 2;
        y += size / 2;

        contextFg.moveTo(x, y);
        contextFg.beginPath();
        contextFg.arc(x, y, 2, 0, Math.PI*2, true);
        contextFg.strokeStyle = "#000";
        contextFg.stroke();

        contextFg.moveTo(x, y);
        contextFg.beginPath();
        contextFg.arc(x, y, 3, 0, Math.PI*2, true);
        contextFg.strokeStyle = "#fff";
        contextFg.stroke();
    }

    var paintSquare = function() {
        if(context == null) return;

        xy = size * 0.25;
        wh = size * 0.5;
        
        context.clearRect(xy, xy, wh, wh);

        var rgb = hsb2rgb(hsb.hue, 255, 255);

        context.fillStyle = "rgb("+rgb.red+","+rgb.green+","+rgb.blue+")";
        context.fillRect(xy, xy, wh, wh);

        var lingrad = context.createLinearGradient( size * 0.25, 0, size * 0.75, 0 );
        lingrad.addColorStop( 0, "rgba(255, 255, 255, 1)" );
        lingrad.addColorStop( 1, "rgba(255, 255, 255, 0)" );
        context.fillStyle = lingrad;
        context.fillRect(xy, xy, wh, wh);

        var lingrad = context.createLinearGradient( 0, size * 0.25, 0, size * 0.75 );
        lingrad.addColorStop( 0, "rgba(255,255,255, 0)" );
        lingrad.addColorStop( 1, "rgba(0, 0, 0, 1)" );
        context.fillStyle = lingrad;
        context.fillRect(xy, xy, wh, wh);

        paintFg();
    }

    paintSquare();

    var MODE_NONE      = 0;
    var MODE_SAT_BRI   = 1;
    var MODE_HUE       = 2;
    var mode = MODE_NONE;

    var setMode = function(x, y) {
        var so = size * 0.25;
        if (x > so && x < size - so && y > so && y < size - so) {
            mode = MODE_SAT_BRI;
        } else {
            var mx = x - size / 2;
            var my = y - size / 2;
            var z = Math.sqrt(Math.pow(mx, 2) + Math.pow(my, 2));
            if(z > size * 0.37 && z < size * 0.50) {
                mode = MODE_HUE;
            }
        } 
    }

    var handleEvent = function(x, y) {
        log.info("mode: "+mode);

        if(mode == MODE_SAT_BRI) {
            var so = size * 0.25;
            if(mode == MODE_SAT_BRI) {
                var mx = Math.max(Math.min(x - so, size * 0.5), 0);
                var my = Math.max(Math.min(y - so, size * 0.5), 0);

                hsb.saturation = Math.round((100 / (size * 0.5)) * mx);
                hsb.brightness = 100 - Math.round((100 / (size * 0.5)) * my);

                rgb = hsb2rgb(hsb.hue, hsb.saturation, hsb.brightness);
                paintFg();
            }

        }
        if (mode == MODE_HUE) {
            var mx = x - size / 2;
            var my = y - size / 2;
            var z = Math.sqrt(Math.pow(mx, 2) + Math.pow(my, 2));

            var hue;
            if(mx == 0) {
                if(my > 0) hue = 180;
                if(my < 0) hue = 0;
            } else if(y == 0) {
                if(mx > 0) hue = 90;
                if(mx < 0) hue = 270;
            } else {
                var alpha = toDegrees(Math.acos((mx * mx - z * z - my * my) / (-2 * z * my)));
                if(mx > 0) {
                  hue = 180 - alpha;
                } else {
                  hue = 180 + alpha;
                }
            } 
            hue = Math.round(hue);
            hsb.hue = hue;

            rgb = hsb2rgb(hsb.hue, hsb.saturation, hsb.brightness);
            paintSquare();
            paintFg();
        }
    }
    if($(parent).get(0).addEventListener != null) {
      $(parent).get(0).addEventListener("touchstart", touchHandler, false);
      $(parent).get(0).addEventListener("touchmove", touchHandler, false);
      $(parent).get(0).addEventListener("touchend", touchHandler, false);
      $(parent).get(0).addEventListener("touchcancel", touchHandler, false);
    }  

    $(parent).bind("mousedown", function(evt) {
        cancelBubblePreventDefault(evt);
        var x = evt.pageX - $(parent).offset().left;
        var y = evt.pageY - $(parent).offset().top;
        if (addPageOffsetToMouseEvents) {
			x += window.pageXOffset;
			y += window.pageYOffset;
		}

        setMode(x, y);
        handleEvent(x, y);
        onChange(rgb.red, rgb.green, rgb.blue, true);
    });

    $(parent).bind("drag", function(evt) {
        cancelBubblePreventDefault(evt);
        var x = evt.pageX - $(parent).offset().left;
        var y = evt.pageY - $(parent).offset().top;
        if (addPageOffsetToMouseEvents) {
			x += window.pageXOffset;
			y += window.pageYOffset;
		}

        handleEvent(x, y);
        onChange(rgb.red, rgb.green, rgb.blue, true);
    });

    $(parent).bind("mouseup", function(evt) {
        cancelBubblePreventDefault(evt);
        var x = evt.pageX - $(parent).offset().left;
        var y = evt.pageY - $(parent).offset().top;
        if (addPageOffsetToMouseEvents) {
			x += window.pageXOffset;
			y += window.pageYOffset;
		}
        
        if(mode == MODE_NONE) setMode(x, y);
        handleEvent(x, y);
        mode = MODE_NONE;
        onChange(rgb.red, rgb.green, rgb.blue, false);
    });

    this.setOnChange = function(f) {
        onChange = f;
    }

    this.setRed = function (red) {
        if(mode !== MODE_NONE) return;

        rgb.red = red;
        hsb = rgb2hsb(rgb.red, rgb.green, rgb.blue);

        paintSquare();
    }

    this.setGreen = function (green) {
        if(mode !== MODE_NONE) return;

        rgb.green = green;
        hsb = rgb2hsb(rgb.red, rgb.green, rgb.blue);

        paintSquare();
    }

    this.setBlue = function (blue) {
        if(mode !== MODE_NONE) return;

        rgb.blue = blue;
        hsb = rgb2hsb(rgb.red, rgb.green, rgb.blue);

        paintSquare();
    }

    this.setRGB = function (red, green, blue) {
        if(mode !== MODE_NONE) return;

        rgb.red = red;
        rgb.green = green;
        rgb.blue = blue;
        hsb = rgb2hsb(rgb.red, rgb.green, rgb.blue);

        paintSquare();
    }
}

