# encoding: utf-8
class MyshixunsController < ApplicationController
  layout 'base_myshixun'
  skip_before_filter :verify_authenticity_token, :only => [:training_task_status, :close_webssh, :code_runinng_message]
  before_filter :require_login, :except => [:training_task_status, :close_webssh, :code_runinng_message]
  before_filter :check_authentication, :except => [:training_task_status, :close_webssh, :mul_test_home, :mul_test_user,
                                                   :mul_test_myshixun, :mul_test_shixun, :mul_test_start, :code_runinng_message]
  before_filter :find_myshixun, :only => [:show, :myshixun_reset, :open_webssh, :sync_reset_time, :destroy, :search_file_list, :vnc]
  DCODES = %W(2 3 4 5 6 7 8 9 a b c f e f g h i j k l m n o p q r s t u v w x y z)

  include ApplicationHelper

  def forbidden
    render_403
    return
  end

  def not_found
    render_404
    return
  end

  def search_file_list
    path = params[:path]
    @path = to_path_param(path)
    g = Gitlab.client
    @dir = g.trees(@myshixun.gpid, :path => @path).map{|tree|[tree.type, (@path.blank? ? tree.name : "#{@path}"+"/"+tree.name )]}
    logger.info("dir is ##{@dir}")
    @page = (params['page'] || 1).to_i
    @dir_count = @dir.count
    @limit = 5
    @is_remote = true
    @dir_pages = Paginator.new @dir_count, @limit, params['page'] || 1
    @offset ||= @dir_pages.offset
    @dir = paginateHelper @dir, @limit
    respond_to do |format|
      format.js
    end
  end

  # 并发测试:主页
  # http://localhost:3000/myshixuns/mul_test_home?num=5
  def mul_test_home
    num = params[:num].to_i
    uri = Setting.protocol + "://" + Setting.host_name
    num.times do
      Thread.new{get_url_exec(uri)}
    end

  end

  # 并发测试:个人主页
  def mul_test_user
    num = params[:num].to_i
    login = params[:login]
    uri = Setting.protocol + "://" + Setting.host_name + "/users/#{login}"
    num.times do
      Thread.new{get_url_exec(uri)}
    end
  end

  #并发测试:tpi
  def mul_test_myshixun
    num = params[:num].to_i
    identifier = params[:identifier]
    uri = Setting.protocol + "://" + Setting.host_name + "/tasks/#{identifier}"
    num.times do
      Thread.new{get_url_exec(uri)}
    end
  end

  #并发测试:tpm
  def mul_test_shixun
    num = params[:num].to_i
    identifier = params[:identifier]
    uri = Setting.protocol + "://" + Setting.host_name + "/shixuns/#{identifier}"
    num.times do
      Thread.new{get_url_exec(uri)}
    end
  end

  # 并发测试
  # num为进程数
  # 分三种JAVA/PYTHON/C++
  # 方式模拟请求
  # 实训ID: p - 122,178,120; J - 571,895,50; c++ - 101,70,118
  # http://localhost:3000/myshixuns/mul_test?shixun_id=122,178,120&num=10
  def mul_test
    unless User.current.admin?
      render_403
      return
    end
    codes = %W(1 2 3 4 5 6 7 8 9 A B C D E F G H J K L N M O P Q R S T U V W X Y Z)
    begin
      num = params[:num].to_i
      @myshixuns = Myshixun.where(:shixun_id => params[:shixun_id].split(",")).first(num)
      logger.warn("###1mul test game_build start : myshixuns count is #{@myshixuns.count}")
      @myshixuns.each do |myshixun|
        game = myshixun.games.last
        logger.warn("###2mul test game_build start ")
        identifier = game.try(:identifier)
        if game.status == 2
          code = codes.sample(8).join
          resubmit = "#{code}_#{myshixun.id}"
        end
        logger.warn("###3mul test game_build start ...")
        uri = Setting.protocol + "://" + Setting.host_name + "/api/v1/games/#{identifier}/game_build?resubmit=#{resubmit}&content_modified=0&first=1"
        Thread.new{get_url_exec(uri)}
      end
    rescue Exception => e
      logger.error("mul test failed ===> #{e.message}")
    end
    render :json => {:status => "start"}
  end

  # 最新可以用的并发测试接口
  def sigle_mul_test
   
    codes = %W(1 2 3 4 5 6 7 8 9 A B C D E F G H J K L N M O P Q R S T U V W X Y Z)
    begin
      identifiers = Myshixun.where(:shixun_id => params[:shixun_id].split(",")).pluck(:identifier)
      ide = identifiers[rand(identifiers.length)]
      myshixun = Myshixun.where(:identifier => ide).first

      game = myshixun.games.last
      logger.warn("###2mul test game_build start ")
      identifier = game.try(:identifier)
      if game.status == 2
        code = codes.sample(8).join
        resubmit = "#{code}_#{myshixun.id}"
      end
      logger.warn("###3mul test game_build start ...")
      EvaluateRecord.create!(:user_id => myshixun.user_id, :shixun_id => myshixun.shixun.id, :game_id => game.id)
      redirect_to "/api/v1/games/#{identifier}/game_build?resubmit=#{resubmit}&content_modified=0&first=1"
    rescue Exception => e
      logger.error("mul test failed ===> #{e.message}")
    end
  end

  # 慎用,只能用于测试版
  def sigle_update_myshixun
    shixun = Shixun.find_by_identifier(params[:shixun_id])
    user = User.find(rand(50000))
    code = down_generate_identifier("myshixun")
    ActiveRecord::Base.transaction do
      begin
        Myshixun.create!(:shixun_id => shixun.id, :user_id => user.id, :identifier => code, :modify_time => shixun.modify_time,
                         :reset_time => shixun.reset_time, :onclick_time => Time.now, :gpid => -1,
                         :git_url => "", :commit_id => "123123")
        render :json => {:status => 200}
      rescue Exception => e
        logger.error("sigle test #{e.message}")
        render :json => {:error => "#{e}"}
        raise ActiveRecord::Rollback
      end
    end
  end

  # 开启实训
  def mul_test_start
    unless User.current.admin?
      render_403
      return
    end
    num = params[:num].to_i
    shixuns = params[:shixun_identifier].split(",")

    num.times do |n|
      lastnames = []
      100.times do |n|
        lastnames << "educoder#{sprintf("%04d", n)}"
      end
      lastname = lastnames[rand(lastnames.count)]
      shixun = Shixun.where(:identifier => shixuns[rand(shixuns.count)]).first
      current_user = User.where(:lastname => lastname).first
      Thread.new{start_shixun(shixun, current_user)}
    end

  end

  def start_shixun shixun, current_user
    ActiveRecord::Base.transaction do
      begin
        # fork版本库,如果用户没有同步,则先同步用户
        g = Gitlab.client
        if current_user.gid.nil?
          s = Trustie::Gitlab::Sync.new
          s.sync_user(current_user)
        end
        gshixun = g.fork(shixun.gpid, current_user.gid)

        shixun_tomcat = Redmine::Configuration['shixun_tomcat']
        code = down_generate_identifier("myshixun")


        # 一般通过默认分支是否存在来判断一个项目是否fork成功
        if gshixun.try(:id).present?
          commit_id = g.commits(shixun.gpid).first.try(:id)
          # educoder 加入到myshixun中
          myshixun_admin_gid = User.where(:login => "educoder").first.try(:gid)
          g.add_team_member(gshixun.id, myshixun_admin_gid, 40)  # 40代表角色master

          myshixun = Myshixun.create!(:shixun_id => shixun.id, :user_id => current_user.id, :identifier => code, :modify_time => @shixun.modify_time,
                                      :reset_time => shixun.reset_time, :onclick_time => Time.now, :gpid => gshixun.id,
                                      :git_url => gshixun.try(:path_with_namespace), :commit_id => commit_id)

          # tpm 不需要目录的
          # rep = Repository.create!(:myshixun_id => myshixun.id, :identifier => gshixun.name,:project_id => -1, :shixun_id => -2)
          # rep.update_column(:type, "Repository::Gitlab")
          rep_url = Base64.urlsafe_encode64(gitlab_url shixun) # 注意:educoder为默认给实训创建版本库的用户,如果换成别的用户,名字要相应的修改

          logger.info("start openGameInstance")
          uri = "#{shixun_tomcat}/bridge/game/openGameInstance"
          logger.info("end openGameInstance")
          params = {tpiID: "#{myshixun.id}", tpmGitURL:rep_url, tpiRepoName: gshixun.try(:name)}
          logger.info("openGameInstance params is #{params}")
          res = uri_exec uri, params
          if (res && res['code'].to_i != 0)
            raise("实训云平台繁忙(繁忙等级:83)")
          end

          # 其它创建关卡等操作
          challenges = shixun.challenges
          # 之所以增加user_id是为了方便统计查询性能
          challenges.each_with_index do |challenge, index|
            status = (index == 0 ? 0 : 3)
            code = down_generate_identifier("game")
            Game.create!(:challenge_id => challenge.id, :myshixun_id => myshixun.id, :status => status, :user_id => myshixun.user_id,
                         :open_time => Time.now, :identifier => code, :modify_time => challenge.modify_time)

          end
        else
          raise("实训云平台繁忙(繁忙等级:81)")
        end
        # unlock
        logger.info("myshixun id is #{myshixun.try(:identifier)} and current_task id is#{myshixun.try(:current_task).try(:id)}")
        # 开启实训时更新关联作品的状态
        update_myshixun_work_status myshixun
        render :json => {:status => 1}
      rescue Exception => e
        if e.message == "shixun error"
          flash[:error] = "正在后台执行,请稍后重试"
        elsif e.message.include?("Mysql2::Error")
          flash[:error] = "正在后台执行,请稍后重试"
        else
          flash[:error] = e.message
        end
        logger.info("failed to exec shixun: current task id is #{e}")
        g.delete_project(gshixun.id) if gshixun.try(:id).present?
        render :json => {:status => -1}
        raise ActiveRecord::Rollback
      end
    end
  end

  def down_generate_identifier type
    if type == "game"
      code = DCODES.sample(12).join
      return down_generate_identifier(type) if Game.where(identifier: code).present?
    elsif type == "myshixun"
      code = DCODES.sample(10).join
      return down_generate_identifier(type) if Myshixun.where(identifier: code).present?
    end
    code
  end

  def game_build myshixun_id, game_id, gitUrl, step, language, mul_test, testcases, podtype, tpmScript, mirror_repositories, time_limit, file, port
    logger.info("############### game_build")
    shixun_tomcat = Redmine::Configuration['shixun_tomcat']
    # 注意:这个地方的参数写的时候不能换行
    params = {:tpiID => "#{myshixun_id}", :tpiGitURL  => "#{gitUrl}", :buildID => "#{game_id}",:instanceChallenge  => "#{step}",
              :testCases => "#{testcases}", :resubmit => "", :times => 1, :podType => podtype,
              :containers => "#{Base64.urlsafe_encode64(container_limit(mirror_repositories))}", :tpmScript => "#{tpmScript}",
              :timeLimit => "#{time_limit}", :file => "#{file}", :needPortMapping => "#{port}"}
    logger.info("############### start gameEvaluate")
    uri = "#{shixun_tomcat}/bridge/game/gameEvaluate"
    res = uri_exec uri, params
    logger.info("############### end gameEvaluate")
    if (res && res['code'] != 0)
      @message = "实训云平台繁忙(繁忙等级:86)"
      raise("实训云平台繁忙(繁忙等级:86)")
      mul_test.update_attributes(:time => (Time.now - mul_test.updated_at), :status => 3)
    else
      mul_test.update_attribute(:time, res['costTime'].to_i)
    end
  end

  # 临时使用
  def destroy
    if User.current.admin?
      user = @myshixun.owner
      @myshixun.games.each do |game|
        ex =Experience.where(:container_type => "Game", :container_id => game.id).first
        ex.destroy if ex.present?
        gr = Grade.where(:container_type => "Game", :container_id => game.id).first
        gr.destroy if gr.present?
        grade = user.try(:grade).to_i - game.try(:final_score).to_i
        grade = grade > 0 ? grade : 0
        user.update_column(:grade, grade)
      end
      @myshixun.destroy
      # 删除积分和经验值
      #g = Gitlab.client
      #g.delete_project(@myshixun.gpid) if @myshixun.gpid.present?
    end
  end

  def sync_reset_time
    @myshixun.update_column(:modify_time, @myshixun.shixun.try(:modify_time))
    render :json => {:data => "success"}
  end

  # 将代码重置到开启状态
  # 方法:使用文件更新,非Git重置
  def code_reset

  end

  def code_reset_success

  end

  # 连接webssh
  def open_webssh
    username = Redmine::Configuration['webssh_username']
    password = Redmine::Configuration['webssh_password']
    old_time = Time.now.to_i
    begin
      shixun_tomcat = Redmine::Configuration['shixun_tomcat']
      uri = "#{shixun_tomcat}/bridge/webssh/getConnectInfo"
      user_id = User.current.id
      params = {tpiID:@myshixun.id, podType:@myshixun.shixun.try(:webssh), containers:(Base64.urlsafe_encode64(container_limit @myshixun.shixun.mirror_repositories))}
      res = uri_exec uri, params
      if res && res['code'].to_i != 0
        raise("实训云平台繁忙(繁忙等级:92)")
      end
      render :json => {:host => res['address'],
                       :port => res['port'],
                       :ws_url => res['ws_address'],
                       :username => username,
                       :password => password,
                       :game_id => @myshixun.id,
                       :webssh_url => Redmine::Configuration['tomcat_webssh']}
    rescue Exception => e
      logger.error(e)
      render :json => {:error => e.try(:message)}
        # @message = e.try(:message)
    ensure
      use_time = Time.now.to_i - old_time
      logger.info "open_webssh tpiID #{@myshixun.id} use time #{use_time}"
    end
  end

  def vnc
    vnc_password = Redmine::Configuration['vnc']
    shixun_tomcat = Redmine::Configuration['shixun_tomcat']
    host = Redmine::Configuration['tomcat_php']
    begin
    uri = "#{shixun_tomcat}/bridge/vnc/getvnc"
    params = {tpiID:@myshixun.id}
    res = uri_exec uri, params
    if res && res['code'].to_i != 0
      raise("实训云平台繁忙(繁忙等级:99)")
    end
    # url = host + ":" + res['port'] + "?password=" + vnc_password
    url = host +":#{res['port']}/vnc.html"
    render :json => {:url => url}
    rescue Exception => e
      logger.error(e)
    end
  end

  # 断开webssh连接
  def close_webssh
    username = Redmine::Configuration['webssh_username']
    password = Redmine::Configuration['webssh_password']
    begin
      shixun_tomcat = Redmine::Configuration['shixun_tomcat']
      uri = "#{shixun_tomcat}/bridge/webssh/deleteSSH"
      user_id = User.current.id
      logger.info("#############################")
      logger.info("game_id:" + @_params[:id])
      params = {tpiID:@_params[:id]}  # @_params[:id] 是webssh传过来的gameId
      res = uri_exec uri, params
      if res && res['code'].to_i != 0
        raise("实训云平台繁忙(繁忙等级:93)")
      end
      render :jsonp => {:host => res['address'], :port => res['port'], :username => username, :password => password}
    rescue Exception => e
      logger.error(e)
      @message = e.try(:message)
    end
  end

  # 实训重置只更新脚本
  def myshixun_reset
    shixun = @myshixun.shixun
    # ActiveRecord::Base.transaction do
      begin
        g = Gitlab.client
        # shixun_tomcat = Redmine::Configuration['shixun_tomcat']
        # @challenges = shixun.challenges
        # # 删除选择题用户记录
        # unless @challenges.blank?
        #   @challenges.each do |challenge|
        #     if challenge.st != 0
        #       challenge.challenge_chooses.each do |choose|
        #         user_output = choose.choose_outputs
        #         unless user_output.blank?
        #           user_output.delete
        #         end
        #       end
        #     end
        #   end
        # end
        #end
        StudentWork.where(:myshixun_id => @myshixun.id).update_all(:myshixun_id => nil, :work_status => 0)

        # 数据不关联删除,以后的数据可能要做行为分析
        # @myshixun.destroy
        @myshixun.delete
        # 主从复制出现脏读的情况
        if @myshixun.gpid
          g.delete_project(@myshixun.gpid)
        end
        sleep(1)
        redirect_to shixun_exec_shixun_path(shixun, :type => 1, :is_subject => params[:is_subject])
      rescue Exception => e
        flash[:error] = "实训云平台繁忙(繁忙等级:89)"
        redirect_to shixun_challenges_path(@shixun)
        logger.error("myshixun reset failed #{e}")
        # raise ActiveRecord::Rollback
      end
    # end

  end

  # 代码运行中的信息接口
  def code_runinng_message
    begin
      logger.info("######################params: #{params}")
      jsonTestDetails = JSON.parse(params[:jsonTestDetails])
      game_id = jsonTestDetails['buildID']
      message = jsonTestDetails['textMsg']
      logger.info("##################code_runinng_message:#{jsonTestDetails}")
      logger.info("##################buildID: #{game_id}")
      logger.info("##################textMsg: #{message}")
      if game_id.present? && message.present?
        game = Game.find game_id
        msg = game.run_code_message
        # 只有评测中的game才会创建和更新代码评测中的信息
        logger.info("##################game: #{game.status}")
        logger.info("##################retry_status: #{game.retry_status}")
        if game.status == 1 || game.status == 2
          if msg.blank?
            RunCodeMessage.create!(:game_id => game_id, :status => 1, :message => message)
          else
            msg.update_attributes(:status => (msg.status + 1), :message => message)
          end
        end
        render :json => {:data => "success"}
      end
    rescue Exception => e
      render :json => {:data => "failed, exception_message: #{e}"}
    end
  end

  # taskId 即返回的game id
  # 返回结果:params [:stauts] 0 表示成功,其它则失败
  # msg 错误信息
  # output 为测试用户编译输出结果
  # myshixun:status 1为完成实训
  # @jenkins: caseId对应test_set的position,passed: 1表示成功,0表示失败
  # resubmit 1:表示已通关后重新评测;0:表示非重新评测
  # retry_status 0:初始值;1:重新评测失败;2:重新评测成功
  # tpiRepoPath 中间层图片的workspace路径
  # params[:jsonTestDetails] = '{"buildID":"19284","compileSuccess":"1",
  # "msg":[{"caseId":"1","expectedOutput":"MSAyIDMNCg","input":"MiAzIDE","output":"MSAyIDMNCg","passed":"1"},
  # {"caseId":"2","expectedOutput":"LTMgMSA2DQo","input":"LTMgNiAx","output":"LTMgMSA2DQo","passed":"1"},
  # {"caseId":"3","expectedOutput":"LTcgLTUgLTMNCg","input":"LTcgLTMgLTU","output":"LTcgLTUgLTMNCg","passed":"1"}],
  # "outPut":"Y29tcGlsZSBzdWNjZXNzZnVsbHk","resubmit":"","status":"0"}'
  # params[:timeCost] = '{"evaluateEnd":"2017-11-24 11:04:37","pull":"0.086",
  # "createPod":"1.610","evaluateAllTime":2820,"evaluateStart":"2017-11-24 11:04:35","execute":"0.294"}'
  # params[:pics] = "a.png,b.png,c.png"
  def training_task_status
    ActiveRecord::Base.transaction do
      begin
        t1 = Time.now
        jsonTestDetails = JSON.parse(params[:jsonTestDetails])
        timeCost = JSON.parse(params[:timeCost])
        brige_end_time = Time.parse(timeCost['evaluateEnd']) if  timeCost['evaluateEnd'].present?
        return_back_time = format("%.3f",  ( t1.to_f - brige_end_time.to_f)).to_f
        status = jsonTestDetails['status']
        game_id = jsonTestDetails['buildID']
        logger.info("training_task_status start#1**#{game_id}**** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}")
        resubmit = jsonTestDetails['resubmit']
        outPut = tran_base64_decode64(jsonTestDetails['outPut'])
        jenkins_testsets = jsonTestDetails['msg']
        compile_success  = jsonTestDetails['compileSuccess']
        # message = Base64.decode64(params[:msg]) unless params[:msg].blank?
        logger.info(outPut)
        game = Game.find(game_id)
        myshixun = game.myshixun
        challenge = game.challenge
        # test_sets = challenge.test_sets
        if challenge.picture_path.present?
          #pics = params[:files]
          pics = params[:tpiRepoPath]
          game.update_column(:picture_path, pics)
        end
        logger.info("training_task_status start#2**#{game_id}**** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}")
        test_set_score = 0
        unless jenkins_testsets.blank?
          jenkins_testsets.each_with_index do |j_test_set, i|
            logger.info("j_test_set: ############## #{j_test_set}")
            actual_output = tran_base64_decode64(j_test_set['output'])
            # is_public = test_sets.where(:position => j_test_set['caseId']).first.try(:is_public)
            logger.info "actual_output:################################################# #{actual_output}"
            Output.create!(:code => status, :game_id => game_id, :out_put => outPut, :test_set_position => j_test_set['caseId'],
                           :actual_output => actual_output, :result => j_test_set['passed'].to_i, :query_index => game.query_index,
                           :compile_success => compile_success.to_i)
            # 如果设置了按测试集给分,则需要统计测试集的分值
            if challenge.test_set_score && j_test_set['passed'].to_i == 1
              test_set_score += challenge.test_sets.where(:position => j_test_set['caseId']).pluck(:score).first
            end
          end
        end
        logger.info("#############status: #{status}")
        logger.info("#############resubmit: #{resubmit}")
        record = EvaluateRecord.where(:game_id => game_id).first
        logger.info("training_task_status start#3**#{game_id}**** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}")

        # status:0表示评测成功
        if status == "0"
          if !resubmit.blank?
            game.update_attributes!(:retry_status => 2, :resubmit_identifier => resubmit)
            #if game.had_done == 1
            challenge.path.split(";").each do |path|
              game_passed_code(game.id, path.try(:strip), myshixun.try(:gpid), 1)
            end
            #end
          else
            game.update_attributes!(:status => 2, :end_time => Time.now, :accuracy => format("%.4f", 1.0 / game.query_index))
            myshixun.update_attributes!(:status => 1) if game.had_done == 1
            challenge.path.split(";").each do |path|
              game_passed_code(game.id, path, myshixun.try(:gpid), 1)
            end
            if !game.answer_open && (challenge.shixun.try(:status) > 1) # 如果没有查看答案,则获得该关卡得分
              reward_grade(game.user, game.id, 'Game', challenge.score)
              reward_experience(game.user, game.id, 'Game', challenge.score)
              game.update_attributes!(:final_score => challenge.score)
            end

            # 更新实训关联的作品分数
            update_myshixun_work_score myshixun
          end
          # 如果过关了,下一关的状态是3(为开启),则需要把状态改成1(已开启)
          # next_game = game.next_game
          next_game = Game.next_game(myshixun.shixun_id, game.myshixun_id, challenge.position)
          next_game.update_column(:status, 0) if next_game.present? && next_game.status == 3
        else  # status == "-1" 表示返回结果错误
          if !resubmit.blank?
            game.update_attributes!(:retry_status => 1, :resubmit_identifier => resubmit)
          else
            score = ((challenge.score * test_set_score) / 100.to_f).to_i
            game.update_attributes!(:status => 0, :final_score => score)
          end
        end
        test_cases_time = format("%.3f",  (Time.now.to_f - t1.to_f)).to_f
        if record.present?
          consume_time = format("%.3f",  (Time.now - record.created_at)).to_f
          record.update_attributes!(:consume_time => consume_time, :git_pull => timeCost['pull'] ,
                                    :create_pod => timeCost['createPod'], :pod_execute => timeCost['execute'], :test_cases => test_cases_time,
                                    :brige => timeCost['evaluateAllTime'], :return_back => return_back_time)
        end
        logger.info("training_task_status start#4**#{game_id}**** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}")
        render :json => {:data => "success"}
      rescue Exception => e
        render :json => {:data => "failed, errer_message:#{e}"}
        logger.error("training_task_status error: #{e}")
        raise ActiveRecord::Rollback
      end
    end
  end

  def show
    respond_to do |format|
      format.html{redirect_to myshixun_game_path(@myshixun.current_task, :myshixun_id => @myshixun)}
    end
  end

  private
  # Find myshixun of id params[:id]
  def find_myshixun
    @myshixun = Myshixun.find_by_identifier(params[:id])
    render_404 unless @myshixun.present?
  end
end