/**
 * Binary/text to C source conversion tool.
 * @author Jonathon Fowler <jf@jonof.id.au>
 * @license Artistic License 2.0 (http://www.perlfoundation.org/artistic_license_2_0)
 */

#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
#include <cstdlib>

using namespace std;


void usage()
{
    cout << "Binary/text to C source conversion tool" << endl;
    cout << "Copyright (C) 2018 Jonathon Fowler <jf@jonof.id.au>" << endl;
    cout << "Released under the Artistic License 2.0" << endl;
    cout << endl;
    cout << "  bin2c [-text] <sourcefile> <symbol>" << endl;
    cout << "    <symbol>    Set the symbol name in the output file (default 'bin2cdata')" << endl;
    cout << "    -text       Generate text format output" << endl;
    cout << endl;
}

int main(int argc, const char * const argv[])
{
    bool showusage = false;
    string sourcefile;
    string symbol("bin2cdata");
    bool textmode = false;

    if (argc < 2) {
        showusage = true;
    } else {
        int    anonparam = 0;
        string opt;

        for (int i = 1; i < argc; ++i) {
            if (argv[i][0] == '-') {
                opt = string(argv[i]).substr(1);
                if (opt == "text") {
                    textmode = true;
                } else {
                    cerr << "error: unrecognised option -" << opt << endl;
                    showusage = true;
                }
            } else {
                opt = string(argv[i]);
                switch (anonparam) {
                    case 0:
                        sourcefile = opt;
                        break;
                    case 1:
                        symbol = opt;
                        break;
                    default:
                        cerr << "warning: too many parameters passed" << endl;
                        break;
                }
                anonparam++;
            }
        }
    }

    if (showusage) {
        usage();
        return 1;
    } else {
        ifstream sourcefs(sourcefile.c_str(), ios::in | ios::binary);
        int bytecount = 0;
        char chr;

        if (!sourcefs.is_open()) {
            cerr << "error: could not open " << sourcefile << " for reading." << endl;
            return 1;
        }
        cout << "// Generated by bin2c. Do not edit directly.\n\n";
        if (!textmode) {
            cout << "const unsigned char " << symbol << "[] =";
            cout << hex << setfill('0');
            cout << " {\n";
            while (sourcefs.get(chr)) {
                if ((bytecount & 15) == 0) {
                    cout << "    ";
                } else {
                    cout << " ";
                }
                cout << "0x" << setw(2) << (unsigned)(unsigned char)chr << ",";
                bytecount++;
                if ((bytecount & 15) == 0) {
                    cout << "\n";
                }
            }
            if ((bytecount & 15) > 0) {
                cout << "\n";
            }
            cout << "};\n";
        } else {
            int charcount = 0;

            cout << "const char " << symbol << "[] =";
            cout << "\n";
            while (sourcefs.get(chr)) {
                bytecount++;
                if (charcount == 0) {
                    cout << "    \"";
                }
                charcount++;
                switch (chr) {
                    case '\0':
                        cout << "\\0";
                        break;
                    case '"':
                        cout << "\\\"";
                        break;
                    case '\t':
                        cout << "\\t";
                        break;
                    case '\r':
                        cout << "\\r";
                        break;
                    case '\n':
                        cout << "\\n\"\n";
                        charcount = 0;
                        break;
                    default:
                        if (chr < 32 || chr >= 127) {
                            cout << "\\x" << hex << chr;
                        } else {
                            cout << chr;
                        }
                        break;
                }
            }
            if (charcount != 0) {
                cout << "\"\n";
            }
            cout << ";\n";
        }

        cout << "\nconst int " << symbol << "_size = " << dec << bytecount << ";\n";

        sourcefs.close();
    }

    return 0;
}
