require 'faye/websocket'
require 'rack'
require 'thin'
require 'json'

module CrazyFlirt

  class RandomKeyGenerator
    WORDS = %w(a b c d e f g h i j k l m n o p q r s t u v w x y z 1 2 3 4 5 6 7 8 9 0)
    SIZE = WORDS.size
    def self.key
      words = ::CrazyFlirt::RandomKeyGenerator::WORDS
      size = ::CrazyFlirt::RandomKeyGenerator::SIZE - 1
      random_key = ''
      random = Random.new
      5.times do
        random_key += words[random.rand(0..size)]
      end
      Time.now.to_i.to_s + random_key
    end
  end



  class RandomPicker

    def self.item_generate
      rd = Random.new
      x = @rd.rand(0..@x_max)
      y = @rd.rand(0..@y_max)
      [x, y]
    end

    def initialize(clients)
      @client = clients
      @x_max = 14
      @y_max = 10
      @rd = Random.new
    end

    def pick_ghost_index
      size = @client.size - 1
      @rd.rand(0..size)
    end

    def pick_born_point
      x = @rd.rand(0..@x_max)
      y = @rd.rand(0..@y_max)
      [x, y]
    end
  end

  class Client

    attr_reader :key
    attr_accessor :role, :room, :user_info

    def initialize(ws)
      @ws = ws
      @key = generate_key
      @role = nil
      @room = nil
    end

    def msg(msg)
      @ws.send(msg.to_json)
    end
    private

    def generate_key
      CrazyFlirt::RandomKeyGenerator.key
    end
  end


  class GameRoom

    attr_reader :clients, :name

    def initialize(name)
      @clients = []
      @name = name
      @status = :waiting
      @mutex = Mutex.new
    end

    def set_client(client)
      return unless @status == :waiting
      @mutex.synchronize do
        @clients << client
        @clients.size - 1
      end
    end

    def start!
      @status = :start
    end

    def leave_client(key)
      idx = @clients.find_index { |c| c.key == key}
      @clients.delete_at(idx)
    end

  end

  class SocketEngine

    def initialize
      Faye::WebSocket.load_adapter('thin')
      @default_room = "default_room"
      @faye = Faye::WebSocket
      @socket_pool = {}
      @room_pool = {}
      @room_pool[@default_room] = GameRoom.new(@default_room)
    end


    def call(env)
      return unless @faye.websocket?(env)
      ws = @faye.new(env)
      client = ::CrazyFlirt::Client.new(ws)
      @socket_pool[client.key] = client

      ws.on :message do |event|
        message = JSON.parse(event.data)
        seq = message['type']
        puts 'seq is nil' if seq.nil?
        next if seq.nil?
        command(seq, message, client)
      end

      ws.on :close do |event|
        puts "leave key #{client.key}"
        @socket_pool.delete(client.key)
        p client.room.nil?
        client.room.leave_client(client.key) unless client.room.nil?
        client = nil
        puts 'closed'
      end
      ws.on(:error) do |event|
        puts event.code
        ws.close()
      end

      ws.rack_response
    end

    private

    def command(command, *args)
      send(command, *args)
    end

    def start(msg,cl)
      room = @room_pool[msg['rid']]
      room.start!
      picker = RandomPicker.new(room.clients)
      idx = picker.pick_ghost_index
      ghost = room.clients[idx]
      game_ready_notice(room, ghost, picker)
      timer(room)
    end


    def timer(room)
      @thread = Thread.new do
        puts 'start'
        sleep(30)
        puts 'notice'
        notice_all(room, {
            type: 'game_end',
            rid: room.name,
            payload:{
            }
        })
      end

      @item_thread = Thread.new do
        5.times do
          x,y = RandomPicker.item_generate
          notice_all(room,{
              type: 'item_refresh',
              rid: 'default_room',
              payload:{
                  x:x,
                  y:y
              }
          })
          sleep(5)
        end
      end
    end


    def game_ready_notice(room, ghost, picker)
      room.clients.each do |client|
        msg = ready_message(room, client)
        if ghost.key == client.key
          msg[:payload][:role] = 'ghost'
        else
          x, y = picker.pick_born_point
          msg[:payload][:x] = x
          msg[:payload][:y] = y
          msg[:payload][:role] = 'runner'
        end

        notice_all(room, msg)
      end
    end

    def notice_all(room, msg)
      room.clients.each do |client|
        client.msg(msg)
      end
    end

    def ready_message(room, client)
      {
          type: 'game_ready',
          cid: client.key,
          rid: room.name,
          payload: {
              nick_name: client.user_info['nick_name'],
              avatar_url: client.user_info['avatar_url'],
          }
      }
    end

    def clicking(msg,cl)
      room = @room_pool[msg['rid']]
      room.clients.each do |client|
        client.msg(msg)
      end
    end

    def moving(msg,cl)
      room = @room_pool[msg['rid']]
      room.clients.each do |client|
        client.msg(msg)
      end
    end

    def entry(msg, client)
      room = @room_pool[msg['rid']]
      seat_index = room.set_client(client)
      client.room = room
      client.user_info = {nick_name: msg['payload']['nick_name'], avatar_url: msg['payload']['avatar_url']}
      msg['cid'] = client.key
      msg['payload']['seat'] = seat_index
      msg['payload']['members'] = room.clients.map do |cl| cl.user_info end
      room.clients.each do |client|
        client.msg(msg)
      end
    end

    # def create_room(msg)
    #   client_id = msg['cid']
    #   key = "room_#{::CrazyFlirt::RandomKeyGenerator.key}"
    #   room = GameRoom.new(key)
    #   @room_pool[key] = room
    #   client = @socket_pool[client_id]
    #   room.set_client(client)
    #   client.msg(room_id: key)
    # end
  end
end

Rack::Handler.get('thin').run(CrazyFlirt::SocketEngine.new, Host: '192.168.50.118', Port: 9090)