class GamesController < ApplicationController
  before_action :require_login
  before_action :find_game
  before_action :find_shixun, only: [:show, :answer, :rep_content, :choose_build, :game_build, :game_status]

  before_action :allowed

  #require 'iconv'

  include GamesHelper
  include ApplicationHelper

  def show
    uid_logger("--games show start")
    # 防止评测中途ajaxE被取消;3改成0是为了处理首次进入下一关的问题
    @game.update_attribute(:status, 0) if @game.status == 1
    @game.update_attributes(status: 0, open_time: Time.now) if @game.status == 3
    game_challenge = Challenge.base_attrs.find(@game.challenge_id)

    # 选择题类型的实训关卡总分
    @st = game_challenge.st
    if @st == 1
      game_challenge.score = game_challenge.choose_score.to_i
    end

    game_count = Game.where(myshixun_id: @game.myshixun_id).count

    discusses = @shixun.discusses
    discusses = discusses.where('hidden = false OR user_id = :user_id', user_id: current_user.id) unless current_user.admin?
    discusses_count = discusses.count

    @user = @game.owner
    is_teacher = @user.is_teacher?

    # 实训超时设置
    time_limit = @shixun.exec_time

    # 上一关、下一关
    prev_game = @game.prev_of_current_game(@shixun.id, @game.myshixun_id, game_challenge.position)
    next_game = @game.next_of_current_game(@shixun.id, @game.myshixun_id, game_challenge.position)

    # 关卡点赞数, praise_or_tread = 1则表示赞过
    praise_count = PraiseTread.where(praise_tread_object_id: game_challenge.id, praise_tread_object_type: "Challenge",
                                     praise_or_tread: 1).count
    user_praise = PraiseTread.where(praise_tread_object_id: game_challenge.id, praise_tread_object_type: "Challenge",
                                    user_id: current_user.id, praise_or_tread: 1).present? ? true : false

    # 实训的最大评测次数,这个值是为了优化查询,每次只取最新的最新一次评测的结果集
    max_query_index = @game.query_index.to_i

    # 统计评测时间
    record_onsume_time = EvaluateRecord.where(game_id: @game.id).first.try(:consume_time)

    # power判断用户是否有权限查看隐藏测试集(TPM管理员;平台认证的老师;花费金币查看者)
    # myshixun_manager
    myshixun_manager = current_user.manager_of_shixun?(@shixun) || (current_user.is_teacher? && current_user.pro_certification?)

    # 选择题和编程题公共部分
    @base_date = {st: @st, discusses_count: discusses_count, game_count: game_count, myshixun: @myshixun,
                  challenge: game_challenge.attributes.except("answer"), game: @game.try(:attributes), shixun: @shixun.try(:attributes),
                  record_onsume_time: record_onsume_time, prev_game: prev_game, next_game: next_game,
                  praise_count: praise_count, user_praise: user_praise, time_limit: time_limit,
                  tomcat_url: edu_setting('cloud_tomcat_php'), is_teacher: is_teacher,
                  myshixun_manager: myshixun_manager}

    # 区分选择题和编程题,st:0编程题;
    if @st == 0
      has_answer = game_challenge.challenge_answers.size == 0 ? false : true
      game_challenge.answer = nil
      mirror_name = @shixun.mirror_name

      # 判断tpm是否修改了
      begin
        tpm_modified = @myshixun.repository_is_modified(@shixun.repo_path) # 判断TPM和TPI的版本库是否被改了
      rescue
        uid_logger("实训平台繁忙,繁忙等级(81)")
      end
      tpm_cases_modified = (game_challenge.modify_time != @game.modify_time ? true : false)  # modify_time 决定TPM测试集是否有更新

      @task_result = {tpm_modified: tpm_modified, tpm_cases_modified: tpm_cases_modified, mirror_name: mirror_name, has_answer: has_answer}

      testset_detail max_query_index, game_challenge
    else # 选择题类型的
      # 该方法多个地方调用,比如show、评测
      # 最后一个字段true表示只显示数据,false表示可能有添加数据
      choose_container(game_challenge, @game, max_query_index)
    end
  end

  # 查看效果
  # todo : 这块代码有很大的改进空间
  # todo : 中文排序问题
  def picture_display
    myshixun = Myshixun.find(@game.myshixun_id)
    if myshixun.main_mirror.try(:type_name) == "Android"
      @type = "qrcode"
      workspace = @game.try(:picture_path)
      game_challenge = @game.challenge
      qr = RQRCode::QRCode.new("#{edu_setting('host_name')}/api/shixuns/download_file?file_name=#{workspace}/#{game_challenge.picture_path}/manual-ok.apk", :size => 12, :level => :h)
      @qrcode_str = Base64.encode64( qr.to_img.resize(400,400).to_s )

    else
      @type = "image"
      #conv = Iconv.new("GBK", "utf-8")
      @game_challenge = @game.challenge
      type = @game_challenge.show_type
      workspace_path = @game.try(:picture_path)
      @answer_path = "#{Rails.root}/#{workspace_path}/#{@game_challenge.expect_picture_path}"
      @user_path = "#{Rails.root}/#{workspace_path}/#{@game_challenge.picture_path}"
      @original_path = "#{Rails.root}/#{workspace_path}/#{@game_challenge.original_picture_path}"

      @answer_picture = @game_challenge.expect_picture_path.nil? ? [] : get_dir_filename(@answer_path, type, @game.id)
      #@answer_picture = @answer_picture.sort {|x, y| conv.iconv(x) <=> conv.iconv(y)} if @answer_picture.present?

      @user_picture = @game_challenge.picture_path.nil? ? [] : get_dir_filename(@user_path, type, @game.id)
      #@user_picture = @user_picture.sort {|x, y| conv.iconv(x) <=> conv.iconv(y)}
      if @game_challenge.original_picture_path.blank?
        @orignal_picture = nil
      else
        @orignal_picture = @game_challenge.original_picture_path.nil? ? [] : get_dir_filename(@original_path, type, @game.id)
        #@orignal_picture = @orignal_picture.sort {|x, y| conv.iconv(x) <=> conv.iconv(y)}
      end
    end
  end

  # 同步更新最新代码
  # 同步完成后,需要更新myshixun的commit_id为实训的commit最新值
  # --------------------------
  # 新思路,有冲突则则重置,没有冲突直接pull
  # identifier为password---
  # --------------------------
  # todo: TPI弹框的立即更新
  def sync_codes
    shixun_tomcat = edu_setting('cloud_bridge')
    begin
      git_myshixun_url = repo_ip_url @myshixun.repo_path
      git_shixun_url = repo_ip_url @myshixun.shixun.try(:repo_path)
      git_myshixun_url = Base64.urlsafe_encode64(git_myshixun_url)
      git_shixun_url = Base64.urlsafe_encode64(git_shixun_url)
      # todo: identifier 是以前的密码,用来验证的,新版如果不需要,和中间层协调更改.
      params = {tpiID: "#{@myshixun.try(:id)}", tpiGitURL: "#{git_myshixun_url}", tpmGitURL: "#{git_shixun_url}",
                identifier: "xinhu1ji2qu3"}
      uri = "#{shixun_tomcat}/bridge/game/resetTpmRepository"
      res = uri_post uri, params
      if (res && res['code'] != 0)
        tip_exception("实训云平台繁忙(繁忙等级:95)")
      end
      shixun_new_commit = GitService.commits(repo_path: @myshixun.shixun.repo_path).first["id"]
      @myshixun.update_attributes!(commit_id: shixun_new_commit, reset_time: @myshixun.shixun.try(:reset_time))
      if @game.challenge.st == 0 && @game.challenge.path.present?
        paths = @game.challenge.path.split(";")
        paths.each do |path|
          game_code_init @game.id, path.try(:strip)
        end
      end
      @path = @game.challenge.path
      # 更新完成后,弹框则隐藏不再提示
      @myshixun.update_column(:system_tip, false)
    rescue Exception => e
      tip_exception("立即更新代码失败!#{e.message}")
    end
  end

  ## 给关卡打星星
  def star
    shixun = Shixun.select([:id, :averge_star, :status]).where(id: params[:shixun_id]).first
    grades = Grade.where(user_id: current_user.id, container_id: @game.id, container_type: 'Star')
    if grades.exists?
      tip_exception("您已经评价过该实训")
    else
      @game.update_column(:star, params[:star].to_i)
      # 更新实训平均星星数值
      averge_star = Game.find_by_sql("select ifnull(sum(g.star),0)/ifnull(count(*),1) as averge_star from (games g left join
                                     (myshixuns m join shixuns s on s.id = m.shixun_id) on m.id = g.myshixun_id)
                                     where star != 0 and s.id = #{shixun.id}").first.try(:averge_star)
      averge_star = averge_star.to_f || 5
      shixun.update_column(:averge_star, averge_star.round(1))
      # 随机生成10-100金币作为奖励
      @gold = 0
      # 加积分只针对已发布的实训
      if shixun.status >= 2
        @gold = rand(10..100)

        RewardGradeService.call(current_user, container_id: @game.id, container_type: 'Star', score: @gold)
      end
    end
  end

  ## 代码文件目录结构
  def git_entries
    gpid = params[:gpid]
    @path = params[:path].try(:strip)
    rev = params[:rev] ? params[:rev] : "master"
    @trees = @g.trees(gpid, path: @path, rev: rev)
    unless @trees.count
      tip_exception("版本库异常")
    end
  end

  ## 是否可以查看答案,如果是管理员或者系统认证的老师则直接查看,不需要弹框
  def answer
    challenge = Challenge.select([:answer, :id, :score, :st]).find(@game.challenge_id)
    # 这几种情况可以直接查看答案的:实训未发布;当前用户为实训管理员;已经查看过答案;平台认证的老师;
    @allowed = @shixun.status < 2 || @game.answer_open == 1 || current_user.shixun_identity(@shixun) <= User::EDU_CERTIFICATION_TEACHER
    uid_logger("-- is manager #{current_user.manager_of_shixun?(@shixun)}")

    @result = challenge.st == 0 ? challenge.try(:answer) : challenge.choose_answer
  end

  # 获取答案
  # GET: /tasks/:identifier/get_answer_info
  # 0 直接查看答案, 1 查看答案弹框, 2 答案详情弹框
  def get_answer_info
    challenge = @game.challenge
    @challenge_answers  = challenge.challenge_answers
    # 平台已认证的老师需要控制
    @power = (@identity < User::EDU_GAME_MANAGER)
    if !@power
      if @challenge_answers.size == 0
        tip_exception("无答案可以查看")
      elsif @challenge_answers.size == 1
        # 未看答案,提示弹框
        if @game.answer_open == 0
          tip_exception(1, {answer_id: @challenge_answers.first.id, answer_score:@challenge_answers.first.score})
        end
      else
        if @game.answer_open == 0
          tip_exception(2, @challenge_answers.map{|a| {answer_id: a.id, answer_name: a.name, answer_score:a.score}})
        end
      end
    end

  end

  # 解锁答案
  # GET: /tasks/:identifier/get_answer_info?answer_id=?
  def unlock_answer
    @answer = ChallengeAnswer.find(params[:answer_id])
    challenge = @answer.challenge
    # 解锁需要本层级的答案是否需要扣分
    points = challenge.challenge_answers.where(level: @game.answer_open+1..@answer.level).sum(:score)
    deduct_score = ((points / 100.0) * challenge.score).to_i
    uid_logger("############金币数目: #{current_user.grade}")
    unless current_user.grade.to_i - deduct_score > 0
      tip_exception("您没有足够的金币")
    end

    ActiveRecord::Base.transaction do
      begin
        # 积分消耗情况记录
        score = challenge.st.zero? ? -deduct_score : -challenge.choose_score.to_i
        RewardGradeService.call(current_user, container_id: @answer.id, container_type: 'Answer', score: score)

        # 通关查看答案 不扣 得分
        if @game.status == 2
          @game.update_attributes!(:answer_open => @answer.level)
        else
          @game.update_attributes!(:answer_open => @answer.level, :answer_deduction => deduct_score)
        end

      rescue Exception => e
        uid_logger_error("#######金币扣除异常: #{e.message}")
        raise ActiveRecord::Rollback
      end
    end

  end

  # 查看答案需要扣取金币
  # 必须保证用户的金币数大于关卡的金币数
  def answer_grade
    challenge = Challenge.select([:answer, :id, :score, :st]).find(@game.challenge_id)
    challenge_score = challenge.try(:score)
    final_score = @game.final_score
    @allowed_viewed = current_user.grade.to_i - challenge_score > 0
    unless @allowed_viewed
      tip_exception("您没有足够的金币")
    end
    ActiveRecord::Base.transaction do
      begin
        if @game.answer_open == 0 # 如果这是第一次查看答案
          if challenge.st == 0
            @final_score = final_score - challenge_score
            # 积分消耗情况记录
            RewardGradeService.call(
              current_user,
              container_id: @game.id,
              container_type: 'Answer',
              score: -challenge_score
            )
          else
            @final_score = final_score - challenge.choose_score.to_i
            # 之所以不用final_score是因为过关后查看答案的final_score为0,但是记录需要记录扣除的分数
            RewardGradeService.call(
              current_user,
              container_id: @game.id,
              container_type: 'Answer',
              score: -challenge.choose_score.to_i)
          end
          @game.update_attributes!(:answer_open => true, :final_score => final_score)
        end
        if challenge.st == 0
          @answer = challenge.try(:answer)
        else
          @answer = challenge.choose_answer
        end
        @answer =
        # 更新当前用户的总金币数
        @grade = User.where(:id => @game.user_id).pluck(:grade).first
      rescue Exception => e
        uid_logger_error("#######奖励金币异常: #{e.message}")
        raise ActiveRecord::Rollback
      end
    end

  end

  # 查看隐藏测试集
  # REDO:有漏洞,通过game详情可以看到隐藏的测试集
  def check_test_sets
    challenge = Challenge.select([:id, :score]).find(@game.challenge_id)
    user_grade = current_user.grade
    @minus_grade = challenge.score * 5
    @allowed_viewed = user_grade >= @minus_grade
    if @allowed_viewed
      current_user.update_attribute(:grade, user_grade - @minus_grade)
      @game.update_attribute(:test_sets_view, true)
      # 扣分记录
      Grade.create(:user_id => current_user.id, :container_id => @game.id, :score => -@minus_grade, :container_type => "testSet")
      @status = 0
      @message = "解锁成功!"
    else
      @status = -1
      @message = "本操作需要扣除#{ @minus_grade }金币,您的金币不够了"
    end
  end

  # # 文件更新;数据评测记录
  # # 生成重新评测认证码
  # # content_modified:0 表示文件没有更新;content_modified:1 表示文件有更新
  # def file_update
  #   path = params[:path].strip unless params[:path].blank?
  #   myshixun = @game.myshixun
  #   rev = params[:rev] ? params[:rev] : "master"
  #   @content_modified = 0
  #   # params[:evaluate] 实训评测时更新必须给的参数,需要依据该参数做性能统计,其它类型的更新可以跳过
  #   # 自动保存的时候evaluate为0;点评测的时候为1
  #   if params[:evaluate] == 1
  #     record = EvaluateRecord.create!(:user_id => current_user.id, :shixun_id => myshixun.shixun_id, :game_id => @game.id)
  #     uid_logger("-- game is #{@game.id}, record id is #{record.id}, time is **** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}")
  #     student_work_time = format("%.3f",  (Time.now.to_f - record.created_at.to_f)).to_f
  #     record.update_attributes!(:student_work => student_work_time)
  #   end
  #   # 远程版本库文件内容
  #   last_content = GitService.file_content(repo_path: @repo_path, path: path)["content"]
  #   last_content = tran_base64_decode64(last_content)
  #
  #   content =  if @myshixun.mirror_name.select{|a| a.include?("MachineLearning") || a.include?("Python")}.present? && params[:content].present?
  #                params[:content].gsub(/\t/, '    ')
  #              else
  #                params[:content]
  #              end
  #   if content != last_content
  #     @content_modified = 1
  #     code_file = @g.edit_file(myshixun.gpid, current_user.login, :content => content, :file_path => path,
  #                              :branch_name => rev, :commit_message => params[:evaluate] == 0 ? "auto commit" : "task commit")
  #     uid_logger("-- file update #{code_file}")
  #     # REDO:更新失败的处理
  #     raise("文件更新失败") unless code_file
  #   end
  #
  #   if record.present?
  #     consume_time = format("%.3f",  (Time.now.to_f - record.created_at.to_f)).to_f
  #     record.update_attributes!(:file_update => consume_time)
  #   end
  #
  #   # status为2说明是重新评测
  #   if @game.status == 2
  #     code = CODES.sample(8).join
  #     @resubmit = "#{code}_#{@myshixun.id}"
  #   end
  #
  #   if content != last_content && code_file.blank?
  #     raise("实训平台繁忙(繁忙等级:81),请稍后刷新并重试")
  #   end
  # rescue Exception => e
  #   uid_logger("-- file update failed #{e.message}")
  #   raise Educoder::TipException.new("#{e.message}")
  # end

  # 恢复初始代码
  # 注意path为当前打开文件的path
  def reset_original_code
    path = params[:path]
    # 恢复初始代码应该找tpm的版本库
    repo_path = @myshixun.shixun.repo_path

    @content = git_fle_content(repo_path, path)
    tip_exception("初始代码为空,代码重置失败") if @content.nil?

    # 将tpm的代码内容同步更新到tpi
    update_file_content(@content, @myshixun.repo_path, path, current_user.mail, current_user.full_name, "reset_original_code")
  rescue Exception => e
    uid_logger_error("#{e.message}")
    tip_exception("初始化代码失败")
  end

  # 加载上次通过的代码
  def reset_passed_code
    path = params[:path]
    game_code = GameCode.where(:game_id => @game.try(:id), :path => path).first
    if game_code.present?
      content = game_code.try(:new_code)
      @content =  if @myshixun.mirror_name.select{|a| a.include?("MachineLearning") || a.include?("Python")}.present? && content.present?
                    content.gsub(/\t/, '    ')
                  else
                    content
                  end
      update_file_content(@content, @myshixun.repo_path, path, current_user.mail, current_user.full_name, "game passed reset")
    else
      tip_exception("代码重置失败,代码为空")
    end
  end

  # 获取版本库文件内容
  # 注:如果本身path传错,内容肯定也为空;fork成功后,可能短时间内也获取不到版本库内容
  # params[:status] 1: 目录树点击的请求 0:正常自动加载
  # 返回参数status : -3 需要重试,带retry参数;-1 给出提示
  def rep_content
    challenge_path = @game.challenge.try(:path)
    if challenge_path.blank?
      tip_exception("代码获取异常,请检查实训模板的评测设置是否正确")
    end
    path = @game.challenge.try(:path).split(";")[0].strip()
    path = params[:path] || path
    status = params[:status].to_i
    path = path.try(:strip)
    uid_logger("--rep_content: path is #{path}")
    begin
      @content = git_fle_content(@myshixun.repo_path, path) || ""
    rescue
      if params[:retry].present?
        begin
          begin
            # 检测TPM对应的路径代码是否正常
            git_fle_content(@myshixun.shixun.repo_path, path)
          rescue Exception => e
            uid_logger_error("#{e.message}")
            if @myshixun.shixun.try(:status) < 2
              tip_exception("代码获取异常,请检查实训模板的评测设置是否正确")
            else
              tip_exception("代码获取异常,请联系系统管理员")
            end
          end

          # 如果模板没有问题,则通过中间层检测实训仓库是否异常
          # 监测版本库HEAD是否存在,不存在则取最新的HEAD
          gitUrl = repo_url @myshixun.repo_path
          gitUrl = Base64.urlsafe_encode64(gitUrl)
          shixun_tomcat = edu_setting('cloud_bridge')
          rep_params = {:tpiID => "#{@myshixun.id}", :tpiGitURL  => "#{gitUrl}"}
          # 监测版本库HEAD是否存在,不存在则取最新的HEAD
          uri = "#{shixun_tomcat}/bridge/game/check"
          res = uri_post uri, rep_params
          # res值:0 表示正常;-1表示有错误;-2表示代码版本库没了
          if status == 0 && res && (res['code'] == -2 || res['code'] == -1)
            # 删除不需要的仓库
            begin
              GitService.delete_repository(repo_path: @myshixun.repo_path)
            rescue Exception => e
              uid_logger_error("#{e.message}")
            end
            # fork一个新的仓库
            project_fork(@myshixun, @shixun.repo_path, current_user.login)
          end
        rescue Exception => e
          uid_logger_error(e.message)
          # 报错继续retry
          tip_exception(-3, "#{e.message}")
        end
      end
      tip_exception(-3, "#{e.message}")
    end
  end

  # 编程题评测
  def game_build
    game_challenge = Challenge.select([:id, :position, :picture_path]).find(@game.challenge_id)

    # 更新评测次数
    @game.update_column(:evaluate_count, (@game.evaluate_count.to_i + 1))
    # 清空代码评测信息
    msg = @game.run_code_message
    msg.update_attributes(:status => 0, :message => nil) if msg.present?

    # 更新时间是为了TPM端显示的更新,退出实训及访问实训的时候会更新
    @myshixun.update_column(:updated_at, Time.now)

    gitUrl = repo_ip_url @myshixun.repo_path
    logger.info("#############giturl: ##{gitUrl}")
    gitUrl = Base64.urlsafe_encode64(gitUrl)

    shixun_tomcat = edu_setting('cloud_bridge')
    step = game_challenge.try(:position)
    mirror_repository_limit = @shixun.mirror_repositories.where(main_type: 1).select(:resource_limit).try(:first).try(:resource_limit)
    # mirror表中很很大的脚本字段,所以单独查询一个字段效果更好
    resource_limit = "echo 'ulimit -f #{mirror_repository_limit}' >> /root/.bashrc  ; source /root/.bashrc\n"
    tpmScript = @shixun.evaluate_script.nil? ? "" : Base64.urlsafe_encode64((resource_limit + @shixun.evaluate_script).gsub("\r\n", "\n"))

    # status为2已经通过关,是重新评测
    if @game.status == 2
      resubmit = params[:resubmit]
    else
      # 重新评测不影响已通关的实训状态;first为第一次评测,通过前端JS轮询获取
      @game.update_attributes!(status: 1) if params[:first].to_i == 1
    end

    testSet = []
    game_challenge.test_sets.each do |test_set|
      input = test_set.input.nil? ? "" : test_set.input.gsub("\r\n", "\n")
      output = test_set.output.nil? ? "" : test_set.output.gsub("\r\n", "\n")
      test_cases = {:input => input, :output => output}
      testSet << test_cases
    end

    testCases = Base64.urlsafe_encode64(testSet.to_json) unless testSet.blank?


    # 注意:这个地方的参数写的时候不能换行
    content_modified = params[:content_modified] # 决定文件内容是否有修改,有修改如果中间层pull没有更新,则轮询等待更新
    br_params = {:tpiID => "#{@myshixun.id}", :tpiGitURL  => "#{gitUrl}", :buildID => "#{@game.id}",
                 :instanceChallenge  => "#{step}", :testCases => "#{testCases}", :resubmit => "#{resubmit}",
                 :times => params[:first].to_i, :podType => @shixun.webssh, :content_modified => content_modified,
                 :containers => "#{Base64.urlsafe_encode64(container_limit(@shixun.mirror_repositories))}",
                 :persistenceName => @shixun.identifier, :tpmScript => "#{tpmScript}",
                 :timeLimit => "#{@shixun.exec_time}", :isPublished => (@shixun.status < 2 ? 0 : 1) }

    # 评测有文件输出的需要特殊传字段 path:表示文件存储的位置
    br_params['file'] = Base64.urlsafe_encode64({path: "#{game_challenge.picture_path}"}.to_json) if game_challenge.picture_path.present?

    # needPortMapping: web类型需要pod端口映射
    br_params[:needPortMapping] = 8080 if @myshixun.mirror_name.include?("Web")

    # 中间层交互
    uri = "#{shixun_tomcat}/bridge/game/gameEvaluate"
    res = interface_post uri, br_params, 502, "gameEvaluate failed"

    @result = {status: 1, resubmit: resubmit, position: game_challenge.position, port: res['port'], had_done: @game.had_done}
  rescue Exception => e
    uid_logger("评测出错,详情:" + e.message)
    @result = {status: -1, message: "实训云平台繁忙(繁忙等级:502),请稍后刷新并重试", position: game_challenge.position,
               had_done: @game.had_done}
  end

  # 选择题评测
  def choose_build
    Rails.logger.error("#################{params}")
    # 选择题如果通关了,则不让再评测
    if @game.status == 2
      raise Educoder::TipException.new("您已通过该关卡")
    end
    # 更新评测次数
    @game.update_column(:evaluate_count, (@game.evaluate_count.to_i + 1))

    game_challenge = Challenge.select([:id, :position]).find(@game.challenge_id)
    user_answer = params[:answer]
    challenge_chooses_count = user_answer.length

    choose_correct_num = 0
    score = 0
    had_passed = true
    test_sets = []
    str = ""
    game_challenge.challenge_chooses.includes(:challenge_tags).each_with_index do |choose, index|
      # user_answer虽然是传的数组,但是可能存在多选择提的情况.
      user_answer_tran = user_answer[index].size > 1 ? user_answer[index].split("").sort.join("") :  user_answer[index]
      standard_answer_tran = choose.standard_answer.size > 1 ? choose.standard_answer.split("").sort.join("") :  choose.standard_answer
      correct = (user_answer_tran == standard_answer_tran) ? true : false
      if str.present?
        str += ","
      end
      str += "('#{@game.id}', '#{choose.position}', '#{user_answer_tran}', '#{correct ? 1 : 0}', '#{@game.next_query_index}', '#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}', '#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}')"
      # 只要有一题错误就不能通关
      had_passed = false if !correct
      choose_correct_num += 1 if correct

      # 全部通关的时候,需要对所得的总分记录
      # 总分的记录应该是根据每一题累加,如果该题正确,则加分
      score += choose.score if correct

      standard_answer = correct ? standard_answer_tran : -1
      sin_test_set = {:result => correct,
                      :actual_output => user_answer_tran,
                      :standard_answer => standard_answer,
                      :position => choose.position}
      test_sets << sin_test_set
    end

    # 批量插入评测结果
    uid_logger("choose_build")
    sql = "INSERT INTO outputs (game_id, test_set_position, actual_output, result, query_index, created_at, updated_at) VALUES" + str
    ActiveRecord::Base.connection.execute sql

    had_done = @game.had_done
    @myshixun.update_attribute(:status, 1) if had_done == 1

    # 没通关或者查看了答案通关的时候经验为0
    # 通关但是查看了答案,评测的时候金币显示0(避免用户以为重复扣除),但是在关卡列表中金币显示负数
    experience = 0
    final_score = 0
    # 如果本次答题全部正确,并且之前没有通关,则进行相应的奖励,每关只要有错题,则不进行任何奖励
    # 注:扣除分数是在查看答案的时候扣除的
    if had_passed && !@game.had_passed?
      @game.update_attributes(:status => 2, :end_time => Time.now)
      # TPM实训已发布并且没有查看答案
      if @shixun.is_published? && !@game.answer_open
        # 查看答案的时候处理final_scor和扣分记录
        experience = score
        reward_attrs = { container_id: @game.id, container_type: 'Game', score: score }
        RewardGradeService.call(@myshixun.owner, reward_attrs)
        @game.update_attribute(:final_score, score)
        final_score = score
        RewardExperienceService.call(@myshixun.owner, reward_attrs)
      end
    end

    grade = @myshixun.owner.try(:grade)

    # 更新实训关联的作品分数 TODO: 更新作业需要等作业模块开了再打开
    # update_myshixun_work_score myshixun

    # 高性能取上一关、下一关
    prev_game = @game.prev_of_current_game(@shixun.id, @game.myshixun_id, game_challenge.position)
    next_game = @game.next_of_current_game(@shixun.id, @game.myshixun_id, game_challenge.position) if had_passed

    # 高性能取上一关、下一关
    #prev_game = Game.prev_identifier(@shixun.id, @game.myshixun_id, game_challenge.position)
    #next_game = Game.next_game(@shixun.id, @game.myshixun_id, game_challenge.position)

    @result = {grade: grade,
               gold: final_score,
               experience: experience,
               challenge_chooses_count: challenge_chooses_count,
               choose_correct_num: choose_correct_num,
               test_sets: test_sets,
               prev_game: prev_game,
               next_game: next_game}
  rescue Exception => e
    uid_logger("choose build failed #{e.message}")
    @result = [status: -1,  contents: "#{e.message}"]
  end

  # 轮询获取状态
  # resubmit是在file_update中生成的,从game_build中传入的
  def game_status
    resubmit_identifier = @game.resubmit_identifier
    # 如果没有超时并且正在评测中
    # 判断评测中的状态有两种:1、如果之前没有通关的,只需判断status为1即可;如果通过关,则判断game的resubmit_identifier是否更新
    uid_logger("################game_status: #{@game.status}")
    uid_logger("################params[:resubmit]: #{params[:resubmit]}")
    uid_logger("################resubmit_identifier: #{resubmit_identifier}")
    uid_logger("################time_out: #{params[:time_out]}")
    if (params[:time_out] == "false") && ((params[:resubmit].blank? && @game.status == 1) || (params[:resubmit].present? &&
        (params[:resubmit] != resubmit_identifier)))
      # 代码评测的信息
      running_code_status = @game.run_code_message.try(:status)
      running_code_message = @game.run_code_message.try(:message)
      render :json => { running_code_status: running_code_status, running_code_message: running_code_message }
    end

    uid_logger("##### resubmit_identifier is #{resubmit_identifier}")
    port = params[:port]
    score = 0
    experience = 0
    game_status = @game.status
    had_done = @game.had_done
    game_challenge = Challenge.select([:id, :score, :position, :shixun_id, :web_route]).find(@game.challenge_id)

    if params[:resubmit].blank?   # 非重新评测
      if game_status == 2  # 通关
        if @shixun.status > 1
          score = @game.final_score  # 查看答案的时候有对最终获得金币进行处理
          experience = @game.final_score
        else
          score = 0
          experience = 0
        end
      end
    else   # 重新评测
      # 如果满足前面的条件,进入此处只可能是结果已返回并存入了数据库
      if params[:resubmit] == resubmit_identifier     # 本次重新评测结果已经返回并存入数据库
        game_status = (@game.retry_status == 2 ? 2 : 0) # retry_status是判断重新评测的通关情况。2表示通关
      end
    end

    # 实训的最大评测次数,这个值是为了优化查询,每次只取最新的最新一次评测的结果集
    max_query_index = @game.query_index
    #max_query_index = @game.outputs.first.try(:query_index)
    # 区分评测过未评测过,未评测过按需求取数据
    testset_detail max_query_index.to_i, game_challenge

    # 处理生成图片类型文件
    picture = (@game.picture_path.nil? ? 0 : @game.id)
    # 针对web类型的实训
    web_route = game_challenge.try(:web_route)
    mirror_name = @shixun.mirror_name

    # 轮询结束,更新评测统计耗时
    if game_status == 0 || game_status == 2
      e_record = EvaluateRecord.where(:game_id => @game.id).first
      if e_record
        front_js = format("%.3f",  (Time.now.to_f - e_record.try(:updated_at).to_f)).to_f
        consume_time = format("%.3f", (Time.now - e_record.created_at)).to_f
        e_record.update_attributes(:consume_time => consume_time, :front_js => front_js)
      end
    end

    uid_logger("game is is #{@game.id}, record id is #{e_record.try(:id)}, time is**** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}")
    # 记录前端总耗时
    record_consume_time = EvaluateRecord.where(:game_id => @game.id).first.try(:consume_time)
    # 实训制作者当前拥有的金币
    grade = User.where(:id => @game.user_id).pluck(:grade).first

    # 高性能取上一关、下一关
    # 上一关、下一关
    prev_game = @game.prev_of_current_game(@shixun.id, @game.myshixun_id, game_challenge.position)
    next_game = @game.next_of_current_game(@shixun.id, @game.myshixun_id, game_challenge.position) if game_status == 2
    @base_date = {grade: grade, gold: score, experience: experience, status: game_status, had_done: had_done,
                  position: game_challenge.position, port: port, record_consume_time: record_consume_time,
                  mirror_name: mirror_name, picture: picture, web_route: web_route, star: @game.star,
                  next_game: next_game, prev_game: prev_game}
  end

  # 记录实训花费的时间
  # REDO:需要添加详细的说明
  def cost_time
    cost_time = params[:time].to_i
    @game.update_attribute(:cost_time, cost_time)
  end

  # 同步challenge的更新时间
  def sync_modify_time
    modify_time = Challenge.where(:id => @game.challenge_id).pluck(:modify_time).first
    @game.update_column(:modify_time, modify_time)
    sucess_status
  end

  # tpi弹框状态更新;true则不再显示;false每次刷新显示
  def system_update
    myshixun = Myshixun.find(params[:myshixun_id])
    myshixun.update_attribute(:system_tip, true)
    sucess_status
  end

  # 关闭webssh
  def close_webssh
    myshixun_id = @game.myshixun_id
    digest = @game.identifier + edu_setting('bridge_secret_key')
    digest_key = Digest::SHA1.hexdigest("#{digest}")
    begin
      shixun_tomcat = edu_setting('cloud_bridge')
      uri = "#{shixun_tomcat}/bridge/webssh/delete"
      Rails.logger.info("#{current_user} => cloese_webssh digest is #{digest}")
      params = {:tpiID => myshixun_id, :digestKey => digest_key, :identifier => @game.identifier}
      res = uri_post uri, params
      if res && res['code'].to_i != 0
        raise("实训云平台繁忙(繁忙等级:110)")
      end
    rescue Exception => e
      Rails.logger.error(e)
      tip_exception("实训云平台繁忙, 关闭失败!")
    end
  end

  # tpi对于实训关卡的点赞或取消点赞
  def plus_or_cancel_praise
    challenge = @game.challenge
    pt = PraiseTread.where(:praise_tread_object_id => challenge.id, :praise_tread_object_type => 'Challenge',
                           :user_id => current_user, :praise_or_tread => 1).first
    # 如果当前用户已赞过,则不能重复赞
    if pt.blank?
      PraiseTread.create!(:praise_tread_object_id => challenge.id, :praise_tread_object_type => 'Challenge',
                          :user_id => current_user.id, :praise_or_tread => 1)  if pt.blank?
      @praise = true
    else
      pt.destroy if pt.present? # 如果已赞过,则删掉这条赞(取消);如果没赞过,则为非法请求不处理
      @praise = false
    end

    @praise_count = PraiseTread.where(:praise_tread_object_id => challenge.id, :praise_tread_object_type => 'Challenge',
                                      :praise_or_tread => 1).count
  end

  private

  # 评测测试机封装
  def testset_detail max_query_index, challenge
    # 是否允许查看隐藏的测试集,以前的power
    @allowed_hidden_testset = @identity < User::EDU_GAME_MANAGER

    if max_query_index > 0
      uid_logger("max_query_index is #{max_query_index} game id is #{@game.id}, challenge_id is #{challenge.id}")
      @qurey_test_sets = TestSet.find_by_sql("SELECT o.code, o.actual_output, o.out_put, o.result, o.test_set_position,
                              o.query_index, t.is_public, t.input, t.output, o.compile_success FROM outputs o, games g, challenges c,
                              test_sets t where g.id=#{@game.id} and c.id=#{challenge.id} and o.query_index=#{max_query_index}
                              and g.id = o.game_id and c.id= g.challenge_id and t.challenge_id = c.id and
                              t.position =o.test_set_position order by o.query_index
                         ")
      @test_sets_count = @qurey_test_sets.count
      # 错误的测试集总数
      @sets_error_count = 0
      @qurey_test_sets.each do |set|
        @sets_error_count += 1 unless set.result
      end
      @last_compile_output = @qurey_test_sets.first['out_put'].gsub(/\n/, '<br/>').gsub(/\t/, "&nbsp\;&nbsp\;&nbsp\;&nbsp\;&nbsp\;&nbsp\;&nbsp\;&nbsp\;") if @qurey_test_sets.first['out_put'].present?
    else
      @qurey_test_sets = TestSet.find_by_sql("SELECT t.is_public, t.input, t.output, t.position
                                                     FROM test_sets t where t.challenge_id = #{challenge.id}")
    end
  end

  # 实训选择题需要局部刷新或者显示的部分
  def choose_container game_challenge, game, max_query_index
    # category 1: 单选题,其它的多选题(目前只有两种)
    challenge_chooses = game_challenge.challenge_chooses.includes(:challenge_questions)
    test_sets = []
    @chooses = []

    # 选择题测试集统计
    challenge_chooses_count = challenge_chooses.count
    choose_correct_num = game.choose_correct_num(max_query_index)
    game_outputs = game.outputs.where(:query_index => max_query_index)

    # 判断用户是否有提交
    had_submmit = game_outputs.present?

    # 判断选择题是否写了标准答案
    has_answer = []
    challenge_chooses.each do |choose|
      challenge_question = []
      output = game_outputs.select{|game_output| game_output.test_set_position == choose.position}[0] unless game_outputs.blank?

      category = choose.category
      subject = choose.subject
      choose.challenge_questions.each do |question|
        position = question.position
        option_name = question.option_name
        challenge_question << {:positon =>  position, :option_name => option_name}
      end
      # actual_output为空表示暂时没有评测答题,不允许查看
      actual_output = output.try(:actual_output).try(:strip)
      has_answer << choose.answer  if choose.answer.present?
      # 标准答案处理,错误的不让用户查看,用-1替代
      standard_answer = (actual_output.blank? || !output.try(:result)) ? -1 : choose.standard_answer
      result = output.try(:result)
      sin_test_set = {:result => result, :actual_output => actual_output, :standard_answer => standard_answer,
                      :position => choose.position}

      sin_choose = {:category => category, :subject => subject, :challenge_question => challenge_question}
      @chooses << sin_choose
      test_sets << sin_test_set
    end
    @has_answer = has_answer.present?
    @choose_test_cases = {:had_submmit => had_submmit, :challenge_chooses_count => challenge_chooses_count,
                          :choose_correct_num => choose_correct_num, :test_sets => test_sets}
  end


  def find_game
    @game = Game.find_by_identifier(params[:identifier])
    if @game.blank?
      normal_status(404, "...")
      return
    end

    @myshixun = @game.myshixun
  end

  def find_shixun
    @shixun = Shixun.find(@myshixun.shixun_id)
  end

  # http://localhost:3000/tasks/hcie39pw2bjn
  # 可以访问条件:学员本身;管理员;TPM制作者
  def allowed
    @identity = current_user.game_identity(@game)
    raise Educoder::TipException.new(403, "..") if @identity > User::EDU_GAME_MANAGER
  end
end