#===============================================================================
#
#===============================================================================
class Player < Trainer
  attr_accessor :has_snag_machine
  attr_accessor :seen_purify_chamber

  alias __shadowPkmn__initialize initialize unless method_defined?(:__shadowPkmn__initialize)
  def initialize(name, trainer_type)
    __shadowPkmn__initialize(name, trainer_type)
    @has_snag_machine    = false
    @seen_purify_chamber = false
  end
end

class PokemonGlobalMetadata
  attr_writer :purifyChamber

  def purifyChamber
    @purifyChamber = PurifyChamber.new() if !@purifyChamber
    return @purifyChamber
  end

  # @deprecated Use {Player#seen_purify_chamber} instead. This alias is slated to be removed in v20.
  def seenPurifyChamber
    Deprecation.warn_method('PokemonGlobalMetadata#seenPurifyChamber', 'v20', '$Trainer.seen_purify_chamber')
    return @seenPurifyChamber if !@seenPurifyChamber.nil?
    return ($Trainer) ? $Trainer.seen_purify_chamber : false
  end

  # @deprecated Use {Player#seen_purify_chamber=} instead. This alias is slated to be removed in v20.
  def seenPurifyChamber=(value)
    Deprecation.warn_method('PokemonGlobalMetadata#seenPurifyChamber=', 'v20', '$Trainer.seen_purify_chamber=')
    if value.nil?
      @seenPurifyChamber = value   # For setting to nil by a save data conversion
    else
      $Trainer.seen_purify_chamber = value
    end
  end

  # @deprecated Use {Player#has_snag_machine} instead. This alias is slated to be removed in v20.
  def snagMachine
    Deprecation.warn_method('PokemonGlobalMetadata#snagMachine', 'v20', '$Trainer.has_snag_machine')
    return @snagMachine if !@snagMachine.nil?
    return ($Trainer) ? $Trainer.has_snag_machine : false
  end

  # @deprecated Use {Player#has_snag_machine=} instead. This alias is slated to be removed in v20.
  def snagMachine=(value)
    Deprecation.warn_method('PokemonGlobalMetadata#snagMachine=', 'v20', '$Trainer.has_snag_machine=')
    if value.nil?
      @snagMachine = value   # For setting to nil by a save data conversion
    else
      $Trainer.has_snag_machine = value
    end
  end
end

#===============================================================================
# General purpose utilities
#===============================================================================
def pbDrawGauge(bitmap,rect,color,value,maxValue)
  return if !bitmap
  bitmap.fill_rect(rect.x,rect.y,rect.width,rect.height,Color.new(0,0,0))
  width=(maxValue<=0) ? 0 : (rect.width-4)*value/maxValue
  if rect.width>=4 && rect.height>=4
    bitmap.fill_rect(rect.x+2,rect.y+2,rect.width-4,rect.height-4,Color.new(248,248,248))
    bitmap.fill_rect(rect.x+2,rect.y+2,width,rect.height-4,color)
  end
end

def calcPoint(x,y,distance,angle) # angle in degrees
  angle -=(angle/360.0).floor*360 # normalize
  angle = (angle/360.0)*(2*Math::PI) # convert to radians
  angle = -angle % (2*Math::PI) # normalize radians
  point = [(Math.cos(angle) * distance), (Math.sin(angle) * distance)]
  point[0] += x
  point[1] += y
  return point
end

#===============================================================================
#
#===============================================================================
class PurifyChamberSet
  attr_reader :shadow   # The Shadow Pokémon in the middle
  attr_reader :facing   # Index in list of Pokémon the Shadow Pokémon is facing

  def partialSum(x)
    return (x*x+x)/2   # pattern: 1, 3, 6, 10, 15, 21, 28, ...
  end

  def length
    return @list.length
  end

  def initialize
    @list=[]
    @facing=0
  end

  def facing=(value)
    if value>=0 && value<@list.length
      @facing=value
    end
  end

  def shadow=(value)
    if value==nil || value.shadowPokemon?
      @shadow=value
    end
  end

  # Main component is tempo
  # Boosted if center has advantage over facing Pokemon
  # Boosted based on number of best circles
  def flow
    ret=0
    return 0 if !@shadow
    for i in 0...@list.length
      ret+=(PurifyChamberSet.isSuperEffective(@list[i],@list[(i+1)%@list.length])) ? 1 : 0
    end
    if @list[@facing]
      ret+=PurifyChamberSet.isSuperEffective(@shadow,@list[@facing]) ? 1 : 0
    end
    return [ret+(@list.length/2),4].min
  end

  def shadowAffinity
    return 0 if @facing<0 || @facing>=@list.length || !@shadow
    return (PurifyChamberSet.isSuperEffective(@shadow,@list[@facing])) ? 2 : 1
  end

  def affinity(i)
    return 0 if i<0 || i>=@list.length
    return (PurifyChamberSet.isSuperEffective(@list[i],@list[(i+1)%@list.length])) ? 2 : 1
  end

  # Tempo refers to the type advantages of each Pokemon in a certain set in a
  # clockwise direction. Tempo also depends on the number of Pokemon in the set
  def tempo
    ret=0
    for i in 0...@list.length
      ret+=(PurifyChamberSet.isSuperEffective(@list[i],@list[(i+1)%@list.length])) ? 1 : 0
    end
    return partialSum(@list.length)+ret
  end

  def list
    return @list.clone
  end

  def [](index)
    return @list[index]
  end

  def insertAfter(index,value)
    return if self.length>=PurifyChamber::SETSIZE
    return if index<0 || index>=PurifyChamber::SETSIZE
    unless value && value.shadowPokemon?
      @list.insert(index+1,value)
      @list.compact!
      @facing+=1 if @facing>index && value!=nil
      @facing=[[@facing,@list.length-1].min,0].max
    end
  end

  def insertAt(index,value)
    return if index<0 || index>=PurifyChamber::SETSIZE
    unless value && value.shadowPokemon?
      @list[index]=value
      @list.compact!
      @facing=[[@facing,@list.length-1].min,0].max
    end
  end

  # Purify Chamber treats Normal/Normal matchup as super effective
  def self.typeAdvantage(p1,p2)
    return true if p1 == :NORMAL && p2 == :NORMAL
    return Effectiveness.super_effective_type?(p1, p2)
  end

  def self.isSuperEffective(p1,p2)
    return (typeAdvantage(p1.type1,p2.type1) || typeAdvantage(p1.type1,p2.type2))
  end
end

#===============================================================================
#
#===============================================================================
class PurifyChamber
  attr_reader :sets
  attr_reader :currentSet
  NUMSETS = 9
  SETSIZE = 4

  def self.maximumTempo()   # Calculates the maximum possible tempo
    x=SETSIZE+1
    return ((x*x+x)/2)-1
  end

  def initialize
    @sets=[]
    @currentSet=0
    NUMSETS.times { |i| @sets[i]=PurifyChamberSet.new }
  end

  def currentSet=(value)
    @currentSet=value if value>=0 && value<NUMSETS
  end

  # Number of regular Pokemon in a set
  def setCount(set)
    return @sets[set].length
  end

  def setList(set)
    return [] if set<0 || set>=NUMSETS
    return @sets[set].list
  end

  def chamberFlow(chamber)   # for speeding up purification
    return 0 if chamber<0 || chamber>=NUMSETS
    return @sets[chamber].flow
  end

  def getShadow(chamber)
    return nil if chamber<0 || chamber>=NUMSETS
    return @sets[chamber].shadow
  end

  def setShadow(chamber,value)   # allow only "shadow" Pokemon
    return if chamber<0 || chamber>=NUMSETS
    @sets[chamber].shadow=value
  end

  def switch(set1,set2)
    return if set1<0 || set1>=NUMSETS
    return if set2<0 || set2>=NUMSETS
    s=@sets[set1]
    @sets[set1]=@sets[set2]
    @sets[set2]=s
  end

  def insertAfter(set,index,value)
    return if set<0 || set>=NUMSETS
    @sets[set].insertAfter(index,value)
  end

  def insertAt(set,index,value)
    return if set<0 || set>=NUMSETS
    @sets[set].insertAt(index,value)
  end

  def [](chamber,slot=nil)
    if slot==nil
      return @sets[chamber]
    end
    return nil if chamber<0 || chamber>=NUMSETS
    return nil if slot<0 || slot>=SETSIZE
    return @sets[chamber][slot]
  end

  def isPurifiableIgnoreRegular?(set)
    shadow=getShadow(set)
    return false if !shadow
    return false if shadow.heart_gauge != 0
    # Define an exception for Lugia
    if shadow.isSpecies?(:LUGIA)
      maxtempo=PurifyChamber.maximumTempo()
      for i in 0...NUMSETS
        return false if @sets[i].tempo!=maxtempo
      end
    end
    return true
  end

  def isPurifiable?(set)
    isPurifiableIgnoreRegular?(set) && setCount(set)>0
  end

  def update # called each step
    for set in 0...NUMSETS
      # If a shadow Pokemon and a regular Pokemon are on the same set
      if @sets[set].shadow
        if @sets[set].shadow.heart_gauge > 0
          flow=self.chamberFlow(set)
          @sets[set].shadow.adjustHeart(-flow)
          if isPurifiable?(set)
            pbMessage(_INTL("Your {1} in the Purify Chamber is ready for purification!",@sets[set].shadow.name))
          end
        end
      end
    end
  end

  def debugAddShadow(set,species)
    pkmn=Pokemon.new(species,1)
    pkmn.makeShadow
    setShadow(set,pkmn)
  end

  def debugAddNormal(set,species)
    pkmn=Pokemon.new(species,1)
    insertAfter(set,setCount(set),pkmn)
  end

  def debugAdd(set,shadow,type1,type2=nil)
    pkmn=PseudoPokemon.new(shadow,type1,type2||type1)
    if pkmn.shadowPokemon?
      self.setShadow(set,pkmn)
    else
      self.insertAfter(set,setCount(set),pkmn)
    end
  end
end

#===============================================================================
#
#===============================================================================
module PurifyChamberHelper
  def self.pbGetPokemon2(chamber,set,position)
    if position==0
      return chamber.getShadow(set)
    elsif position>0
      position-=1
      if position%2==0
        return chamber[set,position/2]
      else # In between two indices
        return nil
      end
    end
    return nil
  end

  def self.pbGetPokemon(chamber,position)
    if position==0
      return chamber.getShadow(chamber.currentSet)
    elsif position>0
      position-=1
      if position%2==0
        return chamber[chamber.currentSet,position/2]
      else # In between two indices
        return nil
      end
    end
    return nil
  end

  def self.adjustOnInsert(position)
    if position>0
      position-=1
      oldpos=position/2
      if position%2==0
        return position+1
      else
        return ((oldpos+1)*2)+1
      end
    end
    return position
  end

  def self.pbSetPokemon(chamber,position,value)
    if position==0
      chamber.setShadow(chamber.currentSet,value)
    elsif position>0
      position-=1
      if position%2==0
        chamber.insertAt(chamber.currentSet,position/2,value)
      else # In between two indices
        chamber.insertAfter(chamber.currentSet,position/2,value)
      end
    end
  end
end

#===============================================================================
#
#===============================================================================
class PurifyChamberScreen
  def initialize(scene)
    @scene=scene
    @chamber=$PokemonGlobal.purifyChamber
#    for j in 0...PurifyChamber::NUMSETS
#      @chamber.debugAddShadow(j,rand(100)+1)
#      @chamber[j].shadow.heart_gauge = 0
#      for i in 0...PurifyChamber::SETSIZE
#        @chamber.debugAddNormal(j,rand(100)+1)
#      end
#    end
  end

  def pbPlace(pkmn,position)
    return false if !pkmn
    if pkmn.egg?
      @scene.pbDisplay(_INTL("Can't place an egg there."))
      return false
    end
    if (position==0)
      if pkmn.shadowPokemon?
        # Remove from storage and place in set
        oldpkmn=PurifyChamberHelper.pbGetPokemon(@chamber,position)
        if oldpkmn
          @scene.pbShift(position,pkmn)
        else
          @scene.pbPlace(position,pkmn)
        end
        PurifyChamberHelper.pbSetPokemon(@chamber,position,pkmn)
        @scene.pbRefresh
      else
        @scene.pbDisplay(_INTL("Only a Shadow Pokémon can go there."))
        return false
      end
    elsif (position>=1)
      if pkmn.shadowPokemon?
        @scene.pbDisplay(_INTL("Can't place a Shadow Pokémon there."))
        return false
      else
        oldpkmn=PurifyChamberHelper.pbGetPokemon(@chamber,position)
        if oldpkmn
          @scene.pbShift(position,pkmn)
        else
          @scene.pbPlace(position,pkmn)
        end
        PurifyChamberHelper.pbSetPokemon(@chamber,position,pkmn)
        @scene.pbRefresh
      end
    end
    return true
  end

  def pbPlacePokemon(pos,position)
    return false if !pos
    pkmn=$PokemonStorage[pos[0],pos[1]]
    if pbPlace(pkmn,position)
      $PokemonStorage.pbDelete(pos[0],pos[1])
      return true
    end
    return false
  end

  def pbOnPlace(pkmn)
    set=@chamber.currentSet
    if @chamber.setCount(set)==0 && @chamber.isPurifiableIgnoreRegular?(set)
      pkmn=@chamber.getShadow(set)
      @scene.pbDisplay(
         _INTL("This {1} is ready to open its heart. However, there must be at least one regular Pokémon in the set to perform a purification ceremony.",pkmn.name))
    end
  end

  def pbOpenSetDetail
    chamber=@chamber
    @scene.pbOpenSetDetail(chamber.currentSet)
    heldpkmn=nil
    loop do
      # Commands
      # array[0]==0 - a position was chosen
      # array[0]==1 - a new set was chosen
      # array[0]==2 - choose Pokemon command
      cmd=@scene.pbSetScreen()
      if cmd[0]==0
        # Place Pokemon in the set
        curpkmn=PurifyChamberHelper.pbGetPokemon(@chamber,cmd[1])
        if curpkmn || heldpkmn
          commands=[_INTL("MOVE"),_INTL("SUMMARY"),
             _INTL("WITHDRAW")]
          if curpkmn && heldpkmn
            commands[0]=_INTL("EXCHANGE")
          elsif heldpkmn
            commands[0]=_INTL("PLACE")
          end
          cmdReplace=-1
          cmdRotate=-1
          if !heldpkmn && curpkmn && cmd[1]==0 &&
             @chamber[@chamber.currentSet].length>0
            commands[cmdRotate=commands.length]=_INTL("ROTATE")
          end
          if !heldpkmn && curpkmn
            commands[cmdReplace=commands.length]=_INTL("REPLACE")
          end
          commands.push(_INTL("CANCEL"))
          choice=@scene.pbShowCommands(
             _INTL("What shall I do with this {1}?",
             heldpkmn ? heldpkmn.name : curpkmn.name),commands)
          if choice==0
            if heldpkmn
              if pbPlace(heldpkmn,cmd[1]) # calls place or shift as appropriate
                if !curpkmn
                  pbOnPlace(heldpkmn)
                  @scene.pbPositionHint(PurifyChamberHelper.adjustOnInsert(cmd[1]))
                  heldpkmn=nil # Pokemon was placed
                else
                  heldpkmn=curpkmn # Pokemon was shifted
                end
              end
            else
              @scene.pbMove(cmd[1])
              PurifyChamberHelper.pbSetPokemon(@chamber,cmd[1],nil)
              @scene.pbRefresh
              heldpkmn=curpkmn
            end
          elsif choice==1
            @scene.pbSummary(cmd[1],heldpkmn)
          elsif choice==2
            if pbBoxesFull?
              @scene.pbDisplay("All boxes are full.")
            elsif heldpkmn
              @scene.pbWithdraw(cmd[1],heldpkmn)
              $PokemonStorage.pbStoreCaught(heldpkmn)
              heldpkmn=nil
              @scene.pbRefresh
            else
              # Store and delete Pokemon.
              @scene.pbWithdraw(cmd[1],heldpkmn)
              $PokemonStorage.pbStoreCaught(curpkmn)
              PurifyChamberHelper.pbSetPokemon(@chamber,cmd[1],nil)
              @scene.pbRefresh
            end
          elsif cmdRotate>=0 && choice==cmdRotate
            count=@chamber[@chamber.currentSet].length
            nextPos=@chamber[@chamber.currentSet].facing
            if count>0
              @scene.pbRotate((nextPos+1)%count)
              @chamber[@chamber.currentSet].facing=(nextPos+1)%count
              @scene.pbRefresh
            end
          elsif cmdReplace>=0 && choice==cmdReplace
            pos=@scene.pbChoosePokemon
            if pos
              newpkmn=$PokemonStorage[pos[0],pos[1]]
              if newpkmn
                if (newpkmn.shadowPokemon?)!=(curpkmn.shadowPokemon?)
                  @scene.pbDisplay(_INTL("That Pokémon can't be placed there."))
                else
                  @scene.pbReplace(cmd,pos)
                  PurifyChamberHelper.pbSetPokemon(@chamber,cmd[1],newpkmn)
                  $PokemonStorage[pos[0],pos[1]]=curpkmn
                  @scene.pbRefresh
                  pbOnPlace(curpkmn)
                end
              end
            end
          end
        else  # No current Pokemon
          pos=@scene.pbChoosePokemon
          if pbPlacePokemon(pos,cmd[1])
            curpkmn=PurifyChamberHelper.pbGetPokemon(@chamber,cmd[1])
            pbOnPlace(curpkmn)
            @scene.pbPositionHint(PurifyChamberHelper.adjustOnInsert(cmd[1]))
          end
        end
      elsif cmd[0]==1 # Change the active set
        @scene.pbChangeSet(cmd[1])
        chamber.currentSet=cmd[1]
      elsif cmd[0]==2 # Choose a Pokemon
        pos=@scene.pbChoosePokemon()
        pkmn=pos ? $PokemonStorage[pos[0],pos[1]] : nil
        heldpkmn=pkmn if pkmn
      else # cancel
        if heldpkmn
          @scene.pbDisplay("You're holding a Pokémon!")
        else
          if !@scene.pbConfirm("Continue editing sets?")
            break
          end
        end
      end
    end
    if pbCheckPurify()
      @scene.pbDisplay(_INTL("There is a Pokémon that is ready to open its heart!\1"))
      @scene.pbCloseSetDetail()
      pbDoPurify()
      return false
    else
      @scene.pbCloseSetDetail()
      return true
    end
  end

  def pbDisplay(msg)
    @scene.pbDisplay(msg)
  end

  def pbConfirm(msg)
    @scene.pbConfirm(msg)
  end

  def pbRefresh
    @scene.pbRefresh
  end

  def pbCheckPurify
    purifiables=[]
    for set in 0...PurifyChamber::NUMSETS
      if @chamber.isPurifiable?(set) # if ready for purification
        purifiables.push(set)
      end
    end
    return purifiables.length>0
  end

  def pbDoPurify
    purifiables=[]
    for set in 0...PurifyChamber::NUMSETS
      if @chamber.isPurifiable?(set) # if ready for purification
        purifiables.push(set)
      end
    end
    for i in 0...purifiables.length
      set=purifiables[i]
      @chamber.currentSet=set
      @scene.pbOpenSet(set)
      @scene.pbPurify()
      pbPurify(@chamber[set].shadow,self)
      pbStorePokemon(@chamber[set].shadow)
      @chamber.setShadow(set,nil) # Remove shadow Pokemon from set
      if (i+1)!=purifiables.length
        @scene.pbDisplay(_INTL("There is another Pokémon that is ready to open its heart!"))
        if !@scene.pbConfirm("Would you like to switch sets?")
          @scene.pbCloseSet()
          break
        end
      end
      @scene.pbCloseSet()
    end
  end

  def pbStartPurify
    chamber=@chamber
    @scene.pbStart(chamber)
    if pbCheckPurify()
      pbDoPurify()
      @scene.pbEnd()
      return
    end
    @scene.pbOpenSet(chamber.currentSet)
    loop do
      set=@scene.pbChooseSet
      if set<0
        if !@scene.pbConfirm("Continue viewing holograms?")
          break
        end
      else
        chamber.currentSet=set
        cmd=@scene.pbShowCommands(
           _INTL("What do you want to do?"),[
           _INTL("EDIT"),_INTL("SWITCH"),_INTL("CANCEL")])
        if cmd==0 # edit
          if !pbOpenSetDetail()
            break
          end
        elsif cmd==1 # switch
          chamber.currentSet=set
          newSet=@scene.pbSwitch(set)
          chamber.switch(set,newSet)
          chamber.currentSet=newSet
          @scene.pbRefresh()
        end
      end
    end
    @scene.pbCloseSet()
    @scene.pbEnd()
  end
end

#===============================================================================
#
#===============================================================================
class Window_PurifyChamberSets < Window_DrawableCommand
  attr_reader :switching

  def initialize(chamber,x,y,width,height,viewport=nil)
    @chamber=chamber
    @switching=-1
    super(x,y,width,height,viewport)
  end

  def itemCount
    return PurifyChamber::NUMSETS
  end

  def switching=(value)
    @switching = value
    refresh
  end

  def drawItem(index,_count,rect)
    textpos=[]
    rect=drawCursor(index,rect)
    if index==@switching
      textpos.push([(index+1).to_s,rect.x,
         rect.y-6,false,Color.new(248,0,0),self.shadowColor])
    else
      textpos.push([(index+1).to_s,rect.x,
         rect.y-6,false,self.baseColor,self.shadowColor])
    end
    if @chamber.setCount(index)>0
      pbDrawGauge(self.contents,Rect.new(rect.x+16,rect.y+6,48,8),
         Color.new(0,0,256),@chamber[index].tempo,PurifyChamber.maximumTempo())
    end
    if @chamber.getShadow(index)
      pbDrawGauge(self.contents, Rect.new(rect.x+16,rect.y+18,48,8),
                  Color.new(192,0,256), @chamber.getShadow(index).heart_gauge,
                  Pokemon::HEART_GAUGE_SIZE)
    end
    pbDrawTextPositions(self.contents,textpos)
  end
end

#===============================================================================
#
#===============================================================================
class DirectFlowDiagram
  def initialize(viewport=nil)
    @points=[]
    @angles=[]
    @viewport=viewport
    @strength=0
    @offset=0
    @x=306
    @y=138
    @distance=96
  end

# 0=none, 1=weak, 2=strong
  def setFlowStrength(strength)
    @strength=strength
  end

  def visible=(value)
    for point in @points
      point.visible=value
    end
  end

  def dispose
    for point in @points
      point.dispose
    end
  end

  def ensurePoint(j)
    if !@points[j] || @points[j].disposed?
      @points[j]=BitmapSprite.new(8,8,@viewport)
      @points[j].bitmap.fill_rect(0,0,8,8,Color.new(0,0,0))
    end
    @points[j].tone=(@strength==2) ? Tone.new(232,232,248) :
       Tone.new(16,16,232)
    @points[j].visible=(@strength!=0)
  end

  def update
    for point in @points
      point.update
      point.visible=false
    end
    j=0
    i=0
    while i<@distance
      o=(i+@offset)%@distance
      if o>=0 && o<@distance
        ensurePoint(j)
        pt=calcPoint(@x,@y,o,@angle)
        @points[j].x=pt[0]
        @points[j].y=pt[1]
        j+=1
      end
      i+=(@strength==2) ? 16 : 32
    end
    @offset+=(@strength==2) ? 3 : 2
    @offset%=@distance
  end

  def color=(value)
    for point in @points
      point.color=value
    end
  end

  def setAngle(angle1)
    @angle=angle1-(angle1/360).floor*360
  end
end

#===============================================================================
#
#===============================================================================
class FlowDiagram
  def initialize(viewport=nil)
    @points=[]
    @angles=[]
    @viewport=viewport
    @strength=0
    @offset=0
    @x=306
    @y=138
    @distance=96
  end

# 0=none, 1=weak, 2=strong
  def setFlowStrength(strength)
    @strength=strength
  end

  def visible=(value)
    for point in @points
      point.visible=value
    end
  end

  def dispose
    for point in @points
      point.dispose
    end
  end

  def ensurePoint(j)
    if !@points[j] || @points[j].disposed?
      @points[j]=BitmapSprite.new(8,8,@viewport)
      @points[j].bitmap.fill_rect(0,0,8,8,Color.new(0,0,0))
    end
    @points[j].tone=(@strength==2) ? Tone.new(232,232,248) :
       Tone.new(16,16,232)
    @points[j].visible=(@strength!=0)
  end

  def withinRange(angle,startAngle,endAngle)
    if startAngle>endAngle
      return (angle>=startAngle || angle<=endAngle) &&
         (angle>=0 && angle<=360)
    else
      return (angle>=startAngle && angle<=endAngle)
    end
  end

  def update
    for point in @points
      point.update
      point.visible=false
    end
    j=0
    i=0
    while i<360
      angle=(i+@offset)%360
      if withinRange(angle,@startAngle,@endAngle)
        ensurePoint(j)
        pt=calcPoint(@x,@y,@distance,angle)
        @points[j].x=pt[0]
        @points[j].y=pt[1]
        j+=1
      end
      i+=(@strength==2) ? 10 : 20
    end
    @offset-=(@strength==2) ? 3 : 2
    @offset%=(360*6)
  end

  def color=(value)
    for point in @points
     point.color=value
    end
  end

  def setRange(angle1,angle2)
    @startAngle=angle1-(angle1/360).floor*360
    @endAngle=angle2-(angle2/360).floor*360
    if @startAngle==@endAngle && angle1!=angle2
      @startAngle=0
      @endAngle=359.99
    end
  end
end

#===============================================================================
#
#===============================================================================
class PurifyChamberSetView < SpriteWrapper
  attr_reader :set
  attr_reader :cursor
  attr_reader :heldpkmn

  def initialize(chamber,set,viewport=nil)
    super(viewport)
    @set=set
    @heldpkmn=nil
    @cursor=-1
    @view=BitmapSprite.new(64,64,viewport)
    @view.bitmap.fill_rect(8,8,48,48,Color.new(255,255,255))
    @view.bitmap.fill_rect(10,10,44,44,Color.new(255,255,255,128))
    @info=BitmapSprite.new(Graphics.width-112,48,viewport)
    @flows=[]
    @directflow=DirectFlowDiagram.new(viewport)
    @directflow.setAngle(0)
    @directflow.setFlowStrength(1)
    @__sprites=[]
    @__sprites[0]=PokemonIconSprite.new(nil,viewport)
    for i in 0...PurifyChamber::SETSIZE*2
      @__sprites[i+1]=PokemonIconSprite.new(nil,viewport)
    end
    @__sprites[1+PurifyChamber::SETSIZE*2]=PokemonIconSprite.new(nil,viewport)
    @chamber=chamber
    refresh
  end

  def refreshFlows()
    for flow in @flows
      flow.setFlowStrength(0)
    end
    setcount=@chamber.setCount(@set)
    for i in 0...setcount
      if !@flows[i]
        @flows[i]=FlowDiagram.new(self.viewport)
      end
      angle=360-(i*360/setcount)
      angle+=90 # start at 12 not 3 o'clock
      endAngle=angle-(360/setcount)
      @flows[i].setRange(endAngle,angle)
      @flows[i].setFlowStrength(@chamber[@set].affinity(i))
    end
  end

  def moveCursor(button)
    points=[@chamber.setCount(@set)*2,1].max
    oldcursor=@cursor
    if @cursor==0 && points>0
      @cursor=1 if button==Input::UP
      @cursor=points*1/4+1 if button==Input::RIGHT
      @cursor=points*2/4+1 if button==Input::DOWN
      @cursor=points*3/4+1 if button==Input::LEFT
    elsif @cursor>0
      pos=@cursor-1
      if @chamber.setCount(@set)==PurifyChamber::SETSIZE
        points=[points/2,1].max
        pos/=2
      end
      seg=pos*8/points
      if seg==7 || seg==0
        pos-=1 if button==Input::LEFT
        pos+=1 if button==Input::RIGHT
        pos=nil if button==Input::DOWN
      elsif seg==1 || seg==2
        pos-=1 if button==Input::UP
        pos+=1 if button==Input::DOWN
        pos=nil if button==Input::LEFT
      elsif seg==3 || seg==4
        pos-=1 if button==Input::RIGHT
        pos+=1 if button==Input::LEFT
        pos=nil if button==Input::UP
      elsif seg==5 || seg==6
        pos-=1 if button==Input::DOWN
        pos+=1 if button==Input::UP
        pos=nil if button==Input::RIGHT
      end
      if pos!=nil
        pos-=(pos/points).floor*points # modulus
        pos*=2 if @chamber.setCount(@set)==PurifyChamber::SETSIZE
        @cursor=pos+1
      else
        @cursor=0
      end
    end
    if @cursor!=oldcursor
      refresh
    end
  end

  def checkCursor(index)
    if @cursor==index
      @view.x=@__sprites[index].x-32
      @view.y=@__sprites[index].y-40
      @view.visible=true
    end
  end

  def refresh
    pkmn=@chamber.getShadow(@set)
    @view.visible=false
    @info.bitmap.fill_rect(0,0,@info.bitmap.width,@info.bitmap.height,Color.new(0,248,0))
    pbSetSmallFont(@info.bitmap)
    textpos=[]
    if pkmn
      if pkmn.type1==pkmn.type2
        textpos.push([_INTL("{1}  Lv.{2}  {3}",pkmn.name,pkmn.level,GameData::Type.get(pkmn.type1).name),2,-6,0,
           Color.new(248,248,248),Color.new(128,128,128)])
      else
        textpos.push([_INTL("{1}  Lv.{2}  {3}/{4}",pkmn.name,pkmn.level,GameData::Type.get(pkmn.type1).name,
           GameData::Type.get(pkmn.type2).name),2,-6,0,
           Color.new(248,248,248),Color.new(128,128,128)])
      end
      textpos.push([_INTL("FLOW"),2+@info.bitmap.width/2,18,0,
         Color.new(248,248,248),Color.new(128,128,128)])
      # draw heart gauge
      pbDrawGauge(@info.bitmap, Rect.new(@info.bitmap.width*3/4,8,@info.bitmap.width*1/4,8),
                  Color.new(192,0,256), pkmn.heart_gauge, Pokemon::HEART_GAUGE_SIZE)
      # draw flow gauge
      pbDrawGauge(@info.bitmap,Rect.new(@info.bitmap.width*3/4,24+8,@info.bitmap.width*1/4,8),
         Color.new(0,0,248),@chamber.chamberFlow(@set),6)
    end
    if @chamber.setCount(@set)>0
      textpos.push([_INTL("TEMPO"),2,18,0,
         Color.new(248,248,248),Color.new(128,128,128)])
      # draw tempo gauge
      pbDrawGauge(@info.bitmap,Rect.new(@info.bitmap.width*1/4,24+8,@info.bitmap.width*1/4,8),
         Color.new(0,0,248),@chamber[@set].tempo,
         PurifyChamber.maximumTempo())
    end
    pbDrawTextPositions(@info.bitmap,textpos)
    @info.x=Graphics.width-@info.bitmap.width
    @info.y=Graphics.height-@info.bitmap.height
    @__sprites[0].pokemon=pkmn
    @__sprites[0].x=312
    @__sprites[0].y=136
    @__sprites[0].z=2
    @directflow.setAngle(angle)
    @directflow.setFlowStrength(0)
    checkCursor(0)
    points=[@chamber.setCount(@set)*2,1].max
    setList=@chamber.setList(@set)
    refreshFlows()
    for i in 0...PurifyChamber::SETSIZE*2
      pkmn=(i%2==1 || i>=points) ? nil : setList[i/2]
      angle=360-(i*360/points)
      angle+=90 # start at 12 not 3 o'clock
      if pkmn && @chamber[@set].facing==i/2
        @directflow.setAngle(angle)
        @directflow.setFlowStrength(@chamber[@set].shadowAffinity)
      end
      point=calcPoint(280,96,96,angle)
      @__sprites[i+1].x=point[0]+32
      @__sprites[i+1].y=point[1]+40
      @__sprites[i+1].z=2
      @__sprites[i+1].pokemon=pkmn
      checkCursor(i+1)
    end
    @__sprites[1+PurifyChamber::SETSIZE*2].pokemon=@heldpkmn
    @__sprites[1+PurifyChamber::SETSIZE*2].visible=@view.visible
    @__sprites[1+PurifyChamber::SETSIZE*2].x=@view.x+32
    @__sprites[1+PurifyChamber::SETSIZE*2].y=@view.y-6+40
    @__sprites[1+PurifyChamber::SETSIZE*2].z=3
  end

  def getCurrent
    return PurifyChamberHelper.pbGetPokemon(@chamber,@cursor)
  end

  def cursor=(value)
    @cursor=value
    refresh
  end

  def heldpkmn=(value)
    @heldpkmn=value
    refresh
  end

  def set=(value)
    @set=value
    refresh
  end

  def visible=(value)
    super
    for sprite in @__sprites
      sprite.visible=value
    end
    for flow in @flows
      flow.visible=value
    end
    @directflow.visible=value
    @view.visible=value
    @info.visible=value
  end

  def color=(value)
    super
    for sprite in @__sprites
      sprite.color=pbSrcOver(sprite.color,value.clone)
    end
    for flow in @flows
      flow.color=value.clone
    end
    @directflow.color=value.clone
    @view.color=value.clone
    @info.color=value.clone
  end

  def update
    super
    for sprite in @__sprites
      sprite.update if sprite
    end
    for flow in @flows
      flow.update
    end
    @directflow.update
    @view.update
    @info.update
  end

  def dispose
    for sprite in @__sprites
      sprite.dispose if sprite
    end
    for flow in @flows
      flow.dispose
    end
    @directflow.dispose
    @view.dispose
    @info.dispose
    @__sprites.clear
    super
  end
end

#===============================================================================
#
#===============================================================================
class PurifyChamberScene
  def pbUpdate()
    pbUpdateSpriteHash(@sprites)
  end

  def pbRefresh()
    if @sprites["setview"]
      @sprites["setview"].refresh
      @sprites["setwindow"].refresh
    end
  end

  def pbStart(chamber)
    @chamber=chamber
  end

  def pbEnd()
  end

  def pbOpenSet(set)
    @sprites={}
    @viewport=Viewport.new(0,0,Graphics.width,Graphics.height)
    @viewport.z=99999
    @viewportmsg=Viewport.new(0,0,Graphics.width,Graphics.height)
    @viewportmsg.z=99999
    addBackgroundOrColoredPlane(@sprites,"bg","purifychamberbg",
       Color.new(0,0,0),@viewport)
    @sprites["setwindow"]=Window_PurifyChamberSets.new(
       @chamber,0,0,112,Graphics.height,@viewport)
    @sprites["setview"]=PurifyChamberSetView.new(@chamber,set,@viewport)
    @sprites["msgwindow"]=Window_AdvancedTextPokemon.new("")
    @sprites["msgwindow"].viewport=@viewportmsg
    @sprites["msgwindow"].visible=false
    @sprites["setwindow"].index=set
    pbDeactivateWindows(@sprites)
    pbFadeInAndShow(@sprites) { pbUpdate }
  end

  def pbCloseSet()
    pbFadeOutAndHide(@sprites) { pbUpdate }
    pbDisposeSpriteHash(@sprites)
    @viewport.dispose
    @viewportmsg.dispose
  end

  def pbOpenSetDetail(set)
    @sprites["setwindow"].index=set
    @sprites["setview"].set=set
    @sprites["setview"].cursor=0
  end

  def pbCloseSetDetail()
  end

  def pbPurify()
    pbRefresh
  end

  def pbMove(_pos)
    @sprites["setview"].heldpkmn=@sprites["setview"].getCurrent()
    pbRefresh
  end

  def pbShift(_pos,_heldpoke)
    @sprites["setview"].heldpkmn=@sprites["setview"].getCurrent()
    pbRefresh
  end

  def pbPlace(_pos,_heldpoke)
    @sprites["setview"].heldpkmn=nil
    pbRefresh
  end

  def pbReplace(_pos,_storagePos)
    @sprites["setview"].heldpkmn=nil
    pbRefresh
  end

  def pbRotate(facing); end

  def pbWithdraw(_pos,_heldpoke)
    @sprites["setview"].heldpkmn=nil
    pbRefresh
  end

  def pbDisplay(msg)
    UIHelper.pbDisplay(@sprites["msgwindow"],msg,false) { pbUpdate }
  end

  def pbConfirm(msg)
    UIHelper.pbConfirm(@sprites["msgwindow"],msg) { pbUpdate }
  end

  def pbShowCommands(msg,commands)
    UIHelper.pbShowCommands(@sprites["msgwindow"],msg,commands) { pbUpdate }
  end

  def pbSetScreen
    pbDeactivateWindows(@sprites) {
      loop do
        Graphics.update; Input.update; pbUpdate
        btn=0
        btn=Input::DOWN if Input.repeat?(Input::DOWN)
        btn=Input::UP if Input.repeat?(Input::UP)
        btn=Input::RIGHT if Input.repeat?(Input::RIGHT)
        btn=Input::LEFT if Input.repeat?(Input::LEFT)
        if btn!=0
          pbPlayCursorSE()
          @sprites["setview"].moveCursor(btn)
        end
        if Input.repeat?(Input::JUMPUP)
          nextset=(@sprites["setview"].set==0) ? PurifyChamber::NUMSETS-1 : @sprites["setview"].set-1
          pbPlayCursorSE()
          return [1,nextset]
        elsif Input.repeat?(Input::JUMPDOWN)
          nextset=(@sprites["setview"].set==PurifyChamber::NUMSETS-1) ? 0 : @sprites["setview"].set+1
          pbPlayCursorSE()
          return [1,nextset]
        elsif Input.trigger?(Input::USE)
          pbPlayDecisionSE()
          return [0,@sprites["setview"].cursor]
        elsif Input.trigger?(Input::BACK)
          pbPlayCancelSE()
          return [3,0]
        end
      end
    }
  end

  def pbChooseSet()
    pbActivateWindow(@sprites,"setwindow") {
      oldindex=@sprites["setwindow"].index
      loop do
        if oldindex!=@sprites["setwindow"].index
          oldindex=@sprites["setwindow"].index
          @sprites["setview"].set=oldindex
        end
        Graphics.update; Input.update; pbUpdate
        if Input.trigger?(Input::USE)
          pbPlayDecisionSE()
          return @sprites["setwindow"].index
        end
        if Input.trigger?(Input::BACK)
          pbPlayCancelSE()
          return -1
        end
      end
    }
  end

  def pbSwitch(set)
    @sprites["setwindow"].switching=set
    ret=pbChooseSet()
    @sprites["setwindow"].switching=-1
    return ret<0 ? set : ret
  end

  def pbSummary(pos,heldpkmn)
    if heldpkmn
      oldsprites=pbFadeOutAndHide(@sprites)
      scene=PokemonSummary_Scene.new
      screen=PokemonSummaryScreen.new(scene)
      screen.pbStartScreen([heldpkmn],0)
      pbFadeInAndShow(@sprites,oldsprites)
      return
    end
    party=[]
    indexes=[]
    startindex=0
    set=@sprites["setview"].set
    for i in 0...@chamber.setCount(set)*2
      p=PurifyChamberHelper.pbGetPokemon2(@chamber,set,i)
      if p
        startindex=party.length if i==pos
        party.push(p)
        indexes.push(i)
      end
    end
    return if party.length==0
    oldsprites=pbFadeOutAndHide(@sprites)
    scene=PokemonSummary_Scene.new
    screen=PokemonSummaryScreen.new(scene)
    selection=screen.pbStartScreen(party,startindex)
    @sprites["setview"].cursor=indexes[selection]
    pbFadeInAndShow(@sprites,oldsprites)
  end

  def pbPositionHint(pos)
    @sprites["setview"].cursor=pos
    pbRefresh
  end

  def pbChangeSet(set)
    @sprites["setview"].set=set
    @sprites["setwindow"].index=set
    @sprites["setwindow"].refresh
    pbRefresh
  end

  def pbChoosePokemon()
    visible=pbFadeOutAndHide(@sprites)
    scene=PokemonStorageScene.new()
    screen=PokemonStorageScreen.new(scene,$PokemonStorage)
    pos=screen.pbChoosePokemon()
    pbRefresh
    pbFadeInAndShow(@sprites,visible)
    return pos
  end
end

#===============================================================================
#
#===============================================================================
def pbPurifyChamber
  $Trainer.seen_purify_chamber = true
  pbFadeOutIn {
    scene = PurifyChamberScene.new
    screen = PurifyChamberScreen.new(scene)
    screen.pbStartPurify
  }
end

#===============================================================================
#
#===============================================================================
class PurifyChamberPC
  def shouldShow?
    return $Trainer.seen_purify_chamber
  end

  def name
    return _INTL("Purify Chamber")
  end

  def access
    pbMessage(_INTL("\\se[PC access]Accessed the Purify Chamber."))
    pbPurifyChamber()
  end
end

#===============================================================================
#
#===============================================================================
PokemonPCList.registerPC(PurifyChamberPC.new)
