// File:    fullSample.cpp
// Library: SimpleOpt
// Author:  Brodie Thiesfield <code@jellycan.com>
// Source:  http://code.jellycan.com/simpleopt/
//
// MIT LICENCE
// ===========
// The licence text below is the boilerplate "MIT Licence" used from:
// http://www.opensource.org/licenses/mit-license.php
//
// Copyright (c) 2006, Brodie Thiesfield
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is furnished
// to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#if defined(_MSC_VER)
# include <windows.h>
# include <tchar.h>
#else
# define TCHAR		char
# define _T(x)		x
# define _tprintf	printf
# define _tmain		main
# define _ttoi      atoi
#endif

#include <stdio.h>
#include <locale.h>
#include "SimpleOpt.h"
#include "SimpleGlob.h"

static void ShowUsage()
{
    _tprintf(
        _T("Usage: fullSample [OPTIONS] [FILES]\n")
        _T("\n")
        _T("--exact     Disallow partial matching of option names\n")
        _T("--noslash   Disallow use of slash as an option marker on Windows\n")
        _T("--shortarg  Permit arguments on single letter options with no equals sign\n")
        _T("--clump     Permit single char options to be clumped as long string\n")
        _T("--noerr     Do not generate any errors for invalid options\n")
        _T("--pedantic  Generate an error for petty things\n")
        _T("\n")
        _T("-d  -e  -f  -g  -flag  --flag               Flag (no arg)\n")
        _T("-s ARG   -sep ARG  --sep ARG                Separate required arg\n")
        _T("-cARG    -c=ARG    -com=ARG    --com=ARG    Combined required arg\n")
        _T("-o[ARG]  -o[=ARG]  -opt[=ARG]  --opt[=ARG]  Combined optional arg\n")
        _T("-man     -mandy    -mandate                 Shortcut matching tests\n")
        _T("--man    --mandy   --mandate                Shortcut matching tests\n")
        _T("--multi0 --multi1 ARG --multi2 ARG1 ARG2    Multiple argument tests\n")
        _T("--multi N ARG-1 ARG-2 ... ARG-N             Multiple argument tests\n")
        _T("open read write close zip unzip             Special words\n")
        _T("\n")
        _T("-?  -h  -help  --help                       Output this help.\n")
        _T("\n")
        _T("If a FILE is `-', read standard input.\n")
        );
}

CSimpleOpt::SOption g_rgFlags[] =
{
    { SO_O_EXACT,    _T("--exact"),     SO_NONE },
    { SO_O_NOSLASH,  _T("--noslash"),   SO_NONE },
    { SO_O_SHORTARG, _T("--shortarg"),  SO_NONE },
    { SO_O_CLUMP,    _T("--clump"),     SO_NONE },
    { SO_O_NOERR,    _T("--noerr"),     SO_NONE },
    { SO_O_PEDANTIC, _T("--pedantic"),  SO_NONE },
    SO_END_OF_OPTIONS
};

enum { OPT_HELP = 0, OPT_MULTI = 100, OPT_MULTI0, OPT_MULTI1, OPT_MULTI2  };
CSimpleOpt::SOption g_rgOptions[] =
{
    { OPT_HELP,   _T("-?"),           SO_NONE    },
    { OPT_HELP,   _T("-h"),           SO_NONE    },
    { OPT_HELP,   _T("-help"),        SO_NONE    },
    { OPT_HELP,   _T("--help"),       SO_NONE    },
    {  1,         _T("-"),            SO_NONE    },
    {  2,         _T("-d"),           SO_NONE    },
    {  3,         _T("-e"),           SO_NONE    },
    {  4,         _T("-f"),           SO_NONE    },
    {  5,         _T("-g"),           SO_NONE    },
    {  6,         _T("-flag"),        SO_NONE    },
    {  7,         _T("--flag"),       SO_NONE    },
    {  8,         _T("-s"),           SO_REQ_SEP },
    {  9,         _T("-sep"),         SO_REQ_SEP },
    { 10,         _T("--sep"),        SO_REQ_SEP },
    { 11,         _T("-c"),           SO_REQ_CMB },
    { 12,         _T("-com"),         SO_REQ_CMB },
    { 13,         _T("--com"),        SO_REQ_CMB },
    { 14,         _T("-o"),           SO_OPT     },
    { 15,         _T("-opt"),         SO_OPT     },
    { 16,         _T("--opt"),        SO_OPT     },
    { 17,         _T("-man"),         SO_NONE    },
    { 18,         _T("-mandy"),       SO_NONE    },
    { 19,         _T("-mandate"),     SO_NONE    },
    { 20,         _T("--man"),        SO_NONE    },
    { 21,         _T("--mandy"),      SO_NONE    },
    { 22,         _T("--mandate"),    SO_NONE    },
    { 23,         _T("open"),         SO_NONE    },
    { 24,         _T("read"),         SO_NONE    },
    { 25,         _T("write"),        SO_NONE    },
    { 26,         _T("close"),        SO_NONE    },
    { 27,         _T("zip"),          SO_NONE    },
    { 28,         _T("unzip"),        SO_NONE    },
    { OPT_MULTI,  _T("--multi"),      SO_MULTI   },
    { OPT_MULTI0, _T("--multi0"),     SO_MULTI   },
    { OPT_MULTI1, _T("--multi1"),     SO_MULTI   },
    { OPT_MULTI2, _T("--multi2"),     SO_MULTI   },
    SO_END_OF_OPTIONS
};

void ShowFiles(int argc, TCHAR ** argv) {
    // glob files to catch expand wildcards
    CSimpleGlob glob(SG_GLOB_NODOT|SG_GLOB_NOCHECK);
    if (SG_SUCCESS != glob.Add(argc, argv)) {
        _tprintf(_T("Error while globbing files\n"));
        return;
    }

    for (int n = 0; n < glob.FileCount(); ++n) {
        _tprintf(_T("file %2d: '%s'\n"), n, glob.File(n));
    }
}

static const TCHAR * 
GetLastErrorText(
    int a_nError
    ) 
{
    switch (a_nError) {
    case SO_SUCCESS:            return _T("Success");
    case SO_OPT_INVALID:        return _T("Unrecognized option");
    case SO_OPT_MULTIPLE:       return _T("Option matched multiple strings");
    case SO_ARG_INVALID:        return _T("Option does not accept argument");
    case SO_ARG_INVALID_TYPE:   return _T("Invalid argument format");
    case SO_ARG_MISSING:        return _T("Required argument is missing");
    case SO_ARG_INVALID_DATA:   return _T("Invalid argument data");
    default:                    return _T("Unknown error");
    }
}

static void 
DoMultiArgs(
    CSimpleOpt &    args, 
    int             nMultiArgs
    )
{
    TCHAR ** rgpszArg = NULL;

    // get the number of arguments if necessary
    if (nMultiArgs == -1) {
        // first arg is a count of how many we have
        rgpszArg = args.MultiArg(1);
        if (!rgpszArg) {
            _tprintf(
                _T("%s: '%s' (use --help to get command line help)\n"),
                GetLastErrorText(args.LastError()), args.OptionText());
            return;
        }

        nMultiArgs = _ttoi(rgpszArg[0]);
    }
    _tprintf(_T("%s: expecting %d args\n"), args.OptionText(), nMultiArgs);

    // get the arguments to follow
    rgpszArg = args.MultiArg(nMultiArgs);
    if (!rgpszArg) {
        _tprintf(
            _T("%s: '%s' (use --help to get command line help)\n"),
            GetLastErrorText(args.LastError()), args.OptionText());
        return;
    }

    for (int n = 0; n < nMultiArgs; ++n) {
        _tprintf(_T("MultiArg %d: %s\n"), n, rgpszArg[n]);
    }
}


int _tmain(int argc, TCHAR * argv[]) {
    setlocale( LC_ALL, "Japanese" );

    // get the flags to use for SimpleOpt from the command line
    int nFlags = SO_O_USEALL;
    CSimpleOpt flags(argc, argv, g_rgFlags, SO_O_NOERR|SO_O_EXACT);
    while (flags.Next()) {
        nFlags |= flags.OptionId();
    }

    // now process the remainder of the command line with these flags
    CSimpleOpt args(flags.FileCount(), flags.Files(), g_rgOptions, nFlags);
    while (args.Next()) {
        if (args.LastError() != SO_SUCCESS) {
            _tprintf(
                _T("%s: '%s' (use --help to get command line help)\n"),
                GetLastErrorText(args.LastError()), args.OptionText());
            continue;
        }

        switch (args.OptionId()) {
        case OPT_HELP:
            ShowUsage();
            return 0;
        case OPT_MULTI:  
            DoMultiArgs(args, -1);
            break;
        case OPT_MULTI0: 
            DoMultiArgs(args, 0);
            break;
        case OPT_MULTI1: 
            DoMultiArgs(args, 1);
            break;
        case OPT_MULTI2: 
            DoMultiArgs(args, 2);
            break;
        default:
            if (args.OptionArg()) {
                _tprintf(_T("option: %2d, text: '%s', arg: '%s'\n"),
                    args.OptionId(), args.OptionText(), args.OptionArg());
            }
            else {
                _tprintf(_T("option: %2d, text: '%s'\n"),
                    args.OptionId(), args.OptionText());
            }
        }
    }

    /* process files from command line */
    ShowFiles(args.FileCount(), args.Files());

	return 0;
}
