"""
Copyright 2012 Free Software Foundation, Inc.

This file is part of GNU Radio

GNU Radio Companion is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

GNU Radio Companion is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
"""

MAIN_TMPL = """\
id: ${prefix}_${sourk}
label: '${title} ${sourk.title()}'
category: '[bladeRF]'
flags: throttle

parameters:
- id: type
  label: '${direction.title()}put Type'
  dtype: enum
  options: [fc32]
  option_labels: [Complex Float32]
  option_attributes:
      type: [fc32]
  hide: part
  
- id: device_id
  label: 'Device'
  dtype: string
  default: '0'
  hide: ${'$'}{ 'none' if device_id else 'part'}
  
- id: nchan
  label: 'Number Channels'
  dtype: int
  default: 1
  options: [ ${", ".join([str(n) for n in range(1, max_nchan+1)])} ]
  
- id: verbosity
  label: 'Verbosity'
  dtype: enum
  default: verbose
  options: ['verbose', 'debug', 'info', 'warning', 'error', 'critical', 'silent']
  option_labels: [verbose, debug, info, warning, error, critical, silent]
  
- id: sample_rate
  label: 'Sample Rate (sps)'
  dtype: real
  default: samp_rate
  
- id: fpga_image
  label: 'FPGA image'
  dtype: string
    
- id: power_monitoring
  label: 'Enable power monitoring'
  dtype: bool
  default: False
 
- id: ref_clk
  label: 'Reference clock'
  dtype: real
  
- id: in_clk
  label: 'Input clock'
  dtype: enum
  default: auto
  options: ['ONBOARD', 'EXTERNAL']
  option_labels: ['ONBOARD', 'EXTERNAL']
  
- id: out_clk
  label: 'Output clock'
  dtype: bool
  default: False

- id: dac
  label: 'VCXTO DAC'
  dtype: real
  default: 10000

- id: xb200
  label: 'XB-200'
  dtype: enum
  default: auto
  options: ['auto', 'auto3db', '50M', '144M', '222M', 'custom']
  option_labels: ['auto', 'auto3db', '50M', '144M', '222M', 'custom']

${params}


inputs:
- domain: message
  id: command
  optional: true
% if sourk == 'source':

outputs:
% endif
- domain: stream
  dtype: ${'$'}{type.type}
  multiplicity: ${'$'}{nchan}
% if sourk == 'sink':

outputs:
- domain: message
  id: async_msgs
  optional: true
% endif

templates:
  imports: |-
     import bladeRF
     import time
  make: |
    bladeRF.${sourk}(
        args="numchan=" + str(${'$'}{nchan})
             + ",type=" + '${'$'}{type}'
             + ",bladerf=" +  str(${'$'}{device_id})
             + ",verbosity=" + '${'$'}{verbosity}'
             + ",fpga=" + str(${'$'}{fpga_image})
             + ",power_monitoring=" + str(${'$'}{power_monitoring})
             + ",ref_clk=" + str(${'$'}{ref_clk})
             + ",in_clk=" + '${'$'}{in_clk}'
             + ",out_clk=" + str(${'$'}{out_clk})
             + ",dac=" + str(${'$'}{dac})
             + ",xb200=" + '${'$'}{xb200}'
    )
    self.${'$'}{id}.set_sample_rate(${'$'}{sample_rate})
    % for n in range(max_nchan):
    ${'%'} if context.get('nchan')() > ${n}:
    self.${'$'}{id}.set_center_freq(${'$'}{${'freq' + str(n)}}, ${n})
    self.${'$'}{id}.set_freq_corr(${'$'}{${'corr' + str(n)}}, ${n})
    % if sourk == 'source':
    self.${'$'}{id}.set_dc_offset_mode(${'$'}{${'dc_offset_mode' + str(n)}}, ${n})
    self.${'$'}{id}.set_iq_balance_mode(${'$'}{${'iq_balance_mode' + str(n)}}, ${n})
    self.${'$'}{id}.set_gain_mode(${'$'}{${'gain_mode' + str(n)}}, ${n})    
    % endif
    self.${'$'}{id}.set_gain(${'$'}{${'gain' + str(n)}}, ${n})
    self.${'$'}{id}.set_if_gain(${'$'}{${'if_gain' + str(n)}}, ${n})
    self.${'$'}{id}.set_bb_gain(${'$'}{${'bb_gain' + str(n)}}, ${n})
    self.${'$'}{id}.set_bandwidth(${'$'}{${'bw' + str(n)}}, ${n})
    ${'%'} endif
    % endfor
  callbacks:
    - set_sample_rate(${'$'}{sample_rate})
    % for n in range(max_nchan):
    - set_center_freq(${'$'}{${'freq' + str(n)}}, ${n})
    - set_freq_corr(${'$'}{${'corr' + str(n)}}, ${n})
    % if sourk == 'source':
    - set_dc_offset_mode(${'$'}{${'dc_offset_mode' + str(n)}}, ${n})
    - set_iq_balance_mode(${'$'}{${'iq_balance_mode' + str(n)}}, ${n})
    - set_gain_mode(${'$'}{${'gain_mode' + str(n)}}, ${n})
    % endif
    - set_gain(${'$'}{${'gain' + str(n)}}, ${n})
    - set_if_gain(${'$'}{${'if_gain' + str(n)}}, ${n})
    - set_bb_gain(${'$'}{${'bb_gain' + str(n)}}, ${n})
    - set_bandwidth(${'$'}{${'bw' + str(n)}}, ${n})
    % endfor

documentation: |-
  The bladeRF ${sourk} block:
  
  Device: 
  Device serial id. If 'device' are not specified, the first available device is used.
  
  Fpga image:  
  Load the FPGA bitstream from the specified file. This is required only once after powering the bladeRF on. If the FPGA is already loaded, this argument is ignored, unless 'fpga-reload=1' is specified.
  
  Enable power monitoring:
  Read value from Power Monitor IC
  
  Reference clock:
  Value of reference clock
  
  Input clock:
  Onboard/external clock.
  
  Output clock:
  Enable/disable clock output.
  
  VCXTO DAC:
  Value to VCTCXO trim DAC  
  
  Num Channels:
  Selects the total number of channels in this multi-device configuration. Required when specifying multiple device arguments.

  Sample Rate:
  The sample rate is the number of samples per second output by this block on each channel.

  Frequency:
  The center frequency is the frequency the RF chain is tuned to.

  Freq. Corr.:
  The frequency correction factor in parts per million (ppm). Set to 0 if unknown.

  % if sourk == 'source':
  DC Offset Mode:
  Controls the behavior of hardware DC offset corrrection.
    Off: Disable correction algorithm (pass through).
    Manual: Keep last estimated correction when switched from Automatic to Manual.
    Automatic: Periodicallly find the best solution to compensate for DC offset.

  This functionality is available for USRP devices only.

  IQ Balance Mode:
  Controls the behavior of software IQ imbalance corrrection.
    Off: Disable correction algorithm (pass through).
    Manual: Keep last estimated correction when switched from Automatic to Manual.
    Automatic: Periodicallly find the best solution to compensate for image signals.

  Gain Mode:
  Chooses between the manual (default) and automatic gain mode where appropriate.
  To allow manual control of RF/IF/BB gain stages, manual gain mode must be configured.
  Currently, only RTL-SDR devices support automatic gain mode.

  % endif
  RF Gain:
  Overall RF gain of the device.

  IF Gain:
  Overall intermediate frequency gain of the device.
  This setting is available for RTL-SDR and OsmoSDR devices with E4000 tuners and HackRF in receive and transmit mode. Observations lead to a reasonable gain range from 15 to 30dB.

  BB Gain:
  Overall baseband gain of the device.
  This setting is available for HackRF in receive mode. Observations lead to a reasonable gain range from 15 to 30dB.

  Bandwidth:
  Set the bandpass filter on the radio frontend. To use the default (automatic) bandwidth filter setting, this should be zero.
  
file_format: 1
"""

# MAIN_TMPL = """\
# <block>
#   <check>$max_nchan >= \$nchan</check>
#   <check>\$nchan > 0</check>
# </block>
# """

PARAMS_TMPL = """
- id: freq${n}
  label: 'Ch${n}: Center frequency (Hz)'
  dtype: real
  default: 1e8
  hide: ${'$'}{'none' if (nchan > ${n}) else 'all'} 
  
- id: corr${n}
  label: 'Ch${n}: Frequency Correction (ppm)'
  dtype: real
  default: 0
  hide: ${'$'}{'none' if (nchan > ${n}) else 'all'} 
   
- id: bw${n}
  label: 'Ch${n}: Bandwidth (Hz)'
  dtype: real
  default: 200000
  hide: ${'$'}{'none' if (nchan > ${n}) else 'all'}  
  
- id: bias_tee${n}
  label: 'Ch${n}: Bias tee'
  dtype: bool
  default: False
  hide: ${'$'}{'none' if (nchan > ${n}) else 'all'}  
  
% if sourk == 'source':
- id: dc_offset_mode${n}
  label: 'Ch${n}: DC Offset Mode'
  dtype: int
  default: 0
  options: [0, 1, 2]  
  option_labels: [Off, Manual, Automatic]
  hide: ${'$'}{'none' if (nchan > ${n}) else 'all'}
  
- id: iq_balance_mode${n}
  label: 'Ch${n}: IQ Balance Mode'
  dtype: int
  default: 0
  options: [0, 1, 2]
  option_labels: [Off, Manual, Automatic]
  hide: ${'$'}{'none' if (nchan > ${n}) else 'all'}
  
- id: gain_mode${n}
  label: 'Ch${n}: Gain Mode'
  dtype: bool
  default: False
  options: [False, True]
  option_labels: [Manual, Automatic]
  hide: ${'$'}{'none' if (nchan > ${n}) else 'all'}
% endif

- id: gain${n}
  label: 'Ch${n}: RF Gain (dB)'
  dtype: real
  default: 10
  hide: ${'$'}{'none' if (nchan > ${n}) else 'all'}
  
- id: if_gain${n}
  label: 'Ch${n}: IF Gain (dB)'
  dtype: real
  default: 20
  hide: ${'$'}{'none' if (nchan > ${n}) else 'all'}
  
- id: bb_gain${n}
  label: 'Ch${n}: BB Gain (dB)'
  dtype: real
  default: 20
  hide: ${'$'}{'none' if (nchan > ${n}) else 'all'}
"""


def parse_tmpl(_tmpl, **kwargs):
    from mako.template import Template
    from mako import exceptions

    try:
        block_template = Template(_tmpl)
        return str(block_template.render(**kwargs))
    except:
        print(exceptions.text_error_template().render())

MAX_NUM_CHANNELS = 2

import os.path

if __name__ == '__main__':
    import sys

    for file in sys.argv[1:]:
        head, tail = os.path.split(file)

        if tail.startswith('bladeRF'):
            title = 'bladeRF'
            prefix = 'bladeRF'
        else:
            raise Exception("file {} has wrong syntax!".format(tail))


        if tail.endswith('source.block.yml'):
            sourk = 'source'
            direction = 'out'
        elif tail.endswith('sink.block.yml'):
            sourk = 'sink'
            direction = 'in'
        else:
            raise Exception("is {} a source or sink?".format(file))

        params = ''.join([
            parse_tmpl(PARAMS_TMPL, n=n, sourk=sourk)
            for n in range(MAX_NUM_CHANNELS)
        ])

        open(file, 'w').write(
            parse_tmpl(
                MAIN_TMPL,
                max_nchan=MAX_NUM_CHANNELS,
                params=params,
                title=title,
                prefix=prefix,
                sourk=sourk,
                direction=direction,
            )
        )