#include "CE_ARGV.h"

CE_ARGV::CE_ARGV(wchar_t* cmdLine)
: ce_argv_(0)
, ce_argc_(0)
{
    const wchar_t* dummyArgv = L"root";  // dummy for the first argv
    const wchar_t* separator = L" ";     // blank space is a separator

    int formattedCmdLineLength = wcslen(dummyArgv) +
                                 wcslen(separator) +
                                 1;  // 1 is for the NULL at the end

    if (wcslen(cmdLine) > 0) {
        formattedCmdLineLength += wcslen(cmdLine);
        formattedCmdLineLength += wcslen(separator);
    }

    // formattedCmdLine will have dummyArgv and a separator at the beginning of cmdLine
    // and a separator at the end to generalize format and reduce the amount of code
    wchar_t* formattedCmdLine = 0;
    formattedCmdLine = new wchar_t[formattedCmdLineLength];

    wcscpy(formattedCmdLine, dummyArgv);
    wcscat(formattedCmdLine, separator);

    int max_possible_argc = 1;  // start with 1 because of the dummyArgv at the beginning

    if (wcslen(cmdLine) > 0) {
        int formattedPos  = wcslen(formattedCmdLine);
        int cmdLineLength = wcslen(cmdLine);

        // Inside of this for loop, it does same thing as strcat except it
        // checks and puts only one single white space between two argv entries.
        for (int i = 0; i < cmdLineLength; ++i) {
            if (iswspace(cmdLine[i]) != 0) {
                ++max_possible_argc;  // counting the number of white spaces
            }

            formattedCmdLine[formattedPos++] = cmdLine[i];

            if (iswspace(cmdLine[i]) != 0) {
                // make sure there is only one white space between two argv entries.
                while ((i < cmdLineLength) && (iswspace(cmdLine[i + 1]) != 0)) {
                    ++i;
                }
            }
        }

        formattedCmdLine[formattedPos] = 0;
        wcscat(formattedCmdLine, separator);  // make sure formattedCmdLine ends with a blank
    }

    int formattedCmdLength = wcslen(formattedCmdLine);

    bool insideQuotation = false;
    int* argv_strlen = 0;
    int entry_size = 0;
    argv_strlen = new int[max_possible_argc];

    // determine argc
    for (int i = 0; i < formattedCmdLength; ++i) {
        if (formattedCmdLine[i] == '\\') {
            ++i; // ignore the following character
            ++entry_size;
        }
        else if (formattedCmdLine[i] == '"') {
            insideQuotation = !insideQuotation;
        }
        else if ((!insideQuotation) && (iswspace(formattedCmdLine[i]) != 0)) {
            // new argv entry end found
            argv_strlen[ce_argc_++] = entry_size;  // cache the size of this entry
            entry_size = 0;
        }
        else {
            ++entry_size;
        }
    }

    ce_argv_ = new wchar_t*[ce_argc_ + 1];
    ce_argv_[ce_argc_] = 0;  // Last command line entry is a NULL.

    for (int j = 0, cmdLinePos = 0; j < ce_argc_; ++j, ++cmdLinePos) {
        int length = argv_strlen[j];

        ce_argv_[j] = new wchar_t[length + 1];
        ce_argv_[j][length] = 0;  // string termination null

        if (iswspace(formattedCmdLine[cmdLinePos]) != 0) {
            // This is where prior argv has trailing '"' at the end.
            ++cmdLinePos;
        }

        for (int n = 0; n < length; ++n, ++cmdLinePos) {
            if ((formattedCmdLine[cmdLinePos] == '\\') || (formattedCmdLine[cmdLinePos] == '"')) {
                ++cmdLinePos;
            }

            ce_argv_[j][n] = formattedCmdLine[cmdLinePos];
        }
    }

    delete argv_strlen;
    delete formattedCmdLine;
}


CE_ARGV::~CE_ARGV(void)
{
    for (int i = 0; i < ce_argc_; ++i) {
        delete [] ce_argv_[i];
    }

    delete [] ce_argv_;
}
