#include "cmdlineparser.h"
#include <iostream>
using namespace std;
template<typename T>
bool mapHasKey(map<int, T> *pMap, int key){
    try {
        pMap->at(key);
        return true;
    }catch(exception e) {
        return false;
    }
}
CmdLineParser::CmdLineParser(string name,string desc)
{
    appDesc = desc;
    appName = name;
}

void CmdLineParser::regOpt(OptDefine optdef)
{
    optMap[optdef.shortName] = optdef;
}

void CmdLineParser::regOpt(string name, int shorName, int needParam, string desc)
{
    OptDefine opt = {name,shorName, needParam, desc};
    regOpt(opt);
}

string CmdLineParser::getOpt(string key)
{
    if(!hasOptSet(key)){
        return "";
    }
    return paraMap.at(key);
}

void CmdLineParser::parseArgs(int argc, char *argv[])
{
    size_t s=optMap.size();
    struct option *longOption = new option[s]();
    int index=0;
    string shortArgs;
    for (auto i = optMap.begin(); i != optMap.end(); ++i){
        struct option logopt={0};
        OptDefine od = i->second;
        logopt.name = od.name.c_str();
        logopt.flag = NULL;
        logopt.has_arg = od.needParam;
        logopt.val = od.shortName;
        if(logopt.val>=65 && logopt.val<=90){
            // this is a short name
            shortArgs.append(1,char(logopt.val));
            if(logopt.has_arg != no_argument){
                shortArgs.append(1,':');
            }
        }
        index++;
    }
    int optIndex=0;
    int opt;
    while ((opt=getopt_long(argc, argv, shortArgs.c_str(), longOption, &optIndex))!=-1) {
        if(!mapHasKey(&optMap, opt)){
            help();
            exit(0);
        }
        OptDefine d = optMap.at(opt);

        if(d.needParam == no_argument){
            paraMap[d.name] = "true";
        } else {
            paraMap[d.name] = optarg;
        }
    }
    //释放动态构造的option数组

    delete[] longOption;
}

bool CmdLineParser::hasOptSet(string key)
{

    if(paraMap.find(key) == paraMap.end()){
        return false;
    }
    return true;
}

void CmdLineParser::help()
{
    cout<< appName <<":" << appDesc << endl;
    cout<< "Usage:" << appName << "[opt] optvalue" << endl;
    for(auto i = optMap.begin();i!=optMap.end(); ++i) {
        OptDefine od = i->second;
        cout<<"--"<<od.name;
        if(od.shortName>=65&&od.shortName<=90){
            cout<<",-"<<char(od.shortName);
        } else {
            cout<<"   ";
        }
        cout<<"     "<<od.desc<<endl;
    }
}
