RootView = require 'views/core/RootView'
template = require 'templates/artisans/solution-problems-view'

Level = require 'models/Level'
Campaign = require 'models/Campaign'

CocoCollection = require 'collections/CocoCollection'
Campaigns = require 'collections/Campaigns'
Levels = require 'collections/Levels'

module.exports = class SolutionProblemsView extends RootView
  template: template
  id: 'solution-problems-view'
  excludedCampaigns = [
    # Misc. campaigns
    'picoctf', 'auditions'

    # Campaign-version campaigns
    #'dungeon', 'forest', 'desert', 'mountain', 'glacier'

    # Test campaigns
    'dungeon-branching-test', 'forest-branching-test', 'desert-branching-test'

    # Course-version campaigns
    #'intro', 'course-2', 'course-3', 'course-4', 'course-5', 'course-6'
  ]
  excludedSimulationLevels = [
    # Course Arenas
    'wakka-maul', 'cross-bones'
  ]
  excludedSolutionLevels = [
    # Multiplayer Levels
    'cavern-survival'
    'dueling-grounds', 'multiplayer-treasure-grove'
    'harrowland'
    'zero-sum'
    'ace-of-coders', 'capture-their-flag'
  ]
  simulationRequirements = [
    'seed'
    'succeeds'
    'heroConfig'
    'frameCount'
    'goals'
  ]
  includedLanguages = [
    'python', 'javascript', 'java', 'lua', 'coffeescript'
  ]
  # TODO: Phase the following out:
  excludedLanguages = [
    'java', 'lua', 'coffeescript'
  ]
  excludedLevelSnippets = [
    'treasure', 'brawl', 'siege'
  ]

  unloadedCampaigns: 0
  campaignLevels: {}
  loadedLevels: {}
  parsedLevels: []
  problemCount: 0

  initialize: ->
    @campaigns = new Campaigns([])
    @listenTo(@campaigns, 'sync', @onCampaignsLoaded)
    @supermodel.trackRequest(@campaigns.fetch(
      data:
        project:'slug'
    ))

  onCampaignsLoaded: (campCollection) ->
    for campaign in campCollection.models
      campaignSlug = campaign.get('slug')
      continue if campaignSlug in excludedCampaigns
      @unloadedCampaigns++

      @campaignLevels[campaignSlug] = new Levels()
      @listenTo(@campaignLevels[campaignSlug], 'sync', @onLevelsLoaded)
      @supermodel.trackRequest(@campaignLevels[campaignSlug].fetchForCampaign(campaignSlug,
        data:
          project: 'thangs,name,slug,campaign'
      ))

  onLevelsLoaded: (lvlCollection) ->
    for level in lvlCollection.models
      @loadedLevels[level.get('slug')] = level
    if --@unloadedCampaigns is 0
      @onAllLevelsLoaded()

  onAllLevelsLoaded: ->
    for levelSlug, level of @loadedLevels
      unless level?
        console.error 'Level Slug doesn\'t have associated Level', levelSlug
        continue
      continue if levelSlug in excludedSolutionLevels
      isBad = false
      for word in excludedLevelSnippets
        if levelSlug.indexOf(word) isnt -1
          isBad = true
      continue if isBad
      thangs = level.get 'thangs'
      component = null
      thangs = _.filter(thangs, (elem) ->
        return _.findWhere(elem.components, (elem2) ->
          if elem2.config?.programmableMethods?
            component = elem2
            return true
        )
      )

      if thangs.length > 1
        unless levelSlug in excludedSimulationLevels
          console.warn 'Level has more than 1 programmableMethod Thangs', levelSlug
        continue
      unless component?
        console.error 'Level doesn\'t have programmableMethod Thang', levelSlug
        continue

      plan = component.config.programmableMethods.plan
      solutions = plan.solutions or []
      problems = []
      problems = problems.concat(@findMissingSolutions solutions)
      unless levelSlug in excludedSimulationLevels
        for solution in solutions
          problems = problems.concat(@findSimulationProblems solution)
          problems = problems.concat(@findPass solution)
          problems = problems.concat(@findIdenticalToSource solution, plan)
          problems = problems.concat(@findTemplateProblems solution, plan)
      @problemCount += problems.length
      @parsedLevels.push
        level: level
        problems: problems

    @renderSelectors '#level-table'

  findMissingSolutions: (solutions) ->
    problems = []
    for lang in includedLanguages
      if _.findWhere(solutions, (elem) -> return elem.language is lang)
      # TODO: Phase the following out:
      else if lang not in excludedLanguages
        problems.push
          type: 'Missing solution language'
          value: lang
    problems

  findSimulationProblems: (solution) ->
    problems = []
    for req in simulationRequirements
      unless solution[req]?
        problems.push
          type: 'Solution is not simulatable'
          value: solution.language
        break
    problems

  findPass: (solution) ->
    problems = []
    if solution.source.search(/pass\n/) isnt -1
      problems.push
        type: 'Solution contains pass'
        value: solution.language
    problems

  findIdenticalToSource: (solution, plan) ->
    problems = []
    source = if solution.lang is 'javascript' then plan.source else plan.languages[solution.language]
    if solution.source is source
      problems.push
        type: 'Solution matches sample code'
        value: solution.language
    problems

  findTemplateProblems: (solution, plan) ->
    problems = []
    source = if solution.lang is 'javascript' then plan.source else plan.languages[solution.language]
    context = plan.context
    try
      _.template(source, context)
    catch error
      console.log source, context, error
      problems.push
        type: 'Solution template syntax error'
        value: error.message
    problems
