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 - 1)
      y = @rd.rand(0..@y_max - 1)
      [x, y]
    end
  end

  class Client

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

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

    def msg(msg)
      puts "reply #{ user_info[:nick_name]}"
      @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
      @rank = RankRoom.new
    end

    def set_client(client)
      unless @status == :waiting
        puts "game is staring now"
        puts "will close #{client.key}"
        client.ws.close
        return
      end
      @mutex.synchronize do
        @clients << client
        @clients.size - 1
      end
    end

    def start!
      @status = :start
    end

    def waiting!
      @status = :waiting
    end

    def leave_client(key)
      idx = @clients.find_index { |c| c.key == key}
      return if idx.nil?
      @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["default_room"]
      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:{
            }
        })
        room.waiting!
        puts ""
        puts "room now wating"
      end

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


    def game_ready_notice(room, ghost, picker)

      msg = {
          type: 'game_ready',
          rid: room.name,
          payload: []
      }

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

    def notice_all(room, msg)
      room.clients.each do |client|
        msg[:cid] = client.key
        client.msg(msg)
      end
    end

    def ready_message(client)
      message = {}
      message[:nick_name] = client.user_info[:nick_name]
      message[:avatar_url] = client.user_info[:avatar_url]
      message[:cid] = client.key
      message
    end

    def clicking(msg,cl)
      room = @room_pool["default_room"]
      room.clients.each do |client|
        msg['cid'] = client.key
        msg['payload']['t_cid'] = cl.key
        client.msg(msg)
      end
    end

    def moving(msg, cl)
      room = @room_pool["default_room"]
      room.clients.each do |client|
        msg['cid'] = client.key
        msg['payload']['t_cid'] = cl.key
        client.msg(msg)
      end
    end

    def entry(msg, client)
      room = @room_pool["default_room"]
      seat_index = room.set_client(client)

      puts seat_index
      puts client.key
      puts msg['payload']['nick_name']


      client.room = room
      client.user_info = {nick_name: msg['payload']['nick_name'], avatar_url: msg['payload']['avatar_url']}
      client.seat_index = seat_index

      msg['payload']['seat'] = seat_index
      msg['payload']['members'] = room.clients.map do |cl| cl.user_info end

      room.clients.each do |client|
        msg['cid'] = client.key
        msg['payload']['seat'] = client.seat_index
        client.msg(msg)
      end
    end


    class RankRoom
      def initialize
        @players = {}
      end

      def insert(key, role)
        if role == 'ghost'
          @players[key] = 0
        else
          @players[key] = 3
        end
      end

      def add_mark(key)
        @players[key] += 1
      end

      def less_mark(key)
        @players[key] -= 1
      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)