#!/bin/bash -f

# mcpack2pb - Make protobuf be front-end of mcpack/compack
# Copyright (c) 2015 Baidu.com, Inc. All Rights Reserved
# Date: Mon Oct 19 17:17:36 CST 2015

# source shflags from current directory
mydir="${BASH_SOURCE%/*}"
if [[ ! -d "$mydir" ]]; then mydir="$PWD"; fi
. $mydir/shflags

# command-line flags
DEFINE_string ns '' 'Namespace of the idl file'
DEFINE_boolean pb3 false 'Use new data structures introduced in protobuf 3'

# parse the command-line
FLAGS "$@" || exit 1
eval set -- "${FLAGS_ARGV}"

# Printing to stderr
function error() {
    >&2 echo "idl2proto: $@"
}

if [[ "$1" == "" ]]; then
    flags_help
    exit 1
fi
if [[ $(basename "$1" | awk -F. '{print $2}') != "idl" ]]; then
    error "$1 is not an idl file";
    exit 1
fi

package_statement=""
if [[ "$FLAGS_ns" != "" ]]; then
    package_statement="package $FLAGS_ns;\n"
fi

sed -e ':a;N;$!ba;s/=\s*\n\+/=/g' $1 | \
 sed -e 's/;\//; \//g' \
    -e 's/^\(\s*#\(define\>\|if\|endif\|undef\)\)/\/\/ \1/g' \
    -e 's/^\s*struct\>/message/g' \
    -e 's/\<\(range\|regex\|rest\)\s*\(([^)]*)\)\s*,*\(.*\)/\3 \/\/\1\2/g' \
    -e 's/\<default(\([^)]*\))\s*,*/[default=\1]/g' \
    -e 's/^\s*\(\w\+\s\+\w\+\)\s*\[[0-9]*\]\(.*\)/rrepeated \1\2/g' \
    -e 's/^\s*\(\w\+\s\+\w\+\s*=.*\)\<\(vector\|array\)(\w*)\s*,*\(.*\)/rrepeated \1\3/g' \
    -e 's/^\s*\(\w\+\s\+\w\+\s*=.*\)\<map(\w*)\s*,*\(.*\)/map \1\2/g' \
    -e 's/^\s*map\s\+\(\w\+\s\+\w\+\s*=.*\)\<optional()\s*,*\(.*\)/map \1\2/g' \
    -e 's/^\s*\(\w\+\s\+\w\+\s*=.*\)\<optional()\s*,*\(.*\)/optional \1\2/g' \
    -e 's/^\s*rrepeated\s\+\(\w\+\s\+\w\+\s*=.*\)\<optional()\s*,*\(.*\)/repeated \1\2/g' \
    -e 's/^\s*\(\(optional\|repeated\|rrepeated\|map\)\s\+\w\+\s\+\w\+\)\s*=/\1 /g' \
    -e 's/^\s*\(\(optional\|repeated\|rrepeated\|map\)\s\+\w\+\s\+\w\+\)\s*;/\1 ;/g' \
    -e 's/^\s*\(\w\+\s\+\w\+\)\s*;/required \1 ;/g' \
    -e 's/^\s*\(\w\+\s\+\w\+\)\s*=\(.*\)/required \1 \2/g' \
    -e 's/\[default=\(0\|""\)\]//g' \
    -e 's/^\(\s*\)\w\+\s\+\(\w\+\)\s*(\s*\(\w\+\)\s\+\w\+\s*,\s*out\s\+\(\w\+\)\s\+\w\+\s*)\s*;/\1rpc \2(\3) returns (\4);/g' \
    -e 's/^\s*#include  *\"\([^.]*\.\)idl *\"/import \"\1proto\";/g' | \
 awk 'BEGIN{\
        typemap["uint64_t"] = "uint64";\
        typemap["int64_t"] = "int64";\
        typemap["int32_t"] = "int32";\
        typemap["uint32_t"] = "uint32";\
        typemap["int16_t"] = "int32";\
        typemap["uint16_t"] = "uint32";\
        typemap["int8_t"] = "int32";\
        typemap["uint8_t"] = "uint32";\
        typemap["binary"] = "bytes";\
        idlopt["int8_t"] = "[(idl_type)=IDL_INT8]";\
        idlopt["uint8_t"] = "[(idl_type)=IDL_UINT8]";\
        idlopt["int16_t"] = "[(idl_type)=IDL_INT16]";\
        idlopt["uint16_t"] = "[(idl_type)=IDL_UINT16]";\
        primitives["int8_t"] = 1; \
        primitives["int16_t"] = 1; \
        primitives["int32_t"] = 1; \
        primitives["int64_t"] = 1; \
        primitives["uint8_t"] = 1; \
        primitives["uint16_t"] = 1; \
        primitives["uint32_t"] = 1; \
        primitives["uint64_t"] = 1; \
        primitives["bool"] = 1; \
        primitives["float"] = 1; \
        primitives["double"] = 1; \
        
        print "// Converted from '$1' by public/mcpack2pb/idl2proto\n" \
              "syntax=\"proto2\";\n" \
              "import \"idl_options.proto\";\n" \
              "option (idl_support) = true;\n\n" \
              "option cc_generic_services = true;\n" \
              "'"$package_statement"'" \
    }{\
        if ($1 == "message") {\
            count = 0;\
            message_name = $2; \
            print $0;\
        } else if ($1 == "required" || $1 == "optional" || \
                   $1 == "repeated" || $1 == "rrepeated" || \
                   $1 == "map") { \
            if ($1 == "rrepeated") { $1 = "repeated"; $4 = "[(idl_on)=1]"$4 } \
            if ($1 == "repeated" && ($2 in primitives)) { $4 = "[packed=true]"$4 } \
            if ($2 in idlopt) { $4 = idlopt[$2]$4 } \
            if ($2 in typemap) { $2 = typemap[$2] } \
            if ($1 == "map") { \
                if ("'"$FLAGS_pb3"'" == "0") { \
                    $1 = "map<string,";\
                    $2 = $2">";\
                } else {\
                    $1 = "repeated";          \
                    if (!($2 in need_map)) {\
                        need_map[$2] = message_name; \
                    } \
                    $2 = need_map[$2] "MapEntry_string_" $2; \
                }\
            } \
            $3 = $3" = "++count;\
            if ($5 ~ /^\/\// || $5 ~ /^\/\*/) {\
                len_before = length($1) + length($2) + length($3) + length($4) + 3;\
                spaces = "";\
                for (; len_before < 50; ++len_before) {\
                    spaces = spaces" ";\
                }\
                $5 = spaces $5;\
            }\
            print "    "$0;\
        } else { print $0 }\
    } END{\
        for (value in need_map) { \
            print "\nmessage " need_map[value] "MapEntry_string_" value " {\n" \
                  "    required string key = 1;\n" \
                  "    optional " value " value = 2;\n" \
                  "}"; \
        }\
    }' | \
 sed -e 's/ ;/; /g' -e 's/};/}/g' -e 's/\]\[/, /g'
