
from pyclassfiller import _core
import numpy

########################################################################
# Observation
########################################################################

class ClassObsHeadAny:
  def __repr__(self):
    string = ""
    for k in list(self._setters.keys()):
      string += "%-10s: %s\n" % (k,repr(self.__dict__[k]))
    return string
    #
  def _sync(self):
    # Activate the section
    _core.set_presec(self._code)
    # Synchronize the values to the 'obs' buffer in the filler
    for k in list(self._setters.keys()):
      self._setters[k](self.__dict__[k])

class ClassObsHeadGen(ClassObsHeadAny):
  def __init__(self):
    self.num = 0
    self.ver = 0
    self.teles = ""
    self.dobs = 0
    self.dred = 0
    self.kind = _core.code_kind_spec
    self.qual = _core.code_qual_unknown
    self.scan = 1
    self.subscan = 1
    self.ut = 0.
    self.st = 0.
    self.az = 0.
    self.el = 0.
    self.tau = 0.
    self.tsys = 100.
    self.time = 100.
    self.xunit = _core.code_xunit_velo  # Unused
    #
    self._code = code.sec.gen
    self.__dict__['_setters'] =  \
          { 'num':     _core.set_gen_num,
            'ver':     _core.set_gen_ver,
            'teles':   _core.set_gen_teles,
            'dobs':    _core.set_gen_dobs,
            'dred':    _core.set_gen_dred,
            'kind':    _core.set_gen_kind,
            'qual':    _core.set_gen_qual,
            'scan':    _core.set_gen_scan,
            'subscan': _core.set_gen_subscan,
            'ut':      _core.set_gen_ut,
            'st':      _core.set_gen_st,
            'az':      _core.set_gen_az,
            'el':      _core.set_gen_el,
            'tau':     _core.set_gen_tau,
            'tsys':    _core.set_gen_tsys,
            'time':    _core.set_gen_time,
            'xunit':   _core.set_gen_xunit }

class ClassObsHeadPos(ClassObsHeadAny):
  def __init__(self):
    self.sourc = ""
    self.system = _core.code_coord_equ
    self.equinox = 2000.0
    self.proj = _core.code_proj_none
    self.lam = 0.
    self.bet = 0.
    self.projang = 0.
    self.lamof = 0.
    self.betof = 0.
    #
    self._code = code.sec.pos
    self.__dict__['_setters'] =  \
          { 'sourc':   _core.set_pos_sourc,
            'system':  _core.set_pos_system,
            'equinox': _core.set_pos_equinox,
            'proj':    _core.set_pos_proj,
            'lam':     _core.set_pos_lam,
            'bet':     _core.set_pos_bet,
            'projang': _core.set_pos_projang,
            'lamof':   _core.set_pos_lamof,
            'betof':   _core.set_pos_betof}

class ClassObsHeadSpe(ClassObsHeadAny):
  def __init__(self):
    self.line = ""
    self.restf = 0.
    self.nchan = 128
    self.rchan = 0.
    self.fres = 0.
    self.vres = 0.
    self.voff = 0.
    self.bad = -1000.
    self.image = 0.
    self.vtype = _core.code_velo_obs
    self.vconv = _core.code_conv_unk
    self.doppler = -1.
    #
    self._code = code.sec.spe
    self.__dict__['_setters'] =  \
          { 'line':    _core.set_spe_line,
            'nchan':   _core.set_spe_nchan,
            'restf':   _core.set_spe_restf,
            'image':   _core.set_spe_image,
            'doppler': _core.set_spe_doppler,
            'rchan':   _core.set_spe_rchan,
            'fres':    _core.set_spe_fres,
            'vres':    _core.set_spe_vres,
            'voff':    _core.set_spe_voff,
            'bad':     _core.set_spe_bad,
            'vtype':   _core.set_spe_vtype,
            'vconv':   _core.set_spe_vconv }

class ClassObsHeadFsw(ClassObsHeadAny):
  def __init__(self):
    self.nphas  = 0
    self.decal  = numpy.zeros((8,),dtype=numpy.float64)  # mxphas = 8
    self.duree  = numpy.zeros((8,),dtype=numpy.float32)  # mxphas = 8
    self.poids  = numpy.zeros((8,),dtype=numpy.float32)  # mxphas = 8
    self.swmod  = _core.code_switch_freq
    self.ldecal = numpy.zeros((8,),dtype=numpy.float32)  # mxphas = 8
    self.bdecal = numpy.zeros((8,),dtype=numpy.float32)  # mxphas = 8
    #
    self._code = code.sec.fsw
    self.__dict__['_setters'] =  \
          { 'nphas':   _core.set_fsw_nphas,
            'decal':   _core.set_fsw_decal,
            'duree':   _core.set_fsw_duree,
            'poids':   _core.set_fsw_poids,
            'swmod':   _core.set_fsw_swmod,
            'ldecal':  _core.set_fsw_ldecal,
            'bdecal':  _core.set_fsw_bdecal }

class ClassObsHeadCal(ClassObsHeadAny):
  def __init__(self):
    self.beeff = 0.
    self.foeff = 0.
    self.gaini = 0.
    self.h2omm = 0.
    self.pamb = 0.
    self.tamb = 0.
    self.tatms = 0.
    self.tchop = 0.
    self.tcold = 0.
    self.taus = 0.
    self.taui = 0.
    self.tatmi = 0.
    self.trec = 0.
    self.cmode = _core.code_calib_auto
    self.atfac = 0.
    self.alti = 0.
    self.count = numpy.zeros((3,),dtype=numpy.float32)
    self.lcalof = 0.
    self.bcalof = 0.
    self.geolong = 0.
    self.geolat = 0.
    #
    self._code = code.sec.cal
    self.__dict__['_setters'] =  \
          { 'beeff':   _core.set_cal_beeff,
            'foeff':   _core.set_cal_foeff,
            'gaini':   _core.set_cal_gaini,
            'h2omm':   _core.set_cal_h2omm,
            'pamb':    _core.set_cal_pamb,
            'tamb':    _core.set_cal_tamb,
            'tatms':   _core.set_cal_tatms,
            'tchop':   _core.set_cal_tchop,
            'tcold':   _core.set_cal_tcold,
            'taus':    _core.set_cal_taus,
            'taui':    _core.set_cal_taui,
            'tatmi':   _core.set_cal_tatmi,
            'trec':    _core.set_cal_trec,
            'cmode':   _core.set_cal_cmode,
            'atfac':   _core.set_cal_atfac,
            'alti':    _core.set_cal_alti,
            'count':   _core.set_cal_count,
            'lcalof':  _core.set_cal_lcalof,
            'bcalof':  _core.set_cal_bcalof,
            'geolong': _core.set_cal_geolong,
            'geolat':  _core.set_cal_geolat }

class ClassObsHeadDri(ClassObsHeadAny):
  def __init__(self):
    self.freq = 0.
    self.width = 0.
    self.npoin = 0
    self.rpoin = 0.
    self.tref = 0.
    self.aref = 0.
    self.apos = 0.
    self.tres = 0.
    self.ares = 0.
    self.bad = 0.
    self.ctype = _core.code_coord_equ
    self.cimag = 0.
    self.colla = 0.
    self.colle = 0.
    #
    self._code = code.sec.dri
    self.__dict__['_setters'] =  \
          { 'freq':  _core.set_dri_freq,
            'width': _core.set_dri_width,
            'npoin': _core.set_dri_npoin,
            'rpoin': _core.set_dri_rpoin,
            'tref':  _core.set_dri_tref,
            'aref':  _core.set_dri_aref,
            'apos':  _core.set_dri_apos,
            'tres':  _core.set_dri_tres,
            'ares':  _core.set_dri_ares,
            'bad':   _core.set_dri_bad,
            'ctype': _core.set_dri_ctype,
            'cimag': _core.set_dri_cimag,
            'colla': _core.set_dri_colla,
            'colle': _core.set_dri_colle }


class ClassObsHead:
  def __init__(self):
    self.presec = numpy.zeros((64,),dtype=bool)
    self.gen = ClassObsHeadGen()
    self.pos = ClassObsHeadPos()
    self.spe = ClassObsHeadSpe()
    self.fsw = ClassObsHeadFsw()
    self.cal = ClassObsHeadCal()
    self.dri = ClassObsHeadDri()


class ClassObservation:
  """obs = ClassObservation()
    This class is used to create a Class observation (e.g. a spectrum)
    before it is written to the Class output file.

    The sections present must be declared e.g.
      obs.head.presec[code.sec.gen] = True
    and then filled appropriately e.g.
      obs.head.spe.line = "MYLINE"
      obs.head.spe.restf = 123456.
      obs.head.spe.nchan = nchan
      obs.head.spe.rchan = 1.
      obs.head.spe.fres = 1.
      obs.head.spe.vres = -1.
      obs.head.spe.voff = 0.
      obs.head.spe.bad = -1000.
      obs.head.spe.image = 98765.
      obs.head.spe.vtype = code.velo.obs
      obs.head.spe.vconv = code.conv.rad
      obs.head.spe.doppler = 0.
  """

  def __init__(self):
    self.head = ClassObsHead()
    self.datay = numpy.zeros((128,),dtype=numpy.float32)

  def write(self):
    """write()

    Write the observation to the output file currently opened (see
    ClassFileOut.open). Sections present must have been declared and
    and filled before, they must be at least the General, Position,
    and Spectroscopy or Continuum Drift sections.

    If obs.head.gen.num is 0, Class will automatically give the next
    available (unused) number in the file.
    """
    if (self.head.gen.kind == _core.code_kind_spec):
      _core.obs_reset(self.head.spe.nchan)
    elif (self.head.gen.kind == _core.code_kind_cont):
      _core.obs_reset(self.head.dri.npoin)
    else:
      raise ValueError("Observation kind not supported")
    if self.head.presec[code.sec.gen]: self.head.gen._sync()
    if self.head.presec[code.sec.pos]: self.head.pos._sync()
    if self.head.presec[code.sec.spe]: self.head.spe._sync()
    if self.head.presec[code.sec.fsw]: self.head.fsw._sync()
    if self.head.presec[code.sec.cal]: self.head.cal._sync()
    if self.head.presec[code.sec.dri]: self.head.dri._sync()
    _core.set_datay(self.datay)
    _core.obs_write()


########################################################################
# File
########################################################################

class ClassFileOut:
  """f = ClassFileOut()
    This class is used to access a Class output file. An instance must
    be opened before writing ClassObservation objects.

    Example:
    --------
    >>> fileout = pyclassfiller.ClassFileOut()
    >>> fileout.open(file='foo.30m',new=True,over=True,size=10000,single=True)
    >>> fileout.close()
    >>> del fileout
    This creates a new, empty, Class file.
  """

  def open(self,file,new,over,size,single):
    """open(file,new,over,size,single)

    Open a Class output file. There must be only one output file opened
    at the same time.

    Parameters
    ----------
    file: the file name
    new: can the file already exist, i.e. reopen it for appending (False),
          or should it be new (True)?
    over: overwrite (True) the previous version of the file, if 'new' is
          requested?
    size: the maximum number of observations the file will store (V1 Class
          files, obsolescent parameter)
    single: if True, there can be only one version of each observation
          in the file.
    """
    _core.fileout_open(file,new,over,size,single)

  def close(self):
    """close()

    Close a Class output file.

    Parameters
    ----------
    None
    """
    _core.fileout_close()


########################################################################
# Codes
########################################################################

class ClassFillerCodesCoord:
  def __init__(self):
    self.unk = _core.code_coord_unk
    self.equ = _core.code_coord_equ
    self.gal = _core.code_coord_gal
    self.hor = _core.code_coord_hor

class ClassFillerCodesKind:
  def __init__(self):
    self.spec = _core.code_kind_spec
    self.cont = _core.code_kind_cont
    self.sky = _core.code_kind_sky
    self.onoff = _core.code_kind_onoff
    self.focus = _core.code_kind_focus

class ClassFillerCodesSwitch:
  def __init__(self):
    self.freq = _core.code_switch_freq
    self.posi = _core.code_switch_posi
    self.fold = _core.code_switch_fold

class ClassFillerCodesXunit:
  def __init__(self):
    self.velo = _core.code_xunit_velo
    self.freq = _core.code_xunit_freq
    self.wave = _core.code_xunit_wave

class ClassFillerCodesProj:
  def __init__(self):
    self.none = _core.code_proj_none
    self.gnomonic = _core.code_proj_gnomonic
    self.ortho = _core.code_proj_ortho
    self.azimuthal = _core.code_proj_azimuthal
    self.stereo = _core.code_proj_stereo
    self.lambert = _core.code_proj_lambert
    self.aitoff = _core.code_proj_aitoff
    self.radio = _core.code_proj_radio
    self.sfl = _core.code_proj_sfl
    self.mollweide = _core.code_proj_mollweide
    self.ncp = _core.code_proj_ncp
    self.cartesian = _core.code_proj_cartesian

class ClassFillerCodesVelo:
  def __init__(self):
    self.unk = _core.code_velo_unk
    self.lsr = _core.code_velo_lsr
    self.helio = _core.code_velo_helio
    self.obs = _core.code_velo_obs
    self.earth = _core.code_velo_earth
    self.auto = _core.code_velo_auto

class ClassFillerCodesConv:
  def __init__(self):
    self.unk = _core.code_conv_unk
    self.rad = _core.code_conv_rad
    self.opt = _core.code_conv_opt
    self.thirtym = _core.code_conv_30m

class ClassFillerCodesSection:
  def __init__(self):
    self.user = _core.code_section_user
    self.gen  = _core.code_section_gen
    self.pos  = _core.code_section_pos
    self.spe  = _core.code_section_spe
    self.bas  = _core.code_section_bas
    self.his  = _core.code_section_his
    self.plo  = _core.code_section_plo
    self.fsw  = _core.code_section_fsw
    self.gau  = _core.code_section_gau
    self.dri  = _core.code_section_dri
    self.bea  = _core.code_section_bea
    self.she  = _core.code_section_she
    self.nh3  = _core.code_section_nh3
    self.abs  = _core.code_section_abs
    self.cal  = _core.code_section_cal
    self.poi  = _core.code_section_poi
    self.sky  = _core.code_section_sky

class ClassFillerCodesQual:
  def __init__(self):
    self.unknown = _core.code_qual_unknown
    self.excellent = _core.code_qual_excellent
    self.good = _core.code_qual_good
    self.fair = _core.code_qual_fair
    self.average = _core.code_qual_average
    self.poor = _core.code_qual_poor
    self.bad = _core.code_qual_bad
    self.awful = _core.code_qual_awful
    self.worst = _core.code_qual_worst
    self.deleted = _core.code_qual_deleted

class ClassFillerCodesCalib:
  def __init__(self):
    self.auto = _core.code_calib_auto
    self.trec = _core.code_calib_trec
    self.humi = _core.code_calib_humi
    self.manu = _core.code_calib_manu

class ClassFillerCodes:
  def __init__(self):
    self.sec = ClassFillerCodesSection()
    self.coord = ClassFillerCodesCoord()
    self.kind = ClassFillerCodesKind()
    self.switch = ClassFillerCodesSwitch()
    self.xunit = ClassFillerCodesXunit()
    self.proj = ClassFillerCodesProj()
    self.velo = ClassFillerCodesVelo()
    self.conv = ClassFillerCodesConv()
    self.qual = ClassFillerCodesQual()
    self.calib = ClassFillerCodesCalib()

code = ClassFillerCodes()
