require 'pp'
require 'sinatra'
require 'json'
require 'geoutm'
require 'redis'
require 'erb'

set :public_folder, 'public'

class Visualizer < Sinatra::Base

  redis = Redis.new(:port => 7777)

  get '/' do
    "Hello"
  end

  get '/p/:key' do
    if (redis.exists(params[:key]))
      # usfeul for http://188.226.176.165:9292/p/58409833-normal-9
      #coords = compute_trigger(redis.lrange(params[:key], 70, 74))
      coords = compute_trigger(redis.lrange(params[:key], 0, 1000000))

      erb "index.html".to_sym, locals: {coords: coords, p: 1}
    else
      "Key not found"
    end
  end

  get '/:key' do
    if (redis.exists(params[:key]))
      coords = compute_trigger(redis.lrange(params[:key], 0, 10000))
      #coords = redis.lrange(params[:key], 0, 10000)
      erb "index.html".to_sym, locals: {coords: coords}
    else
      "Key not found"
    end
  end

  get '/:key1/:key2' do
    if (redis.exists(params[:key1]) && redis.exists(params[:key2]))
      coords1 = redis.lrange(params[:key1], 0, 10000)
      coords2 = redis.lrange(params[:key2], 0, 10000)

      erb "index.html".to_sym, locals: {coords1: coords1, coords2: coords2}
    else
      "Key not found"
    end
  end

  get 'lrange/:key/:lower/:upper' do
    if (redis.exists(params[:key]))
      coords = redis.lrange(params[:key], params[:lower].to_i, params[:upper].to_i)
      erb "index.html".to_sym, locals: {coords: coords}
    else
      "Key not found"
    end
  end

  private

  def compute_trigger(coords)
    eps = 0.1
    coords.reverse!

    result = coords.map.with_index do |coord, i|
      parsed_data = JSON.parse(coord)

      if parsed_data["predicted_position"] != {} && parsed_data["type"] == "requestModelUpdate"
        if i == 0
          next
        end

        previous_position = JSON.parse(coords[i-1])

        real_position      = parsed_data["position"]
        estimated_position = parsed_data["predicted_position"]
        previous_position  = previous_position["position"]

        real_position_utm      = GeoUtm::LatLon.new(real_position["latitude"], real_position["longitude"]).to_utm
        estimated_position_utm = GeoUtm::LatLon.new(estimated_position["latitude"], estimated_position["longitude"]).to_utm
        previous_position_utm  = GeoUtm::LatLon.new(previous_position["latitude"], previous_position["longitude"]).to_utm

        real_obj = {x: real_position_utm.e, y: real_position_utm.n}
        prev_obj = {x: previous_position_utm.e, y: previous_position_utm.n}
        esti_obj = {x: estimated_position_utm.e, y: estimated_position_utm.n}

        coefficient, delta_x, delta_y = find_coefficient(real_obj, prev_obj, esti_obj)
        #p "#{coefficient} - #{delta_x} - #{delta_y}"
        parsed_data["trigger"] = if coefficient > 1 + eps
                                   "v"
                                 elsif coefficient < 1 - eps
                                   "d"
                                 else
                                   "="
                                 end
        parsed_data
      else
        nil
      end
    end.compact
    result.map { |x| JSON.dump(x) }
  end

  def find_coefficient(real, origin, estimated)
    # p real
    # p origin
    # p estimated
    # center in the origin
    o = {x: 0.0, y: 0.0}
    r = {x: real[:x] - origin[:x],      y: real[:y] - origin[:y]}
    e = {x: estimated[:x] - origin[:x], y: estimated[:y] - origin[:y]}

    # coefficient of line belongings to estimated point
    m = e[:y]/e[:x].to_f

    # perpendicular to that line
    perp_m = - 1/m
    perp_q = r[:y] - perp_m * r[:x]

    # compute intersection
    # line1: y = m * x
    # line2: y = perp_m * x + perp_q
    x = (perp_q - 0)/(m - perp_m).to_f
    y = (m * perp_q - perp_m * 0)/(m - perp_m).to_f

    # intersection point
    i = {x: x, y: y}

    delta_y = Math.sqrt((i[:x] - e[:x])**2 + (i[:y] - e[:y])**2)
    delta_x = Math.sqrt((i[:x] - r[:x])**2 + (i[:y] - r[:y])**2)
    [delta_y / delta_x, delta_x, delta_y]
  end
end
