/* -*- mia-c++  -*-
 *
 * This file is part of MIA - a toolbox for medical image analysis
 * Copyright (c) Leipzig, Madrid 1999-2017 Gert Wollny
 *
 * MIA is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with MIA; if not, see <http://www.gnu.org/licenses/>.
 *
 */

#include <stdexcept>
#include <cassert>
#include <cstring>

#include <mia/core/optionparser.hh>
#include <mia/core/msgstream.hh>


NS_MIA_BEGIN
using namespace std;

char CComplexOptionParser::m_parts_seperator      = '+';
char CComplexOptionParser::m_part_options         = ':';
char CComplexOptionParser::m_option_seperator     = ',';
char CComplexOptionParser::m_assignment_seperator = '=';


CComplexOptionParser::CComplexOptionParser(const string& param)
{
       cvdebug() << "CComplexOptionParser: constr '" << param << "'\n";
       typedef list<string> string_list;
       string_list parts;

       if (split(param, m_parts_seperator, parts) == 0)
              return;

       auto i = parts.begin();
       auto e = parts.end();

       while (i != e) {
              auto  part = split_pair(*i, m_part_options);
              CParsedOptions options;
              cvdebug() << "CComplexOptionParser: '" << part.first << "' : '" << part.second
                        << "' len=" << part.second.size() << "\n";
              string help = part.second;

              while (!help.empty()) {
                     cvdebug() << "split '" << help << "'\n";
                     auto next_option = split_pair(help, m_assignment_seperator);

                     if (next_option.second.empty()) {
                            help = next_option.second;
                     } else {
                            auto tail = split_pair_b(next_option.second, m_option_seperator);
                            next_option.second = tail.first;
                            help = tail.second;
                     }

                     cvdebug() << "got option: '" << next_option.first << "=[" << next_option.second << "]'\n";
                     options.insert(next_option);
              }

              cvdebug() << "CComplexOptionParser: add '" << part.first << "'\n";
              m_Parts.push_back(make_pair(part.first, options));
              ++i;
       }

       cvdebug() << "CComplexOptionParser: constr - got " << m_Parts.size() << " option(s)\n";
}

CComplexOptionParser::const_iterator
CComplexOptionParser::begin()const
{
       return m_Parts.begin();
}



CComplexOptionParser::const_iterator
CComplexOptionParser::end()const
{
       return m_Parts.end();
}


CComplexOptionParser::CParts::size_type CComplexOptionParser::size() const
{
       return m_Parts.size();
}


void CComplexOptionParser::set_seperators(SSeperators s)
{
       assert(strlen(s) >= 4);
       m_parts_seperator      = s[0];
       m_part_options         = s[1];
       m_option_seperator     = s[2];
       m_assignment_seperator = s[3];
}

int CComplexOptionParser::split(const string& s, char c, list<string>& result)const
{
       string::size_type i = 0;

       while (i < s.size()) {
              string::size_type start = i;
              int level = 0;

              while (i < s.size() && ( s[i] != c || level > 0 )) {
                     if (s[i] == '[')
                            ++level;

                     if (s[i] == ']') {
                            if (level > 0)
                                   --level;
                            else
                                   throw invalid_argument("CComplexOptionParser: encountered ']' without leading '['");
                     }

                     ++i;
              }

              if (level > 0)
                     throw invalid_argument("CComplexOptionParser: miss closing ']' ");

              if (i < s.size()) {
                     result.push_back(s.substr(start, i - start));
                     ++i;
              } else {
                     result.push_back(s.substr(start, s.size() - start));
              }
       }

       return result.size();
}

CParsedOptions::value_type
CComplexOptionParser::split_pair(const string& s, char c)const
{
       size_t i = s.find(c);

       if (i < s.size()) {
              return CParsedOptions::value_type(s.substr(0, i), s.substr(i + 1, s.size() - i - 1));
       } else
              return CParsedOptions::value_type(s, string(""));
}

CParsedOptions::value_type
CComplexOptionParser::split_pair_b(const string& s, char c)const
{
       bool has_protector = (s[0] == '[');

       if (has_protector) {
              int pdepth = 0;
              size_t i = 1;

              while ( ( pdepth || (s[i] != ']')) && (i < s.size())) {
                     if (s[i] == '[')
                            ++pdepth;
                     else if (s[i] == ']')
                            --pdepth;

                     ++i;
              }

              if (i >= s.size())
                     throw runtime_error(string("Parsing option ") + s + string(" failed"));

              if (i < s.size() - 1) {
                     return CParsedOptions::value_type(s.substr(1, i - 1), s.substr(i + 2, s.size() - i - 1));
              } else
                     return CParsedOptions::value_type(s.substr(1, i - 1), string(""));
       } else {
              size_t i = s.find(c);

              if (i < s.size()) {
                     return CParsedOptions::value_type(s.substr(0, i), s.substr(i + 1, s.size() - i - 1));
              } else
                     return CParsedOptions::value_type(s, string(""));
       }
}


NS_MIA_END
