// [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010.
//
// ## Description
//
// This class implements vibratos.

Vex.Flow.Vibrato = (function() {
  function Vibrato() { this.init(); }
  Vibrato.CATEGORY = "vibratos";

  var Modifier = Vex.Flow.Modifier;

  // ## Static Methods
  // Arrange vibratos inside a `ModifierContext`.
  Vibrato.format = function(vibratos, state, context) {
    if (!vibratos || vibratos.length === 0) return false;

    var text_line = state.text_line;
    var width = 0;
    var shift = state.right_shift - 7;

    // If there's a bend, drop the text line
    var bends = context.getModifiers(Vex.Flow.Bend.CATEGORY);
    if (bends && bends.length > 0) {
      text_line--;
    }

    // Format Vibratos
    for (var i = 0; i < vibratos.length; ++i) {
      var vibrato = vibratos[i];
      vibrato.setXShift(shift);
      vibrato.setTextLine(text_line);
      width += vibrato.getWidth();
      shift += width;
    }

    state.right_shift += width;
    state.text_line += 1;
    return true;
  }

  // ## Prototype Methods
  Vex.Inherit(Vibrato, Modifier, {
    init: function() {
      var superclass = Vex.Flow.Vibrato.superclass;
      superclass.init.call(this);

      this.harsh = false;
      this.position = Vex.Flow.Modifier.Position.RIGHT;
      this.render_options = {
        vibrato_width: 20,
        wave_height: 6,
        wave_width: 4,
        wave_girth: 2
      };

      this.setVibratoWidth(this.render_options.vibrato_width);
    },

    setHarsh: function(harsh) { this.harsh = harsh; return this; },
    setVibratoWidth: function(width) {
      this.vibrato_width = width;
      this.setWidth(this.vibrato_width);
      return this;
    },

    draw: function() {
      if (!this.context) throw new Vex.RERR("NoContext",
        "Can't draw vibrato without a context.");
      if (!this.note) throw new Vex.RERR("NoNoteForVibrato",
        "Can't draw vibrato without an attached note.");

      var start = this.note.getModifierStartXY(Vex.Flow.Modifier.Position.RIGHT,
          this.index);

      var ctx = this.context;
      var that = this;
      var vibrato_width = this.vibrato_width;

      function 
(x, y) {
        var wave_width = that.render_options.wave_width;
        var wave_girth = that.render_options.wave_girth;
        var wave_height = that.render_options.wave_height;
        var num_waves = vibrato_width / wave_width;

        ctx.beginPath();

        var i;
        if (that.harsh) {
          ctx.moveTo(x, y + wave_girth + 1);
          for (i = 0; i < num_waves / 2; ++i) {
            ctx.lineTo(x + wave_width, y - (wave_height / 2));
            x += wave_width;
            ctx.lineTo(x + wave_width, y + (wave_height / 2));
            x += wave_width;
          }
          for (i = 0; i < num_waves / 2; ++i) {
            ctx.lineTo(x - wave_width, (y - (wave_height / 2)) + wave_girth + 1);
            x -= wave_width;
            ctx.lineTo(x - wave_width, (y + (wave_height / 2)) + wave_girth + 1);
            x -= wave_width;
          }
          ctx.fill();
        } else {
          ctx.moveTo(x, y + wave_girth);
          for (i = 0; i < num_waves / 2; ++i) {
            ctx.quadraticCurveTo(x + (wave_width / 2), y - (wave_height / 2),
              x + wave_width, y);
            x += wave_width;
            ctx.quadraticCurveTo(x + (wave_width / 2), y + (wave_height / 2),
              x + wave_width, y);
            x += wave_width;
          }

          for (i = 0; i < num_waves / 2; ++i) {
            ctx.quadraticCurveTo(
                x - (wave_width / 2),
                (y + (wave_height / 2)) + wave_girth,
                x - wave_width, y + wave_girth);
            x -= wave_width;
            ctx.quadraticCurveTo(
                x - (wave_width / 2),
                (y - (wave_height / 2)) + wave_girth,
                x - wave_width, y + wave_girth);
            x -= wave_width;
          }
          ctx.fill();
        }
      }

      var vx = start.x + this.x_shift;
      var vy = this.note.getYForTopText(this.text_line) + 2;

      renderVibrato(vx, vy);
    }
  });

  return Vibrato;
}());

renderVibrato