//    OpenVPN -- An application to securely tunnel IP networks
//               over a single port, with support for SSL/TLS-based
//               session authentication and key exchange,
//               packet encryption, packet authentication, and
//               packet compression.
//
//    Copyright (C) 2012-2017 OpenVPN Inc.
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU Affero General Public License Version 3
//    as published by the Free Software Foundation.
//
//    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 Affero General Public License for more details.
//
//    You should have received a copy of the GNU Affero General Public License
//    along with this program in the COPYING file.
//    If not, see <http://www.gnu.org/licenses/>.

#ifndef OPENVPN_TIME_DURHELPER_H
#define OPENVPN_TIME_DURHELPER_H

#include <openvpn/common/options.hpp>
#include <openvpn/time/time.hpp>
#include <openvpn/random/randapi.hpp>

namespace openvpn {
  inline void set_duration_parm(Time::Duration& dur,
				const std::string& name,
				const std::string& valstr,
				const unsigned int min_value,
				const bool x2, // multiply result by 2
				const bool ms) // values are in milliseconds rather than seconds
  {
    const unsigned int maxdur = ms ? 1000*60*60*24 : 60*60*24*7; // maximum duration -- milliseconds: 1 day, seconds: 7 days
    unsigned int value = 0;
    const bool status = parse_number<unsigned int>(valstr, value);
    if (!status)
      OPENVPN_THROW(option_error, name << ": error parsing number of " << (ms ? "milliseconds" : "seconds"));
    if (x2)
      value *= 2;
    if (value == 0 || value > maxdur)
      value = maxdur;
    if (value < min_value)
      value = min_value;
    dur = ms ? Time::Duration::milliseconds(value) : Time::Duration::seconds(value);
  }

  inline const Option* load_duration_parm(Time::Duration& dur,
					  const std::string& name,
					  const OptionList& opt,
					  const unsigned int min_value,
					  const bool x2,
					  const bool allow_ms)
  {
    // look for milliseconds given as <name>-ms
    if (allow_ms)
      {
	const Option *o = opt.get_ptr(name + "-ms");
	if (o)
	  {
	    set_duration_parm(dur, name, o->get(1, 16), min_value, x2, true);
	    return o;
	  }
      }

    // look for seconds given as <name>
    {
      const Option *o = opt.get_ptr(name);
      if (o)
	set_duration_parm(dur, name, o->get(1, 16), allow_ms ? 1 : min_value, x2, false);
      return o;
    }
  }

  inline Time::Duration load_duration_default(const std::string& name,
					      const OptionList& opt,
					      const Time::Duration& default_duration,
					      const unsigned int min_value,
					      const bool x2,
					      const bool allow_ms)
  {
    Time::Duration ret(default_duration);
    load_duration_parm(ret, name, opt, min_value, x2, allow_ms);
    return ret;
  }

  inline Time::Duration skew_duration(const Time::Duration& dur,
				      const Time::Duration& min,
				      const unsigned int flux_order,
				      RandomAPI& rng)
  {
    const unsigned int range = 1 << flux_order;
    const int delta = int(rng.rand_get<unsigned int>() & (range-1)) - int(range>>1);
    const Time::Duration ret = dur + delta;
    if (ret >= min)
      return ret;
    else
      return min;
  }
}

#endif
