;+
; NAME:
;	SMEI_OPERATOR::INIT
;
;
; PURPOSE:
;	Constructor method for a SMEI operator.
;
;
; CATEGORY:
;	SMEI_OPERATOR
;
;
; CALLING SEQUENCE:
;	sop = obj_new('SMEI_OPERATOR', op, arglist)
;
;
; INPUTS:
;	op	string	The operator (or function) to apply
;	arglist	obj	An array of SMEI_OPERATOR_ARGUMENT objects.
;
;
; KEYWORD PARAMETERS:
;	If the operator is a function, then any keywords to the
;	function are accepted.
;
; OUTPUTS:
;	sop	The SMEI_OPERATOR object generated.
;
;
; MODIFICATION HISTORY:
;	Original: 22/10/03; SJT
;	Add median special OP, and correct flagging in %cat: 19/02/04;
;	SJT
;-


function smei_operator::init, op, arglist, _extra = _extra

top = size(op, /type)
if top ne 7 then begin
    smei_msg, /error, 'The operation must be a string'
    return, 0
end

sargl = size(arglist)
if sargl[0] gt 1 then begin
    smei_msg, /error, 'The argument list must be a scalar or a 1-D ' + $
              'array'
    return, 0
endif
if sargl[sargl[0]+1] ne 11 then begin
    smei_msg, /error, 'The argument list must be an object reference'
    return, 0
endif

for j = 0, n_elements(arglist)-1 do begin
    if obj_class(arglist[j]) ne 'SMEI_OPERATOR_ARGUMENT' then begin
        smei_msg, /error, 'Arguments must be of class ' + $
                  'SMEI_OPERATOR_ARGUMENT'
        return, 0
    endif
endfor

operators = ['+', '-', '*', '/', '^', '>', '<', '#', '##', 'mod', $
             'not', 'or', 'and', 'eq', 'gt', 'lt', 'ge', 'le', 'ne', $
             'xor']

specops = ['++', $              ; Average (special synonym for %MEAN)
           '%mean', $           ; Average
           '%sum', $            ; Total
           '%csum', $           ; Cumulative sum
           '%min', $            ; Least
           '%max', $            ; Greatest
           '%cat', $            ; Concatenate.
           '%median']           ; Median

self.operator = strlowcase(op)
self.arg_count = n_elements(arglist)
self.arglist = ptr_new(arglist)

locs = where(operators eq self.operator, nop)

if nop eq 1 then begin          ; The operator is a regular
                                ; IDL operator.
    self.class = 1
    self.is_cumulative = 0b
endif else begin
    
    locs = where(specops eq self.operator, nsop)
    if nsop eq 1 then begin     ; The operator is a special operation
                                ; (defined by an internal function)

        if locs[0] eq 0 then self.operator = '%mean'
        self.class = 2
        if self.operator eq '%cat' then self.is_cumulative = 0b $
        else self.is_cumulative = 1b
    endif else begin            ; The operator is a function
        self.class = 3
        self.is_cumulative = 0b ; We may later be able to think of
                                ; cases where this should be 1 (that's
                                ; why there's a separate field for
                                ; cumulative). 
	if keyword_set(_extra) then self.extras = ptr_new(_extra)

    endelse
endelse

counts = intarr(self.arg_count)
for j = 0, self.arg_count-1 do counts[j] = arglist[j] -> get_count()
if max(counts) ne 1 then begin
    locs = where(counts ne 1)
    counts = counts[locs]
    locs = where(counts ne counts[0], nmm)
    if nmm ne 0 and not self.is_cumulative and self.class ne 2 $
      then begin              ; There are non-unit args with differing
                                ; lengths
        smei_msg, /alert, 'Cannot combine array arguments of ' + $
                  'differing lengths'
        ptr_free, self.arglist
        return, 0
    endif
    self.array_count = counts[0]
endif else self.array_count = 1

return, 1

end
