-- ****************************************************************************
--   Handler for numeric replies and scripting commands
--   Event method is the entry method for messages
--
-- ****************************************************************************

::CLASS NumHandler
::METHOD Init
  expose client results
  use arg client
  results = .array~new

::METHOD Event
  use arg message
  do methodname over .local['IRC.EVENT.NUMRPL.'message~number]
    if self~hasmethod(methodname) then do
      .message~new(self,methodname,"I",message)~~send
      return
    end
  end

::METHOD RunMethod
  use arg name, mode, arguments
  self~start(name, mode, arguments)

::METHOD eventid PRIVATE
  expose results
  i = 1
  do while results[i] \= .nil
    i = i + 1
  end
  return i
::METHOD lastevent PRIVATE
  expose results
  i = 1
  do until results[i] = .nil
    i = i + 1
  end
  return i
-- =================================   
-- SCRIPTING METHODS                   
-- =================================   
::METHOD Say
  expose client
  use arg target, text
  text=text~strip
  if text~length = 0 then return
  client~writeln("PRIVMSG" target~Makestring ':'text)
-- make it possible for the client to track own PRIVMSG's
--  client~event(.local['IRC.EVENT.SELFSAY'],.array~of(client~nick,target,text))
::METHOD Notice
  expose client
  use arg target, text
  text=text~strip
  if text~length = 0 then return
  client~writeln("NOTICE" target~makestring ':'text)
-- make it possible for the client to track own NOTICE's
--  client~event(.local['IRC.EVENT.SELFNOTICE'],.array~of(client~nick,target,text))

::METHOD CTCPReply
  expose client
  use arg target, type, text
  text=text~strip; type = type~strip
  if type~length = 0 then return
  marker = '1'~d2c
  client~writeln('NOTICE' target~makestring ':'marker||type||' '||text||marker)
--  client~event(.local['IRC.EVENT.CTCPREPLY'],.array~of(target,type,text))

-- ================ WHOIS       =================================
::METHOD WhoIs
  expose results whois_done client
  parse arg nick, waittime
  client~writeln('WHOIS' nick)
  eventid = self~eventid
  whois_done = .false
  results[eventid] = .array~of("WHOIS",IRCUpper(nick~strip),.directory~new)
  if \datatype(waittime,'N') then waittime = .local['IRC.TIMEOUT.SCRIPTS']
  timeoutalarm = .alarm~new(waittime, .message~new(self, 'WHOISCANCEL'))
  guard off when whois_done
  timeoutalarm~cancel
  ret = results[eventid][3]
  results~remove(eventid)
  return ret
::METHOD WhoIsCancel
  expose whois_done
  whois_done = .true

::METHOD RPL_WHOISUSER
  expose client users results
  use arg message
  parse value message~string with me nick username host '*' ':' description
  nick = IRCUpper(nick~strip)
  do i = 1 to results~items
    if results[i] = .nil then iterate
    if results[i][1] = 'WHOIS' & results[i][2] = nick then do
      results[i][3]['DESCRIPTION'] = description
      results[i][3]['USERNAME'] = username
      results[i][3]['HOST'] = HOST
      leave
    end
  end

::METHOD RPL_ENDOFWHOIS
  expose events results users whois_done
  use arg message
  parse value message~string with me nick ':' .
  nick = IRCUpper(nick~strip)
  do i = 1 to results~items
    if results[i] = .nil then iterate
    if results[i][1] = 'WHOIS' & results[i][2] = nick then do
      whois_done = .true
      leave
    end
  end
  
::METHOD RPL_WHOISCHANNELS
  expose users results
  use arg message
  parse value message~string with me nick ':'channels
  nick = IRCUpper(nick~strip)
  do i = 1 to results~items
    if results[i] = .nil then iterate
    if results[i][1] = 'WHOIS' & results[i][2] = nick then
      if results[i][3]['CHANNELS'] = .nil then results[i][3]['CHANNELS'] = channels
      else results[i][3]['CHANNELS'] = results[i][3]['CHANNELS'] channels
  end

::METHOD RPL_WHOISSERVER
  expose results
  use arg message
  parse value message~string with me nick server ':'info
  nick = IRCUpper(nick~strip)
  do i = 1 to results~items
    if results[i] = .nil then iterate
    if results[i][1] = 'WHOIS' & results[i][2] = nick then do
      results[i][3]['SERVER'] = server
      results[i][3]['SERVERINFO'] = info
      leave
    end
    --  WHOWAS also returns the whois server line
    else if results[i][1] = 'WHOWAS' then do 
        results[i][2][results[i][2]~items][5] = server
        results[i][2][results[i][2]~items][6] = info
        leave
      end
  end

-- IDLE of WHOIS reply
::METHOD RPL_WHOISIDLE
  expose results
  use arg message
  parse value message~string with me nick idle signon ':' .
  nick = IRCUpper(nick~strip)
  do i = 1 to results~items
    if results[i] = .nil then iterate
    if results[i][1] = 'WHOIS' & results[i][2] = nick then do
      results[i][3]['IDLE'] = idle
      -- singnon is permitted by some servers
      results[i][3]['SIGNON'] = signon
      leave
    end
  end

-- ================ ISON        =================================
::METHOD IsOn
  expose results ison_done client
  parse arg nicks, waittime
  eventid = self~eventid
  ison_done = .false
  results[eventid] = .array~of("ISON","")
  if \datatype(waittime,'N') then waittime = .local['IRC.TIMEOUT.SCRIPTS']
  timeoutalarm = .alarm~new(waittime, .message~new(self, 'IsOnCancel'))
  client~writeln('ISON' nicks)
  guard off when ison_done \= .false
  timeoutalarm~cancel
  ret = results[eventid][2]
  results~remove(eventid)
  return ret

::METHOD RPL_IsOn
  expose results ison_done
  use arg message
  parse value message~string with . ':' nicks
  do i = 1 to results~items
    if results[i] = .nil then iterate
    if results[i][1] = 'ISON' then do
      results[i][2] = nicks
      ison_done = nicks
    end
  end

::METHOD IsOnCancel
  expose ison_done
  ison_done = .true

-- ================ JOIN        =================================
-- Joins a channel, doesn't return anything. Look for the channel in the
-- channel list to find out if it was successful
::METHOD Join 
  expose client
  parse arg channel, key
  say channel
  if IRCIsChannel(channel) then
    client~writeln('JOIN' channel key)

-- ================ TOPIC       =================================
-- Returns the topic of a channel or a null string
::METHOD Topic
  expose results topic_done client
  parse arg channel, waittime
  channel = IRCUpper(channel~strip)
  eventid = self~eventid
  topic_done = .false
  results[eventid] = .array~of("TOPIC",channel,"")
  if \datatype(waittime,'N') then waittime = .local['IRC.TIMEOUT.SCRIPTS']
  timeoutalarm = .alarm~new(waittime, .message~new(self, 'TopicCancel'))
  client~writeln('TOPIC' channel)
  guard off when topic_done \= .false
  timeoutalarm~cancel
  ret = results[eventid][3]
  results~remove(eventid)
  return ret
::METHOD RPL_Topic
  expose results topic_done
  use arg message
  parse value message~string with . channel':' topic
  channel = IRCUpper(channel~strip)
  do i = 1 to results~items
    if results[i] = .nil then iterate
    if results[i][1] = 'TOPIC' & results[i][2] = channel then do
      results[i][3] = topic
      topic_done = .true
    end
  end
::METHOD RPL_NoTopic
  expose results topic_done
  use arg message
  parse value message~string with . channel':'
  channel = IRCUpper(channel~strip)
  do i = 1 to results~items
    if results[i] = .nil then iterate
    if results[i][1] = 'TOPIC' & results[i][2] = channel then do
      topic_done = .true
    end
  end
::METHOD TopicCancel
  expose topic_done
  topic_done = .true
-- ================ LIST        =================================
-- returns a 2 dimensional array. array[n]=[channel,users,topic]
::METHOD List 
  expose results list_done client
  parse arg channels, waittime
  channels = IRCUpper(channels~space~translate(' ',','))
  eventid = self~eventid
  list_done = .false
  results[eventid] = .array~of("LIST",.array~new)
  if \datatype(waittime,'N') then waittime = .local['IRC.TIMEOUT.SCRIPTS']
  timeoutalarm = .alarm~new(waittime, .message~new(self, 'ListCancel'))
  client~writeln('LIST' channels)
  guard off when list_done \= .false
  timeoutalarm~cancel
  ret = results[eventid][2]
  results~remove(eventid)
  return ret

-- this reply is used by some servers but it's discarded here
::METHOD RPL_ListStart
::METHOD RPL_LIST
  expose results
  use arg message
  parse value message~string with . channel users ':' topic
  do i = 1 to results~items
    if results[i] = .nil then iterate
    if results[i][1] = 'LIST' then do
      results[i][2][results[i][2]~items+1] = .array~of(channel~strip,users~strip,topic~strip)
    end
  end
::METHOD RPL_ListEnd
  expose results list_done
  do i = 1 to results~items
    if results[i] = .nil then iterate
    if results[i][1] = 'LIST' then do
      list_done = .true
    end
  end
::METHOD ListCancel
  expose list_done
  list_done = .true
-- ================ TIME        =================================
::METHOD Time
  expose results time_done client
  parse arg server, waittime
  eventid = self~eventid
  time_done = .false
  results[eventid] = .array~of("TIME",.array~of('',''))
  if \datatype(waittime,'N') then waittime = .local['IRC.TIMEOUT.SCRIPTS']
  timeoutalarm = .alarm~new(waittime, .message~new(self, 'TimeCancel'))
  client~writeln('TIME' server)
  guard off when Time_done \= .false
  timeoutalarm~cancel
  ret = results[eventid][2]
  results~remove(eventid)
  return ret
::METHOD RPL_Time
    expose results Time_done
    use arg message
    parse value message~string with . server ':' time
    do i = 1 to results~items
	if results[i] = .nil then iterate
        if results[i][1] = 'TIME' then do
	    results[i][2]~~put(server~strip,1)~~put(time~strip,2)
	    time_done = .true
	end
    end
::METHOD TimeCancel
  expose time_done
  time_done = .true

-- ================ WHOWAS      =================================

-- Returns a 2 dimensional array, array one contains n arrays with 4 or 6 strings each.
-- array[n] = [nick, user, host, realname]
-- - or -
-- array[n] = [nick, user, host, realname,server,serverinfo]
-- Call: self~WhoWas(nick[,limit, target, waittime])
-- Example:
--  list = self~whowas("can",5)
--  if list = .nil then say "There was no such nick!"
--  else do i = 1 to list~items
--    do string over list[i]
--      call charout ,string' '
--    end
--    Say
--  end


::METHOD WhoWas
  expose results whowas_done client
  parse arg nick, limit, target, waittime
  eventid = self~eventid
  whowas_done = .false
  if \ datatype(limit,'N') then results[eventid] = .array~of("WHOWAS", .array~new)
    else results[eventid] = .array~of("WHOWAS", .array~new(limit))
  if \datatype(waittime,'N') then waittime = .local['IRC.TIMEOUT.SCRIPTS']
  timeoutalarm = .alarm~new(waittime, .message~new(self, 'whowasCancel'))
  client~writeln('WHOWAS' nick limit target)
  guard off when whowas_done \= .false
  timeoutalarm~cancel
  if results[eventid][2]~items = 0 then ret = .nil
  else ret = results[eventid][2]
  results~remove(eventid)
  return ret
::METHOD RPL_WhoWasUser
  expose results
  use arg message
  parse value message~string with . nick user host '*' ':' realname
  do i = 1 to results~items
    if results[i] = .nil then iterate
    if results[i][1] = 'WHOWAS' then do
      results[i][2][results[i][2]~items+1] = .array~of(nick~strip, user~strip,host~strip,realname~strip)
    end
  end
  
-- Don't think we need this method, see the if ... after
-- the timoutalarm~cancel in the whowas method 
-- ::METHOD Err_WasNoSuchNick 
--   expose results
--  parse arg . nick . ':' .
--  do i = 1 to results~items
--    if results[i] = .nil then iterate
--    if results[i][1] = 'WHOWAS' then do
--      results[i][2] = .nil
--    end
--  end
::METHOD RPL_EndOfWhoWas
  expose results whowas_done
  do i = 1 to results~items
    if results[i] = .nil then iterate
    if results[i][1] = 'WHOWAS' then do
      whowas_done = .true
    end
  end
::METHOD WhoWasCancel
  expose WhoWas_done
  WhoWas_done = .true

-- ================ WHO         =================================

-- Returns a single-dimensional array with lines of the following structure:
-- "<channel> <user> <host> <server> <nick>
-- ( "H" / "G" > ["*"] [ ( "@" / "+" ) ] :<hopcount> <real name>"
-- h: here, G: Away, *: IRCOp, @: Op in one channel we're in as well, +: Voiced in one channel

::METHOD Who
  expose results Who_done client
  parse arg string, waittime
  eventid = self~eventid
  Who_done = .false
  results[eventid] = .array~of("WHO",.array~new)
  if \datatype(waittime,'N') then waittime = .local['IRC.TIMEOUT.SCRIPTS']
  timeoutalarm = .alarm~new(waittime, .message~new(self, 'WhoCancel'))
  client~writeln('WHO' string)
  guard off when Who_done \= .false
  timeoutalarm~cancel
  ret = results[eventid][2]
  results~remove(eventid)
  return ret
::METHOD RPL_WhoReply
  expose results
  use arg message
  parse value message~string with . line
  do i = 1 to results~items
    if results[i] = .nil then iterate
    if results[i][1] = 'WHO' then do
      results[i][2][results[i][2]~items+1] = line
    end
  end
::METHOD RPL_EndOfWho
  expose results Who_done
--  parse arg ???
  do i = 1 to results~items
    if results[i] = .nil then iterate
    if results[i][1] = 'WHO' then do
      Who_done = .true
    end
  end
::METHOD WhoCancel
  expose Who_done
  Who_done = .true

/*
 --- Construct for active scripts ---
/* ================             =================================*/
::METHOD xxx
  expose results xxx_done client
  parse arg string, waittime
  eventid = self~eventid
  xxx_done = .false
  results[eventid] = .array~of("XXX")
  if \datatype(waittime,'N') then waittime = .local['IRC.TIMEOUT.SCRIPTS']
  timeoutalarm = .alarm~new(waittime, .message~new(self, 'xxxCancel'))
  client~writeln('xxx' server)
  guard off when xxx_done \= .false
  timeoutalarm~cancel
  ret = results[eventid][2]
  results~remove(eventid)
  return ret
::METHOD RPL_xxx
  expose results xxx_done
  parse arg ???
  do i = 1 to results~items
    if results[i] = .nil then iterate
    if results[i][1] = 'xxx' then do
      results[i][2] = ???
      xxx_done = .true
    end
  end
::METHOD xxxCancel
  expose xxx_done
  xxx_done = .true
*/

--  =================================
--  DEFAULT NUMERIC REPLY METHODS
--  =================================
::Method OnRPL_001
  expose client
  client~ClientEvent('IRC.EVENT.CONNECT',client)
  -- If we get the 001 reply we can be sure that we're connected to the server sucessfully
-- message that tells us what our host is
::METHOD RPL_YourHost
    expose client
    use arg message
    parse value message~string with ":" . . . host
    client~Settings~SetEntry("Host",host)
    
-- message that contains the time the server was started
::METHOD RPL_Created
    expose client
    use arg message
    parse value message~string with ":" time
    client~Settings~SetEntry("Created",time)

-- mesage including host, verion of the server and a lot of strange info...
::METHOD RPL_MyInfo
    expose client
    use arg message
    parse value message~string with . host version
    client~Settings~SetEntry("Version",version)

-- Method parses an ISupport reply, number 005
::METHOD RPL_Isupport
    expose client
    return
    parse value message~string with . modifiers ":"
    do i = 1 to modifiers~words
	-- each word has the form PARAMETER[=VALUE] or -PARAMETER
	-- an empty value means that the service PARAMETER is available on the server
	parse value modifier~word(i) with token"="parameter
	if token~left(1) = "-" then do
	    -- Reset setting to default value
	    client~Settings~IsSupported~Revert(token~right(token~lenght-1))
	    iterate
	end
	else client~Settings~IsSupported~SetEntry(token,parameter)
    end

::Method OnRPL_UserHost
  expose users client
  use arg message
  parse value message~string with . ':' userinfostring
  do i = 1 to userinfostring~words
    parse value userinfostring~word(i) with nick'='host
    if nick~right(1) = '*' then do
      -- Nick is an IRCop
      mode = '+O'
      nick = nick~translate('','*')
    end
      else mode = ''
    awaystate = host~left(1)
    host = host~right(host~length-1)
    SELECT
      WHEN awaystate = '+' then mode = mode'-a'
      WHEN awaystate = '-' then mode = mode'+a'
      OTHERWISE Say 'Wrong 302 RPL_USERHOST from server!'
    END
--    nicko=users~getnick(nick)
--    if nicko = .nil then do
--      nicko = .nick~new(self~client,nick'!'host,users,self~client~channels)
--      nicko~mode(.msgmode~new(.nil,nicko,mode))
--      users~AddNick(nicko)
--      SAy "new user created why?" nick
--    end
--    else
--      nicko~mode(.msgmode~new(.nil,nicko,mode))
--    Say '***' nick 'is' host', mode:' mode
  end


::Method OnRPL_353
  expose client
  use arg message
  parse value message~string with . mode channel ':' nicks
  channelname = channel~strip
--  channel = .channel~new(client,channelname)
    channel = client~GetReceiverObject(channelname)
    if channel = .nil then channel = .channel~new(client,channelname)
  client~event(.msgjoin~new(client~users[1],channel))
  do i = 1 to nicks~words
    nick = nicks~word(i)
    mode = ""
    if nick~pos("@")>0 then mode = "@"
    if nick~pos("+")>0 then mode = mode"+"
    nick = nick~translate("",mode)~strip
    user = client~GetReceiverObject(nick)
    if user = .NIL then user = .User~new(client,nick)
    call debug "[NumHandler: OnRPL_353: Created new msgjoin for" nick "joining" channel "with mode" mode,8
    client~event(.msgjoin~new(user,channel,mode))
  end

::Method OnRPL_433
  expose client
  use arg message
  parse value message~string with target nick .
  -- not registered yet
  if target = '*' then do 
    call debug "[NumHandler: 433: initally proposed nick is in use already.",8
    client~event(.msgnick~new(client~users[1],nick~copies(2)~substr(2,nick~length)))
    client~writeln('NICK' client~users[1]~makestring)
  end

-- The Supported class is a subclass of the directory class.
-- It implements the default values of basically the 005 numrpl
::CLASS IsSupported SUBCLASS directory
::METHOD Init
    expose default
-- The default values here may not be accurate. Thus experience from users is highly recommended!
    default = .directory~new
    default["CASEMAPPING"]	= "rfc1459"
    default["CHANNELLEN"]	= 200
    default["CHANTYPES"]	= "#&"
    default["NICKLEN"]	= 9
    default["PREFIX"]	= "(ov)@+"
    default["CHANTYPES"]	= "#&"
    default["MODES"]	= 3
    default["MAXBANS"]	= 25
    default["TOPICLEN"]	= 80
    default["KICKLEN"]	= 80
    default["MAXTARGETS"]	= 4
    
    -- at the time of the initialization the default values are copied to the current values. They
    -- are expected to be overwritten by the IRC server in the progress of establishing the connection.
    do name over default
	self~SetEntry(name,default[name])
    end

-- This method resets a value to its default value
-- It is only called in case the client receives a message saying to drop the value received earlier for
-- this particular setting.
::METHOD Revert
    expose default
    use arg name
    self~SetEntry(name,default[name])

