var tracks;
var flash;

var checkLength = 36

function loadTracks()
{
  Hash.ify(tracks = $$(".track").reject(function(e) { return e.hasClassName("item") }).collect(function(e) { return new Track(e)}))
  
  flash = DetectFlashVer(8,0,0)
  
  if ($("user_track"))
  {
    $("user_track").observe("change", function() {
      if ($("replace") && !$("replace").visible())
      {
        $("replace").appear().pulsate()
      }
    });
  }
}

Event.observe(window, "load", loadTracks)

var Track = Class.create({
  initialize: function(e) {
    this.id = e.id.split("_").last();
    
    this._over = false
    this.inScrubber = false
		this.scrubbing = false
		
    this.e = e
    this.a = e.getElementsBySelector("a").last()
    
    this.nameE = e.getElementsBySelector(".name").first()
    var m = this
    
    this.votesE = e.getElementsBySelector(".votes").first()
    
    this.votesE.getElementsBySelector("img").each(function(tick) {
      m.observeTick(tick)
    })
    
    this.selectorE = e.getElementsBySelector(".selector").first()
    
    if (this.selectorE)
    {
      this.selectorE.observe("click", this.selectored.bind(this))
    }
    
    if (document.location.href.indexOf("votes") > -1)
    {
      Event.observe(this.e, "mouseenter", function(e) {
        if (e.element().tagName != "IMG" && !e.element().hasClassName(".votes") && Track.votes() < Track.limit && !m.removed && m.protoVote == undefined)
        {
        m.entered = e
        m.protoVote = new Element("img", {src: "/images/check.png", border: 0, style: "writingMode: tb-rl; display: inline-block; "})
        m.protoVote.observe("click", m.setVote.bind(m))
        
      
        m.votesE.insert({"top": m.protoVote})
        
        m.protoVote.setOpacity(0.65)
        //m.protoVote.appear()
        m.added = true
        }
      
        m.removed = false
      })
    
      Event.observe(this.e, "mouseleave", function(e) {
        if (m.protoVote)
        {
          m.protoVote.remove()
          m.protoVote = undefined
        }
      })
    
      Event.observe(this.e, "click", function(e) {
        el = e.element()
        if (el.tagName == "IMG" && el.src.indexOf("tick") > -1)
        {
          // Ignore a click on a cross
          //console.log("ignored a click on a cross, eh")
          if (el != m.protoVote)
          {
            
          }
          else
          {
            m.setVote()
          }
        }
        else
        {
          if (m.protoVote)
          {
            m.setVote()
          }
        }
      })
    }
    
    this.buttonLength = 50
    
    this.paused = true;
    this._over = false;
    
    this.length = Number(this.a.getAttribute("length"))
    
    if (DetectFlashVer(8,0,0))
    {
      var length = this.buttonLength
      this.button = new Element("canvas", {width: length, height: length})
      this.e.insert({"top": this.button})
      
      if (this.button.getContext && !$("null")) // Detect canvas, eh
      {
        this.a.style.height = 0
        this.a.style.width = 0
        this.a.style.opacity = 0
        this.a.style.fontSize = "0px"
        

        Event.observe(this.button, "click", this.clicked.bind(this))
        Event.observe(this.button, "mousemove", this.move.bind(this))
        Event.observe(this.button, "mouseout", this.out.bind(this))
        Event.observe(this.button, "mousedown", this.down.bind(this))
        Event.observe(this.button, "mouseup", this.up.bind(this))
        Event.observe(this.button, "mousedrag", this.drag.bind(this))
        
        this.drawButton(length);
        
        this.player = $f(this.a, "/flowplayer-3.1.0.swf", {
  	      playlist: [this.a.getAttribute("href")],
  	      plugins: {controls: null},
  	      clip: {autoPlay: true}
  	    })
  	    
  	    this.player.onBeforeClick = this.start.bind(this)
      }
      else
      {
        $f(this.a, "/flowplayer-3.1.0.swf", {
  	      playlist: [this.a.getAttribute("href")],
  	      plugins: {controls: {fullscreen: false, height: 27}},
  	      clip: {autoPlay: true}
  	    })
      }
    }
  },
  
  start: function() {
    console.log("START")
  },
  
  setVote: function() {
    new Effect.Opacity(this.protoVote, { to: 1.0, from: 0.65, duration: 0.5});
    this.observeTick(this.protoVote)
    this.protoVote = undefined
  
    Track.submit()
    
  },
  
  observeTick: function(tick) {
    me = this
    Event.observe(tick, "mouseenter", function() { tick.src = tick.src.replace("check", "cross")})
    Event.observe(tick, "mouseout", function() { tick.src = tick.src.replace("cross", "check")})
    tick.stopObserving("click")
    Event.observe(tick, "click", function(e) { 
      me.removed = true
      tick.fade({afterFinish: function() {
        tick.remove()
        if (me.protoVote == tick)
        {
          me.protoVote = undefined
        } 
        else
        {
          Track.submit()
        }
      }})
    })
  },
  
  votes: function() {
    return this.votesE.getElementsBySelector("img").size()
  },
  
  selectored: function() {
    img = this.selectorE.getElementsBySelector("img")
    current = img.first().src.indexOf("tick") > -1
    
    new Ajax.Updater(this.selectorE, "/tracks/" + this.id, {parameters: {"track[selected]": !current}, method: "put"})
  },
  
  drawButton: function() {
    var length = this.buttonLength
  	var radius = length/2
  	var margin = 2
  	
  	var brown = "rgb(80,45,0)"
  	
    
    var ctx = this.button.getContext("2d")
    
    ctx.globalCompositeOperation = 'source-over'
    ctx.clearRect(0,0,length,length)
    
    ctx.save()

    
    //ctx.fillStyle = "white"
    //ctx.beginPath()
    //ctx.arc(length/2, length/2, radius, 0, Math.PI*2, false)
    //ctx.fill()
    
    ctx.lineWidth = 3
    
    this.playedRadius = radius - margin*2
    
    if (this.circle == undefined)
    {
      this.circle = new Circle(this.playedRadius, this.buttonLength/2, this.buttonLength/2)
    }

    var opacity = this._over ? 1 : 0.7
    
    buttonGradient = ctx.createLinearGradient(0, 0, 0, radius*2)
    buttonGradient.addColorStop(1, "rgba(80,45,0," + opacity + ")")
    buttonGradient.addColorStop(0, "rgba(181,132,63," + opacity + ")")
    
    
    
    ctx.fillStyle = buttonGradient
    ctx.beginPath()
    ctx.arc(length/2, length/2, radius - margin, 0, Math.PI*2, false)
    ctx.fill()
    
    playLength = radius/2.5
    
    if (this.paused)
    {
    ctx.fillStyle = "black"
    ctx.beginPath()
    ctx.moveTo(-1 + length/2 + playLength, length/2)
    ctx.lineTo(-1 + length/2 - playLength/2, length/2 + playLength/1.25)
    ctx.lineTo(-1 + length/2 - playLength/2, length/2 - playLength/1.25)
    ctx.closePath()
    ctx.fill()
    }
    else
    {
      space = 1
      ctx.fillStyle = "black"
      ctx.fillRect(-space + length/2 - playLength/2, length/2 - (playLength*1.5)/2, playLength/2, playLength*1.5)
      ctx.fillRect(space + length/2, length/2 - (playLength*1.5)/2, playLength/2, playLength*1.5)
    }
    
    // loading
    
    ctx.lineWidth = 2
    ctx.strokeStyle = brown
    ctx.beginPath()
    ctx.arc(length/2, length/2, radius -margin*2, -Math.PI/2, -Math.PI/2 + Math.PI*2*this.bp(), false)
    ctx.stroke()
    
    // played
    ctx.strokeStyle = "black"
    ctx.beginPath()
    ctx.arc(length/2, length/2, this.playedRadius, -Math.PI/2, -Math.PI/2 + Math.PI*2*this.p(), false)
    //ctx.arc(length/2, length/2, this.playedRadius, -Math.PI/2, -Math.PI/2 + Math.PI*2, false)
    ctx.stroke()
    
    if (this.p() > 0)
    {
      this.scrubberPoint = this.circle.fromT(this.p()*Math.PI*2 - Math.PI/2)
      this.scrubberCircle = new Circle(6, this.scrubberPoint.x, this.scrubberPoint.y)
      
      // scrubber
      ctx.fillStyle = brown
      ctx.beginPath()
      ctx.arc(this.scrubberPoint.x, this.scrubberPoint.y, 4, -Math.PI, Math.PI, false)
      ctx.fill()
      
      ctx.fillStyle = this.inScrubber ? brown : "black"
      ctx.beginPath()
      ctx.arc(this.scrubberPoint.x, this.scrubberPoint.y, 3, -Math.PI, Math.PI, false)
      ctx.fill()
    }
    
  },
  
  time: function() {
    return this.player ? (this.player.getStatus().time || 0) : 0;
  },
  
  buffered: function() {
    return this.player ? (this.player.getStatus().bufferEnd || 0) : 0;
  },
  
  p: function() {
    return this.time()/this.length;
  },
  
  bp: function() {
    return this.buffered()/this.length;
  },
  
  translateEvent: function(e) {
    return this.translate(new Point(e.pointerX(), e.pointerY()))
  },
  
  translate: function(p) {
    x = p.x - this.e.positionedOffset()[0]
    y = p.y - this.e.positionedOffset()[1]
    
    xMargin = 2
    yMargin = 5
    
    x += xMargin
    y += yMargin
    
    
    return new Point(x, y)

  },
  
  clicked: function(e) {
    if (!this.checkPosition(e) && this._over)
    {
      e.stop()
    if (this.paused)
    {
      this.player.play()
      
      me = this
      if (this.p() < 0.05)
      {
        setTimeout(function() {tracks.without(me).each(function(t) { t.player.stop();t.paused = true;t.drawButton()})}, 500)
      }
      
      this.timer = setInterval(function() { me.drawButton()}, 500)
    }
    else
    {
      this.player.pause()
      
      if (this.timer) 
      {
        clearInterval(this.timer)
        this.timer = undefined
      }
    }
    
    this.paused = !this.paused
    }
    
    this.player.unmute()
    
    
    this.drawButton()
    
    
  },
  
  move: function(e) {
    if (this.scrubbing) return this.drag(e)
    changed = false
    
    p = this.translateEvent(e)
    
    if (this.scrubberCircle != undefined && this.scrubberCircle.contains(p))
    {
      this.inScrubber = true
      changed = true
    }
    else
    {
      this.inScrubber = false
    }
    
    if (this.circle.contains(p))
    {
      if (!this._over) changed = true
      this._over = true
    }
    else
    {
      if (this._over) changed = true
      this._over = false
    }
    
    if (changed)
    {
      this.drawButton()
    }
  },
  
  out: function() {
    this._over = false
    this.inScrubber = false
    this.drawButton()
  },
  
  down: function(e) {
    p = this.translateEvent(e)
    if (this.scrubberCircle != undefined && this.scrubberCircle.contains(p))
    {
      this.scrubbing = true
      //console.log("YOU WANT TO SCRUB?!?!?")
    }
  },
  
  drag: function(e) {
    p = this.translateEvent(e)
    //console.log("dragged to " + p.to_s())
    
    proportion = (this.circle.tFromPoint(p) + Math.PI/2)/(Math.PI*2)
    
    if (p.x < this.circle.a) proportion = 1.0 - proportion
    
    //console.log("prop: " + proportion)
    this.player.seek(proportion*this.length)
  },
  
  up: function(e) {
    this.scrubbing = false
    e.stop()
  },
  
  checkPosition: function(e) {
    point = this.translateEvent(e)
    x = point.x
    y = point.y
    
    r = this.playedRadius
    
    
    expectedY = this.circle.fromX(x)
    
    if (point.absY().distance(new Point(x, expectedY)) < 3)
    {
      
      t = Math.asin((expectedY)/r)
      
      tp = (t/(Math.PI*2))
      
      if (x < 0)
      {
        if (y < 0)
        {
          p = 0.75 - tp
        }
        else
        {
          p = 0.75 + tp
        }
      }
      else
      {
        if (y < 0)
        {
          p = 0.25 + tp
        }
        else
        {
          p = 0.25 - tp
        }
      }
      
      
      if (p < this.bp())
      {
        this.player.seek(p*this.length)
      
        return true;
      }
    }
    else
    {
      return false
    }
  }
})

Track.limit = 10

Track.votes = function() {
  return tracks.collect(function(t) { return t.votes() }).sum()
}

Track.voteHash = function() {
  h = $H()
  
  tracks.each(function(t) {
    if (t.votes() > 0)
    {
      h.set(t.id, t.votes())
    }
  })
  
  return h
}

Track.submit = function() {
  if (Track.votes() > Track.limit)
  {
    $$("p").first().pulsate()
  }
  else
  {
    p = Track.voteHash().collect(function(pair) { return "user[vote][" + pair[0] + "]=" + pair[1]}).join("&") + "&authenticity_token=" + auth
    new Ajax.Request(
			"/user/votes/yes",
			{ parameters: p}
		);
	  
  }
}

var Point = Class.create({
  initialize: function(x, y) {
    this.x = x
    this.y = y
  },
  
  distance: function(p) {
    d = Math.sqrt(Math.pow(p.x - this.x, 2) + Math.pow(p.y - this.y, 2))
    
    //console.log("distance from " + this.to_s() + " to " + p.to_s() + ": " + d)
    
    return d
  },
  
  to_s: function() {
    return "(" + this.x + "," + this.y + ")"
  },
  
  absY: function() {
    return new Point(this.x, Math.abs(this.y))
  }
})

var Circle = Class.create({
  initialize: function(r, a, b)
  {
    if (a == undefined) a = 0
    if (b == undefined) b = 0
    
    this.a = a
    this.b = b
    
    this.r = r
    
    this.r2 = r*r
    
    this.centre = new Point(this.a, this.b)
  },
  
  fromX: function(x) {
    return Math.sqrt(Math.abs(this.r2 - Math.pow(x - this.a, 2))) + this.b
  },
  
  fromT: function(t) {
    return new Point(this.a + this.r*Math.cos(t), this.b + this.r*Math.sin(t))
  },
  
  tFromPoint: function(p) {
    innerCircle = new Circle(p.distance(this.centre), this.a, this.b)
    return Math.asin((p.y - this.b)/innerCircle.r)
  },
  
  contains: function(p) {
    return p.distance(this.centre) < this.r
  }
})