#include "ats_argv.h"

using namespace ats;
using namespace std;

namespace ats
{
	int ctoi(char c)
	{
		char tmp[] = {c};
		return atoi(tmp);
	}
}

// -----------------------------------------------------------------
Argv::Argv(int argc, char** argv, char* option_list, char* param_option)
: m_argc(argc), m_argv(argv), m_option_list(option_list), m_param_option(param_option)
{
	m_str_argv.resize(m_argc);

	for(int i=0; i < m_argc; i++)
	{
		m_str_argv[i] = m_argv[i];
	}

	// 引数が2つ以上 かつoption_listのどれかに何か入っている
	if(m_argc > 1 && (option_list != 0 || param_option != 0)){
		InitOption();
	}
}

// -----------------------------------------------------------------
void Argv::InitOption()
{
	string option_list, param_option, arg_option;

	// i = プログラム引数の解析位置(1番はプログラム名のため飛ばす)
	for(int i=1; i < m_argc; i++){
		// 1文字目が'-'か'/'ならばオプションとして登録
		if(('-' == m_str_argv[i][0]) || ('/' == m_str_argv[i][0])){
			// '-'と'/'を消去
			string current_token(m_str_argv[i].c_str() + 1);
			// m_user_optionに追加
			m_user_option.append(current_token);
			// 引数を取るものが含まれればmap<char, vector<string> >に登録
			int op = FindParamOption(current_token);
			if(op > -1){
				// パラメーターが指定と合わない場合は例外を受け取る
				// ここでは無視するが、ユーザーはIsParameterで検出す
				// ることができる。
				try {
					// i += 解析した数だけ解析位置を進める。
					i += RegistParam(m_param_option[op], ctoi(m_param_option[op+1]), i);
				}
				catch (...) {
				}
			}
		}
	}
}

// ユーザーの指定したオプションが(引数を取って)解釈しなければならな
// いものか判断。
// 引数 : ユーザーが指定したオプションの入った文字列('-'や'/'を含んではならない)
// 戻値 : 解析しなけばならない文字の位置(m_param_option[])
// 引数を取らないオプションか、解釈しなくてもよいオプションなら(-1)
int Argv::FindParamOption(string& user_option)
{
	// プログラマの指定するオプションの位置
	int pg_op_num = 0;
	// 指定された文字列を走査
	for(int i=0; i < user_option.size(); i++){
		// 引数を取るオプションが見つかればその場所を返す
		// (プログラマの指定する文字列とマッチするか)
		pg_op_num = m_param_option.find(user_option[i]);
		if(pg_op_num != string::npos){
			return pg_op_num;
		}
	}
	// 見つからない場合は-1を返す。
	return -1;
}

// 指定された文字と、パラメータ引数を登録
// 引数 : オプションの文字、読み込むパラメーター数、現在の解析位置
// 戻値 : 解析数(登録数ではない)
int Argv::RegistParam(char option, int param_num, int str_current)
{
	// pair<char, vector<string> >を作成するための一時オブジェクト
	vector<string> tmp(param_num);
	int i;          // 解析数
	str_current++;	// 解析位置を進める

	// パラメーター読み込み開始
	for(i=0; i < param_num; i++, str_current++){
		if(('-' == m_str_argv.at(str_current).at(0)) || ('/' == m_str_argv.at(str_current).at(0))){
			// ただし、1文字目に'-'か'/'が検出された場合は即終了。
			// i = 解析数
			return i;
		}
		tmp[i] = m_str_argv[str_current];
	}
	// 登録
	m_user_parameter.insert(pair<char, vector<string> >(option, tmp));
	return i;
}

// -----------------------------------------------------------------
Argv::~Argv()
{

}

// -----------------------------------------------------------------
const string& Argv::operator[](const int a) const
{
	return m_str_argv[a];
}

// -----------------------------------------------------------------
char Argv::Option(const int a) const
{
	return m_user_option[a];
}

// -----------------------------------------------------------------
int Argv::OptionSize() const
{
	return m_user_option.size();
}

// -----------------------------------------------------------------
string Argv::Parameter(const char key, const int num)
{
	map<char, vector<string> >::iterator itr;
	itr = m_user_parameter.find(key);
	if(itr != m_user_parameter.end())
		return itr->second[num];
	else
		return string();
}

// -----------------------------------------------------------------
int Argv::ParameterSize(const char key)
{
	map<char, vector<string> >::iterator itr;
	itr = m_user_parameter.find(key);
	if(itr != m_user_parameter.end())
		return itr->second.size();
	else
		return 0;
}

// -----------------------------------------------------------------
bool Argv::IsParameter(const char key)
{
	map<char, vector<string> >::iterator itr;
	itr = m_user_parameter.find(key);
	// キーに対応するvector<string>(パラメーター)が登録されていて
	if(itr != m_user_parameter.end()){
		vector<string>& vec = itr->second;
		// vector<string>の最後のstringが空なら false
		if((vec.end() -1)->empty() == true){
			return false;
		}
		// vector<string>の最後のstringになにか入ってれば true
		else {
			return true;
		}
	}
	// キーに対応するvector<string>が登録されてない場合 false
	else {
		return false;
	}
}

// -----------------------------------------------------------------
bool Argv::FindOption(const char c)
{
	if(m_user_option.find(c) != string::npos)
		return true;
	else
		return false;
}


