
COLLISIONWIDTH=8

class LinearCollider
  attr_reader :pointA, :pointB
  def initialize (pointA, pointB, mode,collisionWidth=COLLISIONWIDTH)
    @pointA = pointA
    @pointB = pointB
    @mode = mode
    @collisionWidth = collisionWidth

    if (@pointA.x > @pointB.x)
      @pointA, @pointB = @pointB, @pointA
    end

    @linearCollider_collision_once = false
  end

  def collisionSlope args
    if (@pointB.x-@pointA.x == 0)
      return INFINITY
    end
    return (@pointB.y - @pointA.y) / (@pointB.x - @pointA.x)
  end


  def collision? (args, points, ball=nil)

    slope = collisionSlope args
    result = false

    # calculate a vector with a magnitude of (1/2)collisionWidth and a direction perpendicular to the collision line
    vect=nil;mag=nil;vect=nil;
    if @mode == :both
      vect = {x: @pointB.x - @pointA.x, y:@pointB.y - @pointA.y}
      mag  = (vect.x**2 + vect.y**2)**0.5
      vect = {y: -1*(vect.x/(mag))*@collisionWidth*0.5, x: (vect.y/(mag))*@collisionWidth*0.5}
    else
      vect = {x: @pointB.x - @pointA.x, y:@pointB.y - @pointA.y}
      mag  = (vect.x**2 + vect.y**2)**0.5
      vect = {y: -1*(vect.x/(mag))*@collisionWidth, x: (vect.y/(mag))*@collisionWidth}
    end

    rpointA=nil;rpointB=nil;rpointC=nil;rpointD=nil;
    if @mode == :pos
      rpointA = {x:@pointA.x + vect.x, y:@pointA.y + vect.y}
      rpointB = {x:@pointB.x + vect.x, y:@pointB.y + vect.y}
      rpointC = {x:@pointB.x, y:@pointB.y}
      rpointD = {x:@pointA.x, y:@pointA.y}
    elsif @mode == :neg
      rpointA = {x:@pointA.x, y:@pointA.y}
      rpointB = {x:@pointB.x, y:@pointB.y}
      rpointC = {x:@pointB.x - vect.x, y:@pointB.y - vect.y}
      rpointD = {x:@pointA.x - vect.x, y:@pointA.y - vect.y}
    elsif @mode == :both
      rpointA = {x:@pointA.x + vect.x, y:@pointA.y + vect.y}
      rpointB = {x:@pointB.x + vect.x, y:@pointB.y + vect.y}
      rpointC = {x:@pointB.x - vect.x, y:@pointB.y - vect.y}
      rpointD = {x:@pointA.x - vect.x, y:@pointA.y - vect.y}
    end
    #four point rectangle



    if ball != nil
      xs = [rpointA.x,rpointB.x,rpointC.x,rpointD.x]
      ys = [rpointA.y,rpointB.y,rpointC.y,rpointD.y]
      correct = 1
      rect1 = [ball.x, ball.y, ball.width, ball.height]
      #$r1 = rect1
      rect2 = [xs.min-correct,ys.min-correct,(xs.max-xs.min)+correct*2,(ys.max-ys.min)+correct*2]
      #$r2 = rect2
      if rect1.intersect_rect?(rect2) == false
        return false
      end
    end


    #area of a triangle
    triArea = -> (a,b,c) { ((a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y))/2.0).abs }

    #if at least on point is in the rectangle then collision? is true - otherwise false
    for point in points
      #Check whether a given point lies inside a rectangle or not:
      #if the sum of the area of traingls, PAB, PBC, PCD, PAD equal the area of the rec, then an intersection has occurred
      areaRec =  triArea.call(rpointA, rpointB, rpointC)+triArea.call(rpointA, rpointC, rpointD)
      areaSum = [
        triArea.call(point, rpointA, rpointB),triArea.call(point, rpointB, rpointC),
        triArea.call(point, rpointC, rpointD),triArea.call(point, rpointA, rpointD)
      ].inject(0){|sum,x| sum + x }
      e = 0.0001 #allow for minor error
      if areaRec>= areaSum-e and areaRec<= areaSum+e
        result = true
        #return true
        break
      end
    end

    #args.outputs.lines << [@pointA.x, @pointA.y, @pointB.x, @pointB.y,     000, 000, 000]
    #args.outputs.lines << [rpointA.x, rpointA.y, rpointB.x, rpointB.y,     255, 000, 000]
    #args.outputs.lines << [rpointC.x, rpointC.y, rpointD.x, rpointD.y,     000, 000, 255]


    #puts (rpointA.x.to_s + " " +  rpointA.y.to_s + " " + rpointB.x.to_s + " "+ rpointB.y.to_s)
    return result
  end #end collision?

  def getRepelMagnitude (fbx, fby, vrx, vry, ballMag)
    a = fbx ; b = vrx ; c = fby
    d = vry ; e = ballMag
    if b**2 + d**2 == 0
      #unexpected
    end
    x1 = (-a*b+-c*d + (e**2 * b**2 - b**2 * c**2 + 2*a*b*c*d + e**2 + d**2 - a**2 * d**2)**0.5)/(b**2 + d**2)
    x2 = -((a*b + c*d + (e**2 * b**2 - b**2 * c**2 + 2*a*b*c*d + e**2 * d**2 - a**2 * d**2)**0.5)/(b**2 + d**2))
    err = 0.00001
    o = ((fbx + x1*vrx)**2 + (fby + x1*vry)**2 ) ** 0.5
    p = ((fbx + x2*vrx)**2 + (fby + x2*vry)**2 ) ** 0.5
    r = 0
    if (ballMag >= o-err and ballMag <= o+err)
      r = x1
    elsif (ballMag >= p-err and ballMag <= p+err)
      r = x2
    else
      #unexpected
    end
    return r
  end

  def collide args, ball
    slope = collisionSlope args

    # perpVect: normal vector perpendicular to collision
    perpVect = {x: @pointB.x - @pointA.x, y:@pointB.y - @pointA.y}
    mag  = (perpVect.x**2 + perpVect.y**2)**0.5
    perpVect = {x: perpVect.x/(mag), y: perpVect.y/(mag)}
    perpVect = {x: -perpVect.y, y: perpVect.x}
    if perpVect.y > 0 #ensure perpVect points upward
      perpVect = {x: perpVect.x*-1, y: perpVect.y*-1}
    end
    previousPosition = {
      x:ball.x-ball.velocity.x,
      y:ball.y-ball.velocity.y
    }
    yInterc = @pointA.y + -slope*@pointA.x
    if slope == INFINITY
      if previousPosition.x < @pointA.x
        perpVect = {x: perpVect.x*-1, y: perpVect.y*-1}
        yInterc = -INFINITY
      end
    elsif previousPosition.y < slope*previousPosition.x + yInterc #check if ball is bellow or above the collider to determine if perpVect is - or +
      perpVect = {x: perpVect.x*-1, y: perpVect.y*-1}
    end

    velocityMag = (ball.velocity.x**2 + ball.velocity.y**2)**0.5
    theta_ball=Math.atan2(ball.velocity.y,ball.velocity.x) #the angle of the ball's velocity
    theta_repel=Math.atan2(perpVect.y,perpVect.x) #the angle of the repelling force(perpVect)

    fbx = velocityMag * Math.cos(theta_ball) #the x component of the ball's velocity
    fby = velocityMag * Math.sin(theta_ball) #the y component of the ball's velocity

    #the magnitude of the repelling force
    repelMag = getRepelMagnitude(fbx, fby, perpVect.x, perpVect.y, (ball.velocity.x**2 + ball.velocity.y**2)**0.5)
    frx = repelMag* Math.cos(theta_repel) #the x component of the repel's velocity | magnitude is set to twice of fbx
    fry = repelMag* Math.sin(theta_repel) #the y component of the repel's velocity | magnitude is set to twice of fby

    fsumx = fbx+frx #sum of x forces
    fsumy = fby+fry #sum of y forces
    fr = velocityMag#fr is the resulting magnitude
    thetaNew = Math.atan2(fsumy, fsumx)  #thetaNew is the resulting angle
    xnew = fr*Math.cos(thetaNew)#resulting x velocity
    ynew = fr*Math.sin(thetaNew)#resulting y velocity
    if (velocityMag < MAX_VELOCITY)
      ball.velocity =  Vector2d.new(xnew*1.1, ynew*1.1)
    else
      ball.velocity =  Vector2d.new(xnew, ynew)
    end

  end
end
