#!/usr/bin/env python
#
# Copyright 2011-2012 Ettus Research LLC
#
# This program 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 3 of the License, or
# (at your option) any later version.
#
# This program 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, see <http://www.gnu.org/licenses/>.
#

TMPL_HEADER = """
<%
    import time
%>
/***********************************************************************
 * This file was generated by ${file} on ${time.strftime("%c")}
 **********************************************************************/

#include "convert_common.hpp"
#include <uhd/utils/byteswap.hpp>

using namespace uhd::convert;


// item32 -> item32: Just a memcpy. No scaling possible.
DECLARE_CONVERTER(item32, 1, item32, 1, PRIORITY_GENERAL) {
    const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]);
    item32_t *output = reinterpret_cast<item32_t *>(outputs[0]);

    memcpy(output, input, nsamps * sizeof(item32_t));
}
"""

TMPL_CONV_GEN2_ITEM32 = """
DECLARE_CONVERTER(item32, 1, sc16_item32_{end}, 1, PRIORITY_GENERAL) {{
    const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]);
    item32_t *output = reinterpret_cast<item32_t *>(outputs[0]);

    for (size_t i = 0; i < nsamps; i++) {{
        output[i] = {to_wire}(input[i]);
    }}
}}

DECLARE_CONVERTER(sc16_item32_{end}, 1, item32, 1, PRIORITY_GENERAL) {{
    const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]);
    item32_t *output = reinterpret_cast<item32_t *>(outputs[0]);

    for (size_t i = 0; i < nsamps; i++) {{
        output[i] = {to_host}(input[i]);
    }}
}}
"""

TMPL_CONV_U8 = """
DECLARE_CONVERTER(u8, 1, u8_item32_{end}, 1, PRIORITY_GENERAL) {{
    const boost::uint32_t *input = reinterpret_cast<const boost::uint32_t *>(inputs[0]);
    boost::uint32_t *output = reinterpret_cast<boost::uint32_t *>(outputs[0]);

    // 1) Copy all the 4-byte tuples
    size_t n_words = nsamps / 4;
    for (size_t i = 0; i < n_words; i++) {{
        output[i] = {to_wire}(input[i]);
    }}
    // 2) If nsamps was not a multiple of 4, copy the rest by hand
    size_t bytes_left = nsamps % 4;
    if (bytes_left) {{
        const u8_t *last_input_word  = reinterpret_cast<const u8_t *>(&input[n_words]);
        u8_t *last_output_word = reinterpret_cast<u8_t *>(&output[n_words]);
        for (size_t k = 0; k < bytes_left; k++) {{
            last_output_word[k] = last_input_word[k];
        }}
        output[n_words] = {to_wire}(output[n_words]);
    }}
}}

DECLARE_CONVERTER(u8_item32_{end}, 1, u8, 1, PRIORITY_GENERAL) {{
    const boost::uint32_t *input = reinterpret_cast<const boost::uint32_t *>(inputs[0]);
    boost::uint32_t *output = reinterpret_cast<boost::uint32_t *>(outputs[0]);

    // 1) Copy all the 4-byte tuples
    size_t n_words = nsamps / 4;
    for (size_t i = 0; i < n_words; i++) {{
        output[i] = {to_host}(input[i]);
    }}
    // 2) If nsamps was not a multiple of 4, copy the rest by hand
    size_t bytes_left = nsamps % 4;
    if (bytes_left) {{
        boost::uint32_t last_input_word = {to_host}(input[n_words]);
        const u8_t *last_input_word_ptr = reinterpret_cast<const u8_t *>(&last_input_word);
        u8_t *last_output_word = reinterpret_cast<u8_t *>(&output[n_words]);
        for (size_t k = 0; k < bytes_left; k++) {{
            last_output_word[k] = last_input_word_ptr[k];
        }}
    }}
}}
"""

TMPL_CONV_USRP1_COMPLEX = """
DECLARE_CONVERTER(${cpu_type}, ${width}, sc16_item16_usrp1, 1, PRIORITY_GENERAL){
    % for w in range(width):
    const ${cpu_type}_t *input${w} = reinterpret_cast<const ${cpu_type}_t *>(inputs[${w}]);
    % endfor
    boost::uint16_t *output = reinterpret_cast<boost::uint16_t *>(outputs[0]);

    for (size_t i = 0, j = 0; i < nsamps; i++){
        % for w in range(width):
        output[j++] = ${to_wire}(boost::uint16_t(boost::int16_t(input${w}[i].real()${do_scale})));
        output[j++] = ${to_wire}(boost::uint16_t(boost::int16_t(input${w}[i].imag()${do_scale})));
        % endfor
    }
}

DECLARE_CONVERTER(sc16_item16_usrp1, 1, ${cpu_type}, ${width}, PRIORITY_GENERAL){
    const boost::uint16_t *input = reinterpret_cast<const boost::uint16_t *>(inputs[0]);
    % for w in range(width):
    ${cpu_type}_t *output${w} = reinterpret_cast<${cpu_type}_t *>(outputs[${w}]);
    % endfor

    for (size_t i = 0, j = 0; i < nsamps; i++){
        % for w in range(width):
        output${w}[i] = ${cpu_type}_t(
            boost::int16_t(${to_host}(input[j+0]))${do_scale},
            boost::int16_t(${to_host}(input[j+1]))${do_scale}
        );
        j += 2;
        % endfor
    }
}

DECLARE_CONVERTER(sc8_item16_usrp1, 1, ${cpu_type}, ${width}, PRIORITY_GENERAL){
    const boost::uint16_t *input = reinterpret_cast<const boost::uint16_t *>(inputs[0]);
    % for w in range(width):
    ${cpu_type}_t *output${w} = reinterpret_cast<${cpu_type}_t *>(outputs[${w}]);
    % endfor

    for (size_t i = 0, j = 0; i < nsamps; i++){
        % for w in range(width):
        {
        const boost::uint16_t num = ${to_host}(input[j++]);
        output${w}[i] = ${cpu_type}_t(
            boost::int8_t(num)${do_scale},
            boost::int8_t(num >> 8)${do_scale}
        );
        }
        % endfor
    }
}
"""

def parse_tmpl(_tmpl_text, **kwargs):
    from mako.template import Template
    return Template(_tmpl_text).render(**kwargs)

if __name__ == '__main__':
    import sys, os
    file = os.path.basename(__file__)
    output = parse_tmpl(TMPL_HEADER, file=file)

    #generate complex converters for all gen2 platforms
    for end, to_host, to_wire in (
        ('be', 'uhd::ntohx', 'uhd::htonx'),
        ('le', 'uhd::wtohx', 'uhd::htowx'),
    ):
        output += TMPL_CONV_GEN2_ITEM32.format(
                end=end, to_host=to_host, to_wire=to_wire
        )
    #generate raw (u8) converters:
    for end, to_host, to_wire in (
        ('be', 'uhd::ntohx', 'uhd::htonx'),
        ('le', 'uhd::wtohx', 'uhd::htowx'),
    ):
        output += TMPL_CONV_U8.format(
                end=end, to_host=to_host, to_wire=to_wire
        )

    #generate complex converters for usrp1 format (requires Cheetah)
    for width in 1, 2, 4:
        for cpu_type, do_scale in (
            ('fc64', '*scale_factor'),
            ('fc32', '*float(scale_factor)'),
            ('sc16', ''),
        ):
            output += parse_tmpl(
                TMPL_CONV_USRP1_COMPLEX,
                width=width, to_host='uhd::wtohx', to_wire='uhd::htowx',
                cpu_type=cpu_type, do_scale=do_scale
            )
    open(sys.argv[1], 'w').write(output)