#include <iostream>
#include <signal.h>
#include <cstdlib>
#include <boost/program_options.hpp>
#include <thread>
#include <atomic>
#include <experimental/filesystem>
#include <chrono>

#include <sdm/types.hpp>
#include <sdm/config.hpp>
#include <sdm/common.hpp>
#include <sdm/parser/parser.hpp>
#include <sdm/public/algorithm.hpp>
#include <sdm/algorithms.hpp>
#include <sdm/worlds.hpp>
#include <sdm/core/state/private_occupancy_state.hpp>
#include <sdm/utils/value_function/qfunction/pwlc_qvalue_function.hpp>

using namespace sdm;
using namespace std;
namespace po = boost::program_options;

double MAX_RUNNING_TIME = 1800;
bool quit_flag = false;
std::string name = "result";
std::shared_ptr<Algorithm> algorithm;

void leave()
{
    algorithm->logging();
    std::cout << "\n"
              << config::LOG_SDMS << "Exit process" << std::endl;
    algorithm = nullptr;
    exit(0);
}

void handler(int)
{
    leave();
}

int solve(int argv, char **args)
{
    // Handle interrupt signal
    signal(SIGINT, &handler);

    try
    {
        std::string world, algo_name, formalism, upper_bound, lower_bound, ub_init, lb_init, type_sampling;
        int trials, memory;
        number horizon, seed, batch_size, freq_update_lb, freq_update_ub, state_type;
        double error, discount, granularity_start, granularity_end, rate_start, rate_end, rate_decay, eps_start, eps_end, eps_decay;
        double p_b, p_o, p_c;
        bool store_actions, store_states;
        unsigned long long num_samples;

        int freq_pruning_v1 = 1, freq_pruning_v2 = 1;
        std::string type_of_resolution_v1, type_of_resolution_v2, type_of_pruning_v1, type_of_pruning_v2;

        po::options_description options("Options");
        options.add_options()
        ("help", "produce help message")
        ("test", "test the policy found")
        ("save", "save the policy found");

        po::options_description config("Configuration");
        config.add_options()
        ("algorithm,a", po::value<string>(&algo_name)->default_value("hsvi"), "the algorithm to use")
        ("world,w", po::value<string>(&world)->default_value("mabc.dpomdp"), "the world to be solved")
        ("formalism,f", po::value<string>(&formalism)->default_value("OccupancyMDP"), "the formalism to use")
        ("horizon,h", po::value<number>(&horizon)->default_value(5), "the planning horizon")
        ("discount,d", po::value<double>(&discount)->default_value(1.0), "the discount factor")
        ("memory,m", po::value<int>(&memory)->default_value(-1), "the memory for history")
        ("error,e", po::value<double>(&error)->default_value(0.001), "the error")
        ("trials,t", po::value<int>(&trials)->default_value(100000), "the maximum number of trials")
        ("seed,s", po::value<number>(&seed)->default_value(1), "the seed")
        ("batch_size,b", po::value<number>(&batch_size)->default_value(0), "the batch size used in rl")
        ("name,n", po::value<std::string>(&name)->default_value(""), "the name of the experiment")
        ("compression", po::value<number>(&state_type)->default_value(0), "The type of occupancy state (COMPRESSED, ONE_STEP, UNCOMPRESSED)")
        ("store_states", po::value<bool>(&store_states)->default_value(true), "If true, store the macro states.")
        ("store_actions", po::value<bool>(&store_actions)->default_value(true), "If true, store the macro actions.")
        ("p_c", po::value<double>(&p_c)->default_value(config::PRECISION_COMPRESSION), "The precision of the compression.")
        ("p_b", po::value<double>(&p_b)->default_value(config::PRECISION_BELIEF), "The precision of beliefs.")
        ("p_o", po::value<double>(&p_o)->default_value(config::PRECISION_OCCUPANCY_STATE), "The precision of occupancy states.")
        ("time_max", po::value<double>(&MAX_RUNNING_TIME)->default_value(1800), "The maximum running time.");

        po::options_description hsvi_config("HSVI configuration");
        hsvi_config.add_options()
        ("lower_bound", po::value<string>(&lower_bound)->default_value("tabular"), "the lower bound representation")
        ("upper_bound", po::value<string>(&upper_bound)->default_value("tabular"), "the upper bound representation")
        ("lb_init", po::value<string>(&lb_init)->default_value("Min"), "the lower bound initialization method")
        ("ub_init", po::value<string>(&ub_init)->default_value("Max"), "the upper bound initialization method")
        ("freq_update_lb", po::value<number>(&freq_update_lb)->default_value(1), "the update frequency of the lower bound.")
        ("freq_update_ub", po::value<number>(&freq_update_ub)->default_value(1), "the update frequency of the upper bound.")
        ("lb_type_of_resolution", po::value<string>(&type_of_resolution_v1)->default_value("IloIfThen"), "the type of resolution for the lower bound (ex: 'BigM:100' or 'IloIfThen' for LP)")
        ("ub_type_of_resolution", po::value<string>(&type_of_resolution_v2)->default_value("IloIfThen"), "the type of resolution for the upper bound (ex: 'BigM:100' or 'IloIfThen' for LP)")
        ("lb_freq_pruning", po::value<int>(&freq_pruning_v1)->default_value(1), "the pruning frequency for the first value function.")
        ("ub_freq_pruning", po::value<int>(&freq_pruning_v2)->default_value(1), "the pruning frequency for the second value function .")
        ("lb_type_of_pruning", po::value<string>(&type_of_pruning_v1)->default_value("none"), "the pruning type for the lower bound (ex: 'bounded', 'pairwise', 'none'")
        ("ub_type_of_pruning", po::value<string>(&type_of_pruning_v2)->default_value("none"), "the pruning type for the upper bound (ex: 'iterative', 'global', 'none'");

        po::options_description pbvi_config("PBVI configuration");
        pbvi_config.add_options()
        ("num_samples", po::value<unsigned long long>(&num_samples)->default_value(10), "the number of sample to generate in the algorithm")
        ("type_sampling", po::value<string>(&type_sampling)->default_value(""), "the type of sampling process")
        ("value_function", po::value<string>(&lower_bound), "the lower bound representation")
        ("vf_init", po::value<string>(&lb_init), "the lower bound initialization method")
        ("freq_update", po::value<number>(&freq_update_lb), "the update frequency of the lower bound.")
        ("type_of_resolution", po::value<string>(&type_of_resolution_v1), "the type of resolution for the lower bound (ex: 'BigM:100' or 'IloIfThen' for LP)")
        ("freq_pruning", po::value<int>(&freq_pruning_v1), "the pruning frequency for the first value function.")
        ("type_of_pruning", po::value<string>(&type_of_pruning_v1), "the pruning type for the lower bound (ex: 'bounded', 'pairwise', 'none'");

        po::options_description qlearning_config("Q-learning configuration");
        qlearning_config.add_options()
        ("qvalue_function", po::value<string>(&lower_bound), "the q-value function representation")
        ("eps_start", po::value<double>(&eps_start)->default_value(1.0), "the starting epsilon")
        ("eps_end", po::value<double>(&eps_end)->default_value(0.001), "the final epsilon")
        ("eps_decay", po::value<double>(&eps_decay)->default_value(-1), "the decaying epsilon factor (default to linear)")
        ("rate_start", po::value<double>(&rate_start)->default_value(1.0), "the starting learning rate")
        ("rate_end", po::value<double>(&rate_end)->default_value(0.001), "the final learning rate")
        ("rate_decay", po::value<double>(&rate_decay)->default_value(10000), "the decaying factor")
        ("rate_decay_start_time", po::value<double>(&QLearning::RATE_DECAY_START_TIME)->default_value(43200), "the decaying factor")
        ("rate_decay_duration", po::value<double>(&QLearning::DURATION_RATE_DECAY)->default_value(43200), "the decaying factor")
        ("q_init", po::value<string>(&lb_init), "the q-value function initialization method")
        ("g_start", po::value<double>(&granularity_start)->default_value(oPWLCQ::GRANULARITY_START), "The granularity...")
        ("g_end", po::value<double>(&granularity_end)->default_value(oPWLCQ::GRANULARITY_END), "The granularity...")
        ;

        po::options_description byg_config("Bayesian game solver configuration");
        byg_config.add_options()
        ("player_id", po::value<number>(&batch_size), "the identifier of player");

        po::options_description visible("\nUsage:\tsdms-solve [CONFIGS]\n\tSDMStudio solve [CONFIGS]\n\nSolve a problem with specified algorithms and configurations.");
        visible.add(options).add(config).add(hsvi_config).add(pbvi_config).add(qlearning_config).add(byg_config);

        po::options_description config_file_options;
        config_file_options.add(config);

        po::variables_map vm;
        try
        {
            po::store(po::command_line_parser(argv, args).options(visible).run(), vm);
            po::notify(vm);
            if (vm.count("help"))
            {
                std::cout << visible << std::endl;
                return sdm::SUCCESS;
            }
            if (discount >= 1 && !horizon)
            {
                std::cerr << config::LOG_SDMS << "Invalid hyperparameters (discount must be less than 1. for infinite horizon)" << std::endl;
                return sdm::ERROR_IN_COMMAND_LINE;
            }
        }
        catch (po::error &e)
        {
            std::cerr << "ERROR: " << e.what() << std::endl;
            std::cerr << visible << std::endl;
            return sdm::ERROR_IN_COMMAND_LINE;
        }

        // Set the seed
        common::global_urng().seed(seed);

        // Set precisions
        Belief::PRECISION = p_b;
        OccupancyState::PRECISION = p_o;
        PrivateOccupancyState::PRECISION_COMPRESSION = p_c;
        oPWLCQ::GRANULARITY_START = granularity_start;
        oPWLCQ::GRANULARITY_END = granularity_end;

        common::logo();

        // Build algorithm
        algorithm = sdm::algo::make(algo_name,
                                    world,
                                    formalism,
                                    horizon,
                                    discount,
                                    error,
                                    trials,
                                    MAX_RUNNING_TIME,
                                    name,
                                    memory,
                                    (StateType)state_type,
                                    store_states,
                                    store_actions,
                                    batch_size,
                                    num_samples,
                                    type_sampling,
                                    rate_start,
                                    rate_end,
                                    rate_decay,
                                    eps_start,
                                    eps_end,
                                    eps_decay,
                                    lower_bound,
                                    lb_init,
                                    freq_update_lb,
                                    type_of_resolution_v1,
                                    freq_pruning_v1,
                                    type_of_pruning_v1,
                                    upper_bound,
                                    ub_init,
                                    freq_update_ub,
                                    type_of_resolution_v2,
                                    freq_pruning_v2,
                                    type_of_pruning_v2);

        // Initialize algorithm
        algorithm->initialize();

        // Solve the problem
        algorithm->solve();

        if (vm.count("test"))
        {
            algorithm->test();
        }
        if (vm.count("save"))
        {
            algorithm->save();
        }
    }
    catch (std::exception &e)
    {
        std::cerr << "Unhandled Exception reached the top of main: " << e.what() << std::endl;
        return sdm::ERROR_UNHANDLED_EXCEPTION;
    }

    return sdm::SUCCESS;
}

#ifndef __main_program__
#define __main_program__
int main(int argv, char **args)
{

    // return solve(argv, args);
    return solve(argv, args);
}
#endif


// Copyright Oleg Maximenko 2014.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// See http://github.com/svgpp/svgpp for library home page.

#pragma once

#include <svgpp/definitions.hpp>
#include <svgpp/parser/detail/common.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi_grammar.hpp>

namespace svgpp
{

namespace qi = boost::spirit::qi;

namespace detail
{
  struct length_units_symbols
  {
    enum type {em, ex, px, in, cm, mm, pt, pc};

    length_units_symbols()
    {
      symbols_.add("em", em);
      symbols_.add("ex", ex);
      symbols_.add("px", px);
      symbols_.add("in", in);
      symbols_.add("cm", cm);
      symbols_.add("mm", mm);
      symbols_.add("pt", pt);
      symbols_.add("pc", pc);
    }

    qi::symbols<char, type> const & symbols() const { return symbols_; }

    template<class LengthFactory, class Value>
    static typename LengthFactory::length_type create_length(LengthFactory const & length_factory, Value value, type units)
    {
      switch (units)
      {
      default:
        BOOST_ASSERT(false);
      case em:
        return length_factory.create_length(value, tag::length_units::em());
      case ex:
        return length_factory.create_length(value, tag::length_units::ex());
      case px:
        return length_factory.create_length(value, tag::length_units::px());
      case in:
        return length_factory.create_length(value, tag::length_units::in());
      case cm:
        return length_factory.create_length(value, tag::length_units::cm());
      case mm:
        return length_factory.create_length(value, tag::length_units::mm());
      case pt:
        return length_factory.create_length(value, tag::length_units::pt());
      case pc:
        return length_factory.create_length(value, tag::length_units::pc());
      }
    }

  private:
    qi::symbols<char, type> symbols_;
  };

  template<class LengthFactory, class Value>
  inline typename LengthFactory::length_type call_make_length_without_units(LengthFactory const & length_factory, Value value)
  {
    return length_factory.create_length(value, tag::length_units::none());
  }
}

template <
  class PropertySource,
  class Iterator,
  class LengthFactory,
  class PercentageDirectionTag = tag::length_dimension::not_width_nor_height,
  class Number = typename LengthFactory::number_type
>
class length_grammar;

template <typename Iterator, class LengthFactory, class PercentageDirectionTag, class Number>
class length_grammar<tag::source::attribute, Iterator, LengthFactory, PercentageDirectionTag, Number>:
  public qi::grammar<Iterator, typename LengthFactory::length_type(LengthFactory const &), qi::locals<Number> >
{
  typedef length_grammar<tag::source::attribute, Iterator, LengthFactory, PercentageDirectionTag, Number> this_type;
public:
  typedef typename LengthFactory::length_type length_type;

  length_grammar()
    : this_type::grammar(rule_)
  {
    namespace phx = boost::phoenix;
    using qi::_1;
    using qi::_a;
    using qi::_val;
    using qi::_r1;
    rule_
        =   number_ [_a = _1]
            >>  ( units_symbols_.symbols()
                      [_val = phx::bind(&detail::length_units_symbols::create_length<LengthFactory, Number>, _r1, _a, _1)]
                | qi::lit("%")
                      [_val = phx::bind(&length_grammar::call_make_length_percent, _r1, _a)]
                | qi::eps
                      [_val = phx::bind(&detail::call_make_length_without_units<LengthFactory, Number>, _r1, _a)]
                );
  }

private:
  typename this_type::start_type rule_;
  qi::real_parser<Number, detail::number_policies<Number, tag::source::attribute> > number_;
  detail::length_units_symbols units_symbols_;

  static length_type call_make_length_percent(LengthFactory const & length_factory, Number value)
  {
    return length_factory.create_length(value, tag::length_units::percent(), PercentageDirectionTag());
  }
};

template<
  class Iterator,
  class LengthFactory,
  class PercentageDirectionTag,
  class Number
>
class length_grammar<tag::source::css, Iterator, LengthFactory, PercentageDirectionTag, Number>:
  public qi::grammar<Iterator, typename LengthFactory::length_type(LengthFactory const &), qi::locals<Number> >
{
  typedef length_grammar<tag::source::css, Iterator, LengthFactory, PercentageDirectionTag, Number> this_type;
public:
  typedef typename LengthFactory::length_type length_type;

  length_grammar()
    : this_type::grammar(rule_)
  {
    namespace phx = boost::phoenix;
    using qi::_1;
    using qi::_a;
    using qi::_val;
    using qi::_r1;
    rule_
        =   number_ [_a = _1]
            >>  ( qi::no_case
                  [
                    units_symbols_.symbols()
                        [_val = phx::bind(&detail::length_units_symbols::create_length<LengthFactory, Number>, _r1, _a, _1)]
                  ]
                | qi::eps
                      [_val = phx::bind(&detail::call_make_length_without_units<LengthFactory, Number>, _r1, _a)]
                );
  }

private:
  typename this_type::start_type rule_;
  qi::real_parser<Number, detail::number_policies<Number, tag::source::css> > number_;
  detail::length_units_symbols units_symbols_;
};

template <
  class Iterator,
  class LengthFactory,
  class Number = typename LengthFactory::number_type
>
class percentage_or_length_css_grammar:
  public qi::grammar<Iterator, typename LengthFactory::length_type(LengthFactory const &), qi::locals<Number> >
{
  typedef percentage_or_length_css_grammar<Iterator, LengthFactory, Number> this_type;
public:
  typedef typename LengthFactory::length_type length_type;

  percentage_or_length_css_grammar()
    : this_type::grammar(rule_)
  {
    namespace phx = boost::phoenix;
    using qi::_1;
    using qi::_a;
    using qi::_val;
    using qi::_r1;
    rule_
        =   number_ [_a = _1]
            >>  ( qi::no_case
                  [
                    units_symbols_.symbols()
                        [_val = phx::bind(&detail::length_units_symbols::create_length<LengthFactory, Number>, _r1, _a, _1)]
                  ]
                | qi::lit("%")
                      [_val = phx::bind(&this_type::call_make_length_percent, _r1, _a)]
                | qi::eps
                      [_val = phx::bind(&detail::call_make_length_without_units<LengthFactory, Number>, _r1, _a)]
                );
  }

private:
  typename this_type::start_type rule_;
  qi::real_parser<Number, detail::number_policies<Number, tag::source::css> > number_;
  detail::length_units_symbols units_symbols_;

  static length_type call_make_length_percent(LengthFactory const & length_factory, Number value)
  {
    return length_factory.create_length(value, tag::length_units::percent(), tag::length_dimension::not_width_nor_height());
  }
};

}


// (C) Copyright Stephan Dollberg 2013. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef MEMCACHED_CONSISTENT_HASHER_HPP
#define MEMCACHED_CONSISTENT_HASHER_HPP

#include <boost/range/irange.hpp>
#include <map>

namespace memcachedcpp { namespace detail {

    template<typename hash>
    class consistent_hasher {
    public:
        template<typename server_iter>
        consistent_hasher(server_iter begin, server_iter end) {
            auto current_server_id = 0;
            for(auto&& server_name : boost::iterator_range<server_iter>(begin, end)) {
                for(int i : boost::irange(0, 256)) {
                    consistent_hash[hasher(server_name + std::to_string(i))] = current_server_id;
                }
                current_server_id++;
            }
        }

        std::size_t get_node_id(const std::string& key) const {
            auto current_hash = hasher(key);
            auto begin_iter = consistent_hash.upper_bound(current_hash);
            return begin_iter != consistent_hash.end() ? begin_iter->second : consistent_hash.begin()->second;
        }

    private:
        hash hasher;
        std::map<std::size_t, std::size_t> consistent_hash;
    };
}}

#endif // MEMCACHED_CLIENT_HPP


#include <cctbx/boost_python/flex_fwd.h>

#include <cctbx/xray/scatterer_utils.h>
#include <cctbx/crystal/direct_space_asu.h>
#include <cctbx/eltbx/henke.h>
#include <cctbx/eltbx/sasaki.h>
#include <boost/python/class.hpp>
#include <boost/python/def.hpp>
#include <boost/python/args.hpp>
#include <boost/python/return_value_policy.hpp>
#include <boost/python/return_by_value.hpp>

namespace cctbx { namespace xray { namespace boost_python {

namespace {

  struct scatterer_wrappers
  {
    typedef scatterer<> w_t;
    typedef w_t::float_type flt_t;

    static void
    wrap()
    {
      using namespace boost::python;
      typedef return_value_policy<return_by_value> rbv;
      typedef default_call_policies dcp;
      class_<w_t>("scatterer", no_init)
        .def(init<w_t const&>(arg("other")))
        .def(init<std::string const&,
                  fractional<flt_t> const&,
                  flt_t const&,
                  flt_t const&,
                  std::string const&,
                  flt_t const&,
                  flt_t const&>((
          arg("label"),
          arg("site"),
          arg("u_iso"),
          arg("occupancy"),
          arg("scattering_type"),
          arg("fp"),
          arg("fdp"))))
        .def(init<std::string const&,
                  fractional<flt_t> const&,
                  scitbx::sym_mat3<flt_t> const&,
                  flt_t const&,
                  std::string const&,
                  flt_t const&,
                  flt_t const&>((
          arg("label"),
          arg("site"),
          arg("u_star"),
          arg("occupancy"),
          arg("scattering_type"),
          arg("fp"),
          arg("fdp"))))
        .add_property("label", make_getter(&w_t::label, rbv()),
                               make_setter(&w_t::label, dcp()))
        .add_property("scattering_type",
          make_getter(&w_t::scattering_type, rbv()),
          make_setter(&w_t::scattering_type, dcp()))
        .add_property("fp", make_getter(&w_t::fp, rbv()),
                            make_setter(&w_t::fp, dcp()))
        .add_property("fdp", make_getter(&w_t::fdp, rbv()),
                             make_setter(&w_t::fdp, dcp()))
        .add_property("site", make_getter(&w_t::site, rbv()),
                              make_setter(&w_t::site, dcp()))
        .add_property("occupancy", make_getter(&w_t::occupancy, rbv()),
                                   make_setter(&w_t::occupancy, dcp()))
        .add_property("u_iso", make_getter(&w_t::u_iso, rbv()),
                               make_setter(&w_t::u_iso, dcp()))
        .add_property("u_star", make_getter(&w_t::u_star, rbv()),
                                make_setter(&w_t::u_star, dcp()))
        .def_readwrite("flags", &w_t::flags)
        .def("set_use_u", &w_t::set_use_u, (arg("iso"),arg("aniso")))
        .def("set_use_u_iso_only", &w_t::set_use_u_iso_only)
        .def("set_use_u_aniso_only", &w_t::set_use_u_aniso_only)
        .def("convert_to_isotropic", &w_t::convert_to_isotropic, (
          arg("unit_cell")))
        .def("convert_to_anisotropic", &w_t::convert_to_anisotropic, (
          arg("unit_cell")))
        .def("is_positive_definite_u",
          (bool(w_t::*)(uctbx::unit_cell const&) const)
            &w_t::is_positive_definite_u, (
          arg("unit_cell")))
        .def("is_positive_definite_u",
          (bool(w_t::*)(uctbx::unit_cell const&, double const&) const)
            &w_t::is_positive_definite_u, (
          arg("unit_cell"),
          arg("u_cart_tolerance")))
        .def("u_iso_or_equiv", &w_t::u_iso_or_equiv, (arg("unit_cell")))
        .def("b_iso", &w_t::b_iso)
        .def("u_cart_plus_u_iso", &w_t::u_cart_plus_u_iso, (arg("unit_cell")))
        .def("tidy_u",
          (void(w_t::*)(
            uctbx::unit_cell const&,
            sgtbx::site_symmetry_ops const&,
            double const&,
            double const&,
            double const&)) &w_t::tidy_u, (
          arg("unit_cell"),
          arg("site_symmetry_ops"),
          arg("u_min"),
          arg("u_max"),
          arg("anisotropy_min")))
        .def("shift_u",
          (void(w_t::*)(
            uctbx::unit_cell const&,
            double const&)) &w_t::shift_u, (
          arg("unit_cell"),
          arg("u_shift")))
        .def("shift_occupancy",
          (void(w_t::*)(
            double const&)) &w_t::shift_occupancy, (
          arg("q_shift")))
        .def("apply_symmetry",
          (sgtbx::site_symmetry(w_t::*)(
            uctbx::unit_cell const&,
            sgtbx::space_group const&,
            double const&,
            double const&,
            bool)) &w_t::apply_symmetry, (
              arg("unit_cell"),
              arg("space_group"),
              arg("min_distance_sym_equiv")=0.5,
              arg("u_star_tolerance")=0,
              arg("assert_min_distance_sym_equiv")=true))
        .def("apply_symmetry",
          (void(w_t::*)(
            sgtbx::site_symmetry_ops const&,
            double const&)) &w_t::apply_symmetry, (
              arg("site_symmetry_ops"),
              arg("u_star_tolerance")=0))
        .def("apply_symmetry_site", &w_t::apply_symmetry_site, (
          arg("site_symmetry_ops")))
        .def("apply_symmetry_u_star", &w_t::apply_symmetry_u_star, (
          arg("site_symmetry_ops"),
          arg("u_star_tolerance")=0))
        .def("multiplicity", &w_t::multiplicity)
        .def("weight_without_occupancy", &w_t::weight_without_occupancy)
        .def("weight", &w_t::weight)
        .def("report_details", &w_t::report_details, (
          arg("unit_cell"),
          arg("prefix")))
      ;
    }
  };

} // namespace <anoymous>

  void wrap_scatterer()
  {
    using namespace boost::python;

    scatterer_wrappers::wrap();

    def("is_positive_definite_u",
      (af::shared<bool>(*)(
        af::const_ref<scatterer<> > const&,
        uctbx::unit_cell const&)) is_positive_definite_u, (
          arg("scatterers"),
          arg("unit_cell")));

    def("is_positive_definite_u",
      (af::shared<bool>(*)(
        af::const_ref<scatterer<> > const&,
        uctbx::unit_cell const&,
        double)) is_positive_definite_u, (
          arg("scatterers"),
          arg("unit_cell"),
          arg("u_cart_tolerance")));

    def("tidy_us",
      (void(*)(
        af::ref<scatterer<> > const&,
        uctbx::unit_cell const&,
        sgtbx::site_symmetry_table const&,
        double u_min,
        double u_max,
        double anisotropy_min)) tidy_us, (
          arg("scatterers"),
          arg("unit_cell"),
          arg("site_symmetry_table"),
          arg("u_min"),
          arg("u_max"),
          arg("anisotropy_min")));

    def("u_star_plus_u_iso",
      (void(*)(
        af::ref<scatterer<> > const&,
        uctbx::unit_cell const&)) u_star_plus_u_iso, (
          arg("scatterers"),
          arg("unit_cell")));

    def("shift_us",
      (void(*)(
        af::ref<scatterer<> > const&,
        uctbx::unit_cell const&,
        double u_min)) shift_us, (
          arg("scatterers"),
          arg("unit_cell"),
          arg("u_shift")));

    def("shift_us",
      (void(*)(
        af::ref<scatterer<> > const&,
        uctbx::unit_cell const&,
        double u_min,
        af::const_ref<std::size_t> const&)) shift_us, (
          arg("scatterers"),
          arg("unit_cell"),
          arg("u_shift"),
          arg("selection")));

    def("shift_occupancies",
      (void(*)(
        af::ref<scatterer<> > const&,
        double,
        af::const_ref<std::size_t> const&)) shift_occupancies, (
          arg("scatterers"),
          arg("q_shift"),
          arg("selection")));

    def("shift_occupancies",
      (void(*)(
        af::ref<scatterer<> > const&,
        double)) shift_occupancies, (
          arg("scatterers"),
          arg("q_shift")));

    def("apply_symmetry_sites",
      (void(*)(
        sgtbx::site_symmetry_table const&,
        af::ref<scatterer<> > const&)) apply_symmetry_sites, (
          arg("site_symmetry_table"),
          arg("scatterers")));

    def("apply_symmetry_u_stars",
      (void(*)(
        sgtbx::site_symmetry_table const&,
        af::ref<scatterer<> > const&,
        double)) apply_symmetry_u_stars, (
          arg("site_symmetry_table"),
          arg("scatterers"),
          arg("u_star_tolerance")=0));

    def("add_scatterers_ext",
      (void(*)(
        uctbx::unit_cell const&,
        sgtbx::space_group const&,
        af::ref<scatterer<> > const&,
        sgtbx::site_symmetry_table&,
        sgtbx::site_symmetry_table const&,
        double,
        double,
        bool,
        bool)) add_scatterers_ext, (
          arg("unit_cell"),
          arg("space_group"),
          arg("scatterers"),
          arg("site_symmetry_table"),
          arg("site_symmetry_table_for_new"),
          arg("min_distance_sym_equiv"),
          arg("u_star_tolerance"),
          arg("assert_min_distance_sym_equiv"),
          arg("non_unit_occupancy_implies_min_distance_sym_equiv_zero")));

    def("change_basis",
      (af::shared<scatterer<> >(*)(
        af::const_ref<scatterer<> > const&,
        sgtbx::change_of_basis_op const&)) change_basis, (
      arg("scatterers"), arg("cb_op")));

    def("expand_to_p1",
      (af::shared<scatterer<> >(*)(
        uctbx::unit_cell const&,
        sgtbx::space_group const&,
        af::const_ref<scatterer<> > const&,
        sgtbx::site_symmetry_table const&,
        bool)) expand_to_p1, (
          arg("unit_cell"),
          arg("space_group"),
          arg("scatterers"),
          arg("site_symmetry_table"),
          arg("append_number_to_labels")));

    def("n_undefined_multiplicities",
      (std::size_t(*)(
        af::const_ref<scatterer<> > const&)) n_undefined_multiplicities, (
      arg("scatterers")));

    def("asu_mappings_process",
      (void(*)(
        crystal::direct_space_asu::asu_mappings<>&,
        af::const_ref<scatterer<> > const&,
        sgtbx::site_symmetry_table const&)) asu_mappings_process, (
      arg("asu_mappings"), arg("scatterers"), arg("site_symmetry_table")));

    def("rotate",
      (af::shared<scatterer<> >(*)(
        uctbx::unit_cell const&,
        scitbx::mat3<double> const&,
        af::const_ref<scatterer<> > const&)) rotate, (
          arg("unit_cell"),
          arg("rotation_matrix"),
          arg("scatterers")));

    typedef return_value_policy<return_by_value> rbv;
    class_<apply_rigid_body_shift<> >("apply_rigid_body_shift")
      .def(init<af::shared<scitbx::vec3<double> > const&,
                af::shared<scitbx::vec3<double> > const&,
                scitbx::mat3<double> const&,
                scitbx::vec3<double> const&,
                af::const_ref<double> const&,
                uctbx::unit_cell const&,
                af::const_ref<std::size_t> const& >((arg("sites_cart"),
                                              arg("sites_frac"),
                                              arg("rot"),
                                              arg("trans"),
                                              arg("atomic_weights"),
                                              arg("unit_cell"),
                                              arg("selection"))))
      .add_property("sites_frac",  make_getter(
                             &apply_rigid_body_shift<>::sites_frac, rbv()))
      .add_property("sites_cart",  make_getter(
                             &apply_rigid_body_shift<>::sites_cart, rbv()))
      .add_property("center_of_mass",  make_getter(
                             &apply_rigid_body_shift<>::center_of_mass, rbv()))
    ;

    def("set_inelastic_form_factors_from_henke",
      (void(*)(af::ref<scatterer<> > const &,
               eltbx::wavelengths::characteristic,
               bool))
        inelastic_form_factors<eltbx::henke::table>::set,
        (arg("scatterers"), arg("photon"), arg("set_use_fp_fdp")=true));
    def("set_inelastic_form_factors_from_sasaki",
      (void(*)(af::ref<scatterer<> > const &,
               eltbx::wavelengths::characteristic,
               bool))
        inelastic_form_factors<eltbx::sasaki::table>::set,
        (arg("scatterers"), arg("photon"), arg("set_use_fp_fdp")=true));
    def("set_inelastic_form_factors_from_henke",
      (void(*)(af::ref<scatterer<> > const &, float, bool))
        inelastic_form_factors<eltbx::henke::table>::set,
        (arg("scatterers"), arg("wavelength"), arg("set_use_fp_fdp")=true));
    def("set_inelastic_form_factors_from_sasaki",
      (void(*)(af::ref<scatterer<> > const &, float, bool))
        inelastic_form_factors<eltbx::sasaki::table>::set,
        (arg("scatterers"), arg("wavelength"), arg("set_use_fp_fdp")=true));
  }

}}} // namespace cctbx::xray::boost_python


#include "worldengine/images/ancient_map_image.h"
#include "../basic.h"

#include <random>

#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
#endif

#include <boost/log/trivial.hpp>
#include <boost/multiprecision/integer.hpp>
#include <boost/random.hpp>

#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif

namespace bm = boost::multiprecision;

namespace WorldEngine
{
typedef std::function<void(
   boost::gil::rgb8_image_t::view_t& target, uint32_t x, uint32_t y)>
   DrawFunction;

static const uint32_t NUM_COLOR_CHANNELS = 3;

static const boost::gil::rgb8_pixel_t LAND_COLOR =
   boost::gil::rgb8_pixel_t(181, 166, 127);

static void CreateBiomeGroupMasks(
   const World&                                                 world,
   std::unordered_map<BiomeGroup, boost::multi_array<bool, 2>>& biomeMasks,
   uint32_t                                                     scale);
static void CreateMountainMask(const World&                  world,
                               boost::multi_array<float, 2>& mountainMask,
                               uint32_t                      scale);

static void DrawAMountain(boost::gil::rgb8_image_t::view_t& target,
                          int32_t                           x,
                          int32_t                           y,
                          float                             w,
                          int32_t                           h);
static void DrawDesertPattern(boost::gil::rgb8_image_t::view_t& target,
                              int32_t                           x,
                              int32_t                           y,
                              boost::gil::rgb8_pixel_t          c);
static void DrawForestPattern1(boost::gil::rgb8_image_t::view_t& target,
                               int32_t                           x,
                               int32_t                           y,
                               boost::gil::rgb8_pixel_t          c1,
                               boost::gil::rgb8_pixel_t          c2);
static void DrawForestPattern2(boost::gil::rgb8_image_t::view_t& target,
                               int32_t                           x,
                               int32_t                           y,
                               boost::gil::rgb8_pixel_t          c1,
                               boost::gil::rgb8_pixel_t          c2);

static void DrawPixelCheck(boost::gil::rgb8_image_t::view_t& target,
                           int32_t                           x,
                           int32_t                           y,
                           boost::gil::rgb8_pixel_t          c);
static void DrawShadedPixel(boost::gil::rgb8_image_t::view_t& target,
                            uint32_t                          x,
                            uint32_t                          y,
                            uint8_t                           r,
                            uint8_t                           g,
                            uint8_t                           b);
static void DrawBorealForest(boost::gil::rgb8_image_t::view_t& target,
                             uint32_t                          x,
                             uint32_t                          y);
static void
DrawChaparral(boost::gil::rgb8_image_t::view_t& target, uint32_t x, uint32_t y);
static void DrawCoolDesert(boost::gil::rgb8_image_t::view_t& target,
                           uint32_t                          x,
                           uint32_t                          y);
static void DrawColdParklands(boost::gil::rgb8_image_t::view_t& target,
                              uint32_t                          x,
                              uint32_t                          y);
static void
DrawGlacier(boost::gil::rgb8_image_t::view_t& target, uint32_t x, uint32_t y);
static void
DrawHotDesert(boost::gil::rgb8_image_t::view_t& target, uint32_t x, uint32_t y);
static void
DrawJungle(boost::gil::rgb8_image_t::view_t& target, uint32_t x, uint32_t y);
static void
DrawSavanna(boost::gil::rgb8_image_t::view_t& target, uint32_t x, uint32_t y);
static void
DrawSteppe(boost::gil::rgb8_image_t::view_t& target, uint32_t x, uint32_t y);
static void DrawTemperateForest1(boost::gil::rgb8_image_t::view_t& target,
                                 uint32_t                          x,
                                 uint32_t                          y);
static void DrawTemperateForest2(boost::gil::rgb8_image_t::view_t& target,
                                 uint32_t                          x,
                                 uint32_t                          y);
static void DrawTropicalDryForest(boost::gil::rgb8_image_t::view_t& target,
                                  uint32_t                          x,
                                  uint32_t                          y);
static void
DrawTundra(boost::gil::rgb8_image_t::view_t& target, uint32_t x, uint32_t y);
static void DrawWarmTemperateForest(boost::gil::rgb8_image_t::view_t& target,
                                    uint32_t                          x,
                                    uint32_t                          y);
template<typename T>
static void ScaleArray(const boost::multi_array<T, 2>& input,
                       boost::multi_array<T, 2>&       output,
                       uint32_t                        scale);

AncientMapImage::AncientMapImage(const World& world,
                                 uint32_t     seed,
                                 uint32_t     scale,
                                 SeaColor     seaColor,
                                 bool         drawBiome,
                                 bool         drawRivers,
                                 bool         drawMountains,
                                 bool         drawOuterLandBorder) :
    Image(world, scale),
    seed_(seed),
    seaColor_(seaColor),
    drawBiome_(drawBiome),
    drawRivers_(drawRivers),
    drawMountains_(drawMountains),
    drawOuterLandBorder_(drawOuterLandBorder)
{
}

AncientMapImage::~AncientMapImage() {}

void AncientMapImage::DrawImage(boost::gil::rgb8_image_t::view_t& target)
{
   BOOST_LOG_TRIVIAL(debug) << "Ancient map: Initializing";

   std::mt19937 generator(seed_);

   const int32_t width   = world_.width();
   const int32_t height  = world_.height();
   const int32_t sWidth  = width * scale_;
   const int32_t sHeight = height * scale_;

   const boost::gil::rgb8_pixel_t seaColor =
      (seaColor_ == SeaColor::Blue) ? boost::gil::rgb8_pixel_t(142, 162, 179) :
                                      boost::gil::rgb8_pixel_t(212, 198, 169);

   OceanArrayType scaledOcean;
   ScaleArray(world_.GetOceanData(), scaledOcean, scale_);
   boost::multi_array<uint32_t, 2> neighbors = CountNeighbors(scaledOcean);

   boost::multi_array<bool, 2> borders(boost::extents[sHeight][sWidth]);
   std::transform(scaledOcean.data(),
                  scaledOcean.data() + scaledOcean.num_elements(),
                  neighbors.data(),
                  borders.data(),
                  [](const bool& ocean, const uint32_t& neighbors) -> bool {
                     return (ocean || neighbors == 0) ? false : true;
                  });

   // Cache neighbors count at different radii
   std::unordered_map<int32_t, boost::multi_array<int32_t, 2>> borderNeighbors;
   borderNeighbors[6].resize(boost::extents[sHeight][sWidth]);
   borderNeighbors[9].resize(boost::extents[sHeight][sWidth]);
   borderNeighbors[6] = CountNeighbors(borders, 6);
   borderNeighbors[9] = CountNeighbors(borders, 9);

   boost::multi_array<bool, 2> outerBorders;
   if (drawOuterLandBorder_)
   {
      outerBorders.resize(boost::extents[sHeight][sWidth]);

      auto GenerateOuterBorders =
         [&sWidth, &sHeight, &scaledOcean = std::as_const(scaledOcean)](
            const boost::multi_array<bool, 2>& innerBorders,
            boost::multi_array<bool, 2>&       outerBorders) {
            boost::multi_array<uint32_t, 2> neighbors =
               CountNeighbors(innerBorders);

            for (int32_t y = 0; y < sHeight; y++)
            {
               for (int32_t x = 0; x < sWidth; x++)
               {
                  outerBorders[y][x] = !innerBorders[y][x] &&
                                       scaledOcean[y][x] && neighbors[y][x] > 0;
               }
            }
         };

      GenerateOuterBorders(borders, outerBorders);
      GenerateOuterBorders(outerBorders, outerBorders);
   }

   boost::multi_array<float, 2> mountainMask;
   if (drawMountains_)
   {
      CreateMountainMask(world_, mountainMask, scale_);
   }

   std::unordered_map<BiomeGroup, boost::multi_array<bool, 2>> biomeMasks;

   std::function<void(BiomeGroup, DrawFunction, int32_t, DrawFunction)>
      DrawBiome;
   if (drawBiome_)
   {
      CreateBiomeGroupMasks(world_, biomeMasks, scale_);

      DrawBiome = [this,
                   &biomeMasks,
                   &generator,
                   &target,
                   &borderNeighbors = std::as_const(borderNeighbors),
                   &borders = std::as_const(borders)](BiomeGroup   group,
                                                      DrawFunction Draw,
                                                      int32_t      r,
                                                      DrawFunction AltDraw) {
         BOOST_LOG_TRIVIAL(debug)
            << "Ancient map: Drawing biome group " << group;

         boost::random::uniform_real_distribution<float> random(0.0f, 1.0f);

         const int32_t width   = world_.width();
         const int32_t height  = world_.height();
         const int32_t sWidth  = width * scale_;
         const int32_t sHeight = height * scale_;

         for (int32_t sy = 0; sy < sHeight; sy++)
         {
            for (int32_t sx = 0; sx < sWidth; sx++)
            {
               if (biomeMasks.at(group)[sy][sx])
               {
                  if (group == BiomeGroup::Iceland)
                  {
                     if (!borders[sy][sx])
                     {
                        Draw(target, sx, sy);
                     }
                  }
                  else
                  {
                     if (r == 0 || borderNeighbors.at(r)[sy][sx] <= 2)
                     {
                        if (AltDraw != nullptr && random(generator) >= 0.5f)
                        {
                           AltDraw(target, sx, sy);
                        }
                        else
                        {
                           Draw(target, sx, sy);
                        }

                        for (int32_t dy = -r; dy <= r; dy++)
                        {
                           const int32_t yp = sy + dy;
                           for (int32_t dx = -r; dx <= r; dx++)
                           {
                              const int32_t xp = sx + dx;
                              if (0 <= yp && yp < sHeight && 0 <= xp &&
                                  xp < sWidth)
                              {
                                 biomeMasks.at(group)[yp][xp] = false;
                              }
                           }
                        }
                     }
                  }
               }
            }
         }
      };
   }

   BOOST_LOG_TRIVIAL(debug) << "Ancient map: Coloring oceans and borders";

   static const boost::gil::rgb8_pixel_t borderColor(0, 0, 0);
   const boost::gil::rgb8_pixel_t        outerBorderColor =
      Gradient(0.5f, 0.0f, 1.0f, borderColor, seaColor);

   std::vector<boost::multi_array<float, 2>> channels;
   for (uint32_t c = 0; c < NUM_COLOR_CHANNELS; c++)
   {
      channels.push_back(
         boost::multi_array<float, 2>(boost::extents[sHeight][sWidth]));
   }

   for (int32_t y = 0; y < sHeight; y++)
   {
      for (int32_t x = 0; x < sWidth; x++)
      {
         boost::gil::rgb8_pixel_t color;

         if (borders[y][x])
         {
            color = borderColor;
         }
         else if (drawOuterLandBorder_ && outerBorders[y][x])
         {
            color = outerBorderColor;
         }
         else if (scaledOcean[y][x])
         {
            color = seaColor;
         }
         else
         {
            color = LAND_COLOR;
         }

         for (uint32_t c = 0; c < NUM_COLOR_CHANNELS; c++)
         {
            channels[c][y][x] = color[c];
         }
      }
   }

   BOOST_LOG_TRIVIAL(debug) << "Ancient map: Anti-aliasing image";
   for (uint32_t c = 0; c < NUM_COLOR_CHANNELS; c++)
   {
      AntiAlias(channels[c]);
   }

   for (int32_t y = 0; y < sHeight; y++)
   {
      for (int32_t x = 0; x < sWidth; x++)
      {
         target(x, y) =
            boost::gil::rgb8_pixel_t(static_cast<uint8_t>(channels[0][y][x]),
                                     static_cast<uint8_t>(channels[1][y][x]),
                                     static_cast<uint8_t>(channels[2][y][x]));
      }
   }

   if (drawBiome_)
   {
      DrawBiome(BiomeGroup::Iceland, DrawGlacier, 0, nullptr);
      DrawBiome(BiomeGroup::Tundra, DrawTundra, 0, nullptr);
      DrawBiome(BiomeGroup::ColdParklands, DrawColdParklands, 0, nullptr);
      DrawBiome(BiomeGroup::Steppe, DrawSteppe, 0, nullptr);
      DrawBiome(BiomeGroup::Chaparral, DrawChaparral, 0, nullptr);
      DrawBiome(BiomeGroup::Savanna, DrawSavanna, 0, nullptr);
      DrawBiome(BiomeGroup::CoolDesert, DrawCoolDesert, 9, nullptr);
      DrawBiome(BiomeGroup::HotDesert, DrawHotDesert, 9, nullptr);
      DrawBiome(BiomeGroup::BorealForest, DrawBorealForest, 6, nullptr);
      DrawBiome(BiomeGroup::CoolTemperateForest,
                DrawTemperateForest1,
                6,
                DrawTemperateForest2);
      DrawBiome(
         BiomeGroup::WarmTemperateForest, DrawWarmTemperateForest, 6, nullptr);
      DrawBiome(
         BiomeGroup::TropicalDryForest, DrawTropicalDryForest, 6, nullptr);
      DrawBiome(BiomeGroup::Jungle, DrawJungle, 6, nullptr);
   }

   if (drawRivers_)
   {
      BOOST_LOG_TRIVIAL(debug) << "Ancient map: Drawing rivers";
      DrawRivers(target);
   }

   if (drawMountains_)
   {
      BOOST_LOG_TRIVIAL(debug) << "Ancient map: Drawing mountains";

      for (int32_t sy = 0; sy < sHeight; sy++)
      {
         int32_t y = sy / scale_;
         for (int32_t sx = 0; sx < sWidth; sx++)
         {
            int32_t x = sx / scale_;

            float w = mountainMask[sy][sx];
            if (w > 0.0f)
            {
               int32_t h =
                  static_cast<int32_t>(3u + world_.GetLevelOfMountain(x, y));
               int32_t r =
                  std::max<int32_t>(static_cast<int32_t>(w * 2 / 3), h);

               if (borderNeighbors.find(r) == borderNeighbors.end())
               {
                  borderNeighbors[r].resize(boost::extents[sHeight][sWidth]);
                  borderNeighbors[r] = CountNeighbors(borders, r);
               }

               if (borderNeighbors[r][sy][sx] <= 2)
               {
                  DrawAMountain(target, sx, sy, w, h);

                  for (int32_t dy = -r; dy <= r; dy++)
                  {
                     const int32_t yp = sy + dy;
                     for (int32_t dx = -r; dx <= r; dx++)
                     {
                        const int32_t xp = sx + dx;
                        if (0 <= yp && yp < sHeight && 0 <= xp && xp < sWidth)
                        {
                           mountainMask[yp][xp] = 0.0;
                        }
                     }
                  }
               }
            }
         }
      }
   }

   BOOST_LOG_TRIVIAL(debug) << "Ancient map: Complete";
}

static void CreateBiomeGroupMasks(
   const World&                                                 world,
   std::unordered_map<BiomeGroup, boost::multi_array<bool, 2>>& masks,
   uint32_t                                                     scale)
{
   const uint32_t width  = world.width();
   const uint32_t height = world.height();

   for (BiomeGroup group : BiomeGroupIterator())
   {
      boost::multi_array<bool, 2>& mask = masks[group];
      mask.resize(boost::extents[height][width]);
      std::fill(mask.data(), mask.data() + mask.num_elements(), false);

      for (uint32_t y = 0; y < height; y++)
      {
         for (uint32_t x = 0; x < width; x++)
         {
            if (group == world.GetBiomeGroup(x, y))
            {
               mask[y][x] = true;
            }
         }
      }

      const boost::multi_array<uint32_t, 2> neighbors = CountNeighbors(mask);

      std::transform(
         mask.data(),
         mask.data() + mask.num_elements(),
         neighbors.data(),
         mask.data(),
         [&group = std::as_const(group)](const bool&     mask,
                                         const uint32_t& neighbors) -> bool {
            return (mask && (neighbors > 5 || group == BiomeGroup::Iceland));
         });

      ScaleArray(mask, mask, scale);
   }
}

static void CreateMountainMask(const World&                  world,
                               boost::multi_array<float, 2>& mask,
                               uint32_t                      scale)
{
   const uint32_t width  = world.width();
   const uint32_t height = world.height();

   mask.resize(boost::extents[height][width]);
   std::fill(mask.data(), mask.data() + mask.num_elements(), 0.0f);

   for (uint32_t y = 0; y < height; y++)
   {
      for (uint32_t x = 0; x < width; x++)
      {
         if (world.IsMountain(x, y))
         {
            mask[y][x] = 1.0f;
         }
      }
   }

   const boost::multi_array<uint32_t, 2> neighbors = CountNeighbors(mask, 3);

   std::transform(mask.data(),
                  mask.data() + mask.num_elements(),
                  neighbors.data(),
                  mask.data(),
                  [](const float& mask, const uint32_t& neighbors) -> float {
                     if (mask > 0.0f && neighbors > 32)
                     {
                        return neighbors / 4.0f;
                     }
                     else
                     {
                        return 0.0f;
                     }
                  });

   ScaleArray(mask, mask, scale);
}

static void DrawAMountain(boost::gil::rgb8_image_t::view_t& target,
                          int32_t                           x,
                          int32_t                           y,
                          float,
                          int32_t h)
{
   const boost::gil::rgb8_pixel_t mcr(75, 75, 75);

   // Left edge
   for (int32_t mody = -h; mody <= h; mody++)
   {
      const float   bottomness = (mody + h) / 2.0f;
      const int32_t leftBorder = static_cast<int32_t>(bottomness);
      const int32_t darkArea   = static_cast<int32_t>(bottomness / 2.0f);
      const int32_t lightArea  = darkArea;

      for (int32_t itx = darkArea; itx <= leftBorder; itx++)
      {
         DrawPixelCheck(target,
                        x - itx,
                        y + mody,
                        Gradient(static_cast<float>(itx),
                                 static_cast<float>(darkArea),
                                 static_cast<float>(leftBorder),
                                 boost::gil::rgb8_pixel_t(0, 0, 0),
                                 boost::gil::rgb8_pixel_t(64, 64, 64)));
      }
      for (int32_t itx = -darkArea; itx <= lightArea; itx++)
      {
         DrawPixelCheck(target,
                        x + itx,
                        y + mody,
                        Gradient(static_cast<float>(itx),
                                 static_cast<float>(-darkArea),
                                 static_cast<float>(lightArea),
                                 boost::gil::rgb8_pixel_t(64, 64, 64),
                                 boost::gil::rgb8_pixel_t(128, 128, 128)));
      }
      for (int32_t itx = lightArea; itx < leftBorder; itx++)
      {
         DrawPixelCheck(target, x + itx, y + mody, LAND_COLOR);
      }
   }

   // Right edge
   for (int32_t mody = -h; mody <= h; mody++)
   {
      float    bottomness = (mody + h) / 2.0f;
      uint32_t modx       = static_cast<uint32_t>(bottomness);
      DrawPixelCheck(target, x + modx, y + mody, mcr);
   }
}

static void DrawDesertPattern(boost::gil::rgb8_image_t::view_t& target,
                              int32_t                           x,
                              int32_t                           y,
                              boost::gil::rgb8_pixel_t          c)
{
   static const std::vector<Point> points = {
      {-1, -2}, {0, -2}, {1, -2}, {2, -2}, {-2, -1}, {-1, -1}, {0, -1},
      {4, -1},  {-4, 0}, {-3, 0}, {-2, 0}, {-1, 0},  {1, 0},   {2, 0},
      {6, 0},   {-5, 1}, {0, 1},  {7, 1},  {8, 1},   {-8, 2},  {-7, 2}};

   for (Point p : points)
   {
      DrawPixelCheck(target, x + p.first, y + p.second, c);
   }
}

static void DrawForestPattern1(boost::gil::rgb8_image_t::view_t& target,
                               int32_t                           x,
                               int32_t                           y,
                               boost::gil::rgb8_pixel_t          c1,
                               boost::gil::rgb8_pixel_t          c2)
{
   static const std::vector<Point> c1Points = {
      {0, -4}, {0, -3}, {-1, -2}, {1, -2}, {-1, -1}, {1, -1}, {-2, 0}, {1, 0},
      {2, 0},  {-2, 1}, {2, 1},   {-3, 2}, {-1, 2},  {3, 2},  {-3, 3}, {-2, 3},
      {-1, 3}, {0, 3},  {1, 3},   {2, 3},  {3, 3},   {0, 4}};
   static const std::vector<Point> c2Points = {{0, -2},
                                               {0, -1},
                                               {-1, 0},
                                               {0, 0},
                                               {-1, 1},
                                               {0, 1},
                                               {1, 1},
                                               {-2, 2},
                                               {0, 2},
                                               {1, 2},
                                               {2, 2}};

   for (Point p : c1Points)
   {
      DrawPixelCheck(target, x + p.first, y + p.second, c1);
   }
   for (Point p : c2Points)
   {
      DrawPixelCheck(target, x + p.first, y + p.second, c2);
   }
}

static void DrawForestPattern2(boost::gil::rgb8_image_t::view_t& target,
                               int32_t                           x,
                               int32_t                           y,
                               boost::gil::rgb8_pixel_t          c1,
                               boost::gil::rgb8_pixel_t          c2)
{
   static const std::vector<Point> c1Points = {
      {-1, -4}, {0, -4}, {1, -4}, {-2, -3}, {-1, -3}, {2, -3},
      {-2, -2}, {1, -2}, {2, -2}, {-2, -1}, {2, -1},  {-2, 0},
      {-1, 0},  {2, 0},  {-2, 1}, {1, 1},   {2, 1},   {-1, 2},
      {0, 2},   {1, 2},  {0, 3},  {0, 4}};
   static const std::vector<Point> c2Points = {{0, -3},
                                               {1, -3},
                                               {-1, -2},
                                               {0, -2},
                                               {-1, -1},
                                               {0, -1},
                                               {1, -1},
                                               {0, 0},
                                               {1, 0},
                                               {-1, 1},
                                               {0, 1}};

   for (Point p : c1Points)
   {
      DrawPixelCheck(target, x + p.first, y + p.second, c1);
   }
   for (Point p : c2Points)
   {
      DrawPixelCheck(target, x + p.first, y + p.second, c2);
   }
}

static void DrawPixelCheck(boost::gil::rgb8_image_t::view_t& target,
                           int32_t                           x,
                           int32_t                           y,
                           boost::gil::rgb8_pixel_t          c)
{
   if (0 <= x && x < target.width() && 0 <= y && y < target.height())
   {
      target(x, y) = c;
   }
}

static void DrawShadedPixel(boost::gil::rgb8_image_t::view_t& target,
                            uint32_t                          x,
                            uint32_t                          y,
                            uint8_t                           r,
                            uint8_t                           g,
                            uint8_t                           b)
{
   const uint8_t db =
      (bm::powm(x, y / 5, 75) + x * 23 + y * 37 + (x * y) * 13) % 75;
   const uint8_t nr = r - db;
   const uint8_t ng = g - db;
   const uint8_t nb = b - db;

   target(x, y) = boost::gil::rgb8_pixel_t(nr, ng, nb);
}

static void DrawBorealForest(boost::gil::rgb8_image_t::view_t& target,
                             uint32_t                          x,
                             uint32_t                          y)
{
   const boost::gil::rgb8_pixel_t c1(0, 32, 0);
   const boost::gil::rgb8_pixel_t c2(0, 64, 0);
   DrawForestPattern1(target, x, y, c1, c2);
}

static void
DrawChaparral(boost::gil::rgb8_image_t::view_t& target, uint32_t x, uint32_t y)
{
   DrawShadedPixel(target, x, y, 180, 171, 113);
}

static void
DrawCoolDesert(boost::gil::rgb8_image_t::view_t& target, uint32_t x, uint32_t y)
{
   const boost::gil::rgb8_pixel_t c(72, 72, 53);
   DrawDesertPattern(target, x, y, c);
}

static void DrawColdParklands(boost::gil::rgb8_image_t::view_t& target,
                              uint32_t                          x,
                              uint32_t                          y)
{
   const uint8_t db =
      (bm::powm(x, y / 5, 75) + x * 23 + y * 37 + (x * y) * 13) % 75;
   const uint8_t r = 105 - db;
   const uint8_t g = 96 - db;
   const uint8_t b = 38 - db / 2;
   target(x, y)    = boost::gil::rgb8_pixel_t(r, g, b);
}

static void
DrawGlacier(boost::gil::rgb8_image_t::view_t& target, uint32_t x, uint32_t y)
{
   const uint8_t rg =
      255 - (bm::powm(x, y / 5, 75) + x * 23 + y * 37 + (x * y) * 13) % 75;
   target(x, y) = boost::gil::rgb8_pixel_t(rg, rg, 255);
}

static void
DrawHotDesert(boost::gil::rgb8_image_t::view_t& target, uint32_t x, uint32_t y)
{
   const boost::gil::rgb8_pixel_t c(72, 72, 53);
   DrawDesertPattern(target, x, y, c);
}

static void
DrawJungle(boost::gil::rgb8_image_t::view_t& target, uint32_t x, uint32_t y)
{
   const boost::gil::rgb8_pixel_t c1(0, 128, 0);
   const boost::gil::rgb8_pixel_t c2(0, 255, 0);
   DrawForestPattern2(target, x, y, c1, c2);
}

static void
DrawSavanna(boost::gil::rgb8_image_t::view_t& target, uint32_t x, uint32_t y)
{
   DrawShadedPixel(target, x, y, 255, 246, 188);
}

static void
DrawSteppe(boost::gil::rgb8_image_t::view_t& target, uint32_t x, uint32_t y)
{
   DrawShadedPixel(target, x, y, 96, 192, 96);
}

static void DrawTemperateForest1(boost::gil::rgb8_image_t::view_t& target,
                                 uint32_t                          x,
                                 uint32_t                          y)
{
   const boost::gil::rgb8_pixel_t c1(0, 64, 0);
   const boost::gil::rgb8_pixel_t c2(0, 96, 0);
   DrawForestPattern1(target, x, y, c1, c2);
}

static void DrawTemperateForest2(boost::gil::rgb8_image_t::view_t& target,
                                 uint32_t                          x,
                                 uint32_t                          y)
{
   const boost::gil::rgb8_pixel_t c1(0, 64, 0);
   const boost::gil::rgb8_pixel_t c2(0, 112, 0);
   DrawForestPattern2(target, x, y, c1, c2);
}

static void DrawTropicalDryForest(boost::gil::rgb8_image_t::view_t& target,
                                  uint32_t                          x,
                                  uint32_t                          y)
{
   const boost::gil::rgb8_pixel_t c1(51, 36, 3);
   const boost::gil::rgb8_pixel_t c2(139, 204, 58);
   DrawForestPattern2(target, x, y, c1, c2);
}

static void
DrawTundra(boost::gil::rgb8_image_t::view_t& target, uint32_t x, uint32_t y)
{
   DrawShadedPixel(target, x, y, 166, 148, 75);
}

static void DrawWarmTemperateForest(boost::gil::rgb8_image_t::view_t& target,
                                    uint32_t                          x,
                                    uint32_t                          y)
{
   const boost::gil::rgb8_pixel_t c1(0, 96, 0);
   const boost::gil::rgb8_pixel_t c2(0, 192, 0);
   DrawForestPattern2(target, x, y, c1, c2);
}

boost::gil::rgb8_pixel_t Gradient(float                    value,
                                  float                    low,
                                  float                    high,
                                  boost::gil::rgb8_pixel_t lowColor,
                                  boost::gil::rgb8_pixel_t highColor)
{
   if (low == high)
   {
      return lowColor;
   }

   const float range = high - low;
   const float x     = (value - low) / range;
   const float ix    = 1.0f - x;

   const uint8_t lr = lowColor[0];
   const uint8_t lg = lowColor[1];
   const uint8_t lb = lowColor[2];
   const uint8_t hr = highColor[0];
   const uint8_t hg = highColor[1];
   const uint8_t hb = highColor[2];

   const uint8_t r = static_cast<uint8_t>(lr * ix + hr * x);
   const uint8_t g = static_cast<uint8_t>(lg * ix + hg * x);
   const uint8_t b = static_cast<uint8_t>(lb * ix + hb * x);

   return boost::gil::rgb8_pixel_t(r, g, b);
}

template<typename T>
static void ScaleArray(const boost::multi_array<T, 2>& input,
                       boost::multi_array<T, 2>&       output,
                       uint32_t                        scale)
{
   const uint32_t width   = static_cast<uint32_t>(input.shape()[1]);
   const uint32_t height  = static_cast<uint32_t>(input.shape()[0]);
   const uint32_t sWidth  = width * scale;
   const uint32_t sHeight = height * scale;

   output.resize(boost::extents[sHeight][sWidth]);

   if (scale == 1)
   {
      output = input;
   }
   else
   {
      for (int32_t y = height - 1; y >= 0; y--)
      {
         for (uint32_t dy = 0; dy < scale; dy++)
         {
            const uint32_t yp = y * scale + dy;

            for (int32_t x = width - 1; x >= 0; x--)
            {
               for (uint32_t dx = 0; dx < scale; dx++)
               {
                  const uint32_t xp = x * scale + dx;

                  output[yp][xp] = input[y][x];
               }
            }
         }
      }
   }
}

} // namespace WorldEngine


/*==============================================================================
    Copyright (c) 2011 Steven Watanabe

    Distributed under the Boost Software License, Version 1.0. (See accompanying
    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
==============================================================================*/

#ifndef BOOST_PHOENIX_CMATH_HPP_INCLUDED
#define BOOST_PHOENIX_CMATH_HPP_INCLUDED

#include <boost/phoenix/core/limits.hpp>
#include <cmath>
#include <boost/phoenix/function/adapt_callable.hpp>
#include <boost/type_traits/declval.hpp>
#include <boost/phoenix/support/iterate.hpp>

namespace boost {

#if (defined (BOOST_NO_CXX11_DECLTYPE) || \
     defined (BOOST_INTEL_CXX_VERSION) || \
             (BOOST_GCC_VERSION < 40500) )
#define BOOST_PHOENIX_MATH_FUNCTION_RESULT_TYPE(name, n)                \
    typename proto::detail::uncvref<A0>::type
#else
#define BOOST_PHOENIX_MATH_FUNCTION_RESULT_TYPE(name, n)                \
    decltype(name(BOOST_PP_ENUM_BINARY_PARAMS(                          \
                      n                                                 \
                    , boost::declval<typename proto::detail::uncvref<A  \
                    ,  >::type>() BOOST_PP_INTERCEPT)))
#endif
#define BOOST_PHOENIX_MATH_FUNCTION(name, n)                            \
    namespace phoenix_impl {                                            \
    struct name ## _impl {                                              \
        template<class Sig>                                             \
        struct result;                                                  \
        template<class This, BOOST_PHOENIX_typename_A(n)>               \
        struct result<This(BOOST_PHOENIX_A(n))>                         \
        {                                                               \
            typedef                                                     \
                BOOST_PHOENIX_MATH_FUNCTION_RESULT_TYPE(name, n)        \
                type;                                                   \
        };                                                              \
        template<BOOST_PHOENIX_typename_A(n)>                           \
        typename result<name ## _impl(BOOST_PHOENIX_A(n))>::type        \
        operator()(BOOST_PHOENIX_A_const_ref_a(n)) const {              \
            using namespace std;                                        \
            return name(BOOST_PHOENIX_a(n));                            \
        }                                                               \
    };                                                                  \
    }                                                                   \
    namespace phoenix {                                                 \
    BOOST_PHOENIX_ADAPT_CALLABLE(name, phoenix_impl::name ## _impl, n)  \
    }

BOOST_PHOENIX_MATH_FUNCTION(acos, 1)
BOOST_PHOENIX_MATH_FUNCTION(asin, 1)
BOOST_PHOENIX_MATH_FUNCTION(atan, 1)
BOOST_PHOENIX_MATH_FUNCTION(atan2, 2)
BOOST_PHOENIX_MATH_FUNCTION(ceil, 1)
BOOST_PHOENIX_MATH_FUNCTION(cos, 1)
BOOST_PHOENIX_MATH_FUNCTION(cosh, 1)
BOOST_PHOENIX_MATH_FUNCTION(exp, 1)
BOOST_PHOENIX_MATH_FUNCTION(fabs, 1)
BOOST_PHOENIX_MATH_FUNCTION(floor, 1)
BOOST_PHOENIX_MATH_FUNCTION(fmod, 2)
BOOST_PHOENIX_MATH_FUNCTION(frexp, 2)
BOOST_PHOENIX_MATH_FUNCTION(ldexp, 2)
BOOST_PHOENIX_MATH_FUNCTION(log, 1)
BOOST_PHOENIX_MATH_FUNCTION(log10, 1)
BOOST_PHOENIX_MATH_FUNCTION(modf, 2)
BOOST_PHOENIX_MATH_FUNCTION(pow, 2)
BOOST_PHOENIX_MATH_FUNCTION(sin, 1)
BOOST_PHOENIX_MATH_FUNCTION(sinh, 1)
BOOST_PHOENIX_MATH_FUNCTION(sqrt, 1)
BOOST_PHOENIX_MATH_FUNCTION(tan, 1)
BOOST_PHOENIX_MATH_FUNCTION(tanh, 1)

#undef BOOST_PHOENIX_MATH_FUNCTION

}

#endif


/**************************************************************
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 *************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_slideshow.hxx"

#include <canvas/debug.hxx>
#include <tools/diagnose_ex.h>

#include <comphelper/anytostring.hxx>
#include <cppuhelper/exc_hlp.hxx>
#include <basegfx/numeric/ftools.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>

#include <com/sun/star/animations/TransitionType.hpp>
#include <com/sun/star/animations/TransitionSubType.hpp>

#include "transitionfactory.hxx"
#include "transitiontools.hxx"
#include "parametricpolypolygonfactory.hxx"
#include "animationfactory.hxx"
#include "clippingfunctor.hxx"

#include <boost/bind.hpp>

using namespace ::com::sun::star;

namespace slideshow {
namespace internal {

/***************************************************
 ***                                             ***
 ***          Shape Transition Effects           ***
 ***                                             ***
 ***************************************************/

namespace {

class ClippingAnimation : public NumberAnimation
{
public:
    ClippingAnimation(
        const ParametricPolyPolygonSharedPtr&   rPolygon,
        const ShapeManagerSharedPtr&            rShapeManager,
        const TransitionInfo&                   rTransitionInfo,
        bool                                    bDirectionForward,
        bool                                    bModeIn );

    ~ClippingAnimation();

    // Animation interface
    // -------------------
    virtual void prefetch( const AnimatableShapeSharedPtr&     rShape,
                           const ShapeAttributeLayerSharedPtr& rAttrLayer );
    virtual void start( const AnimatableShapeSharedPtr& 	rShape,
                        const ShapeAttributeLayerSharedPtr& rAttrLayer );
    virtual void end();

    // NumberAnimation interface
    // -----------------------
    virtual bool operator()( double nValue );
    virtual double getUnderlyingValue() const;

private:
    void end_();

    AnimatableShapeSharedPtr           mpShape;
    ShapeAttributeLayerSharedPtr       mpAttrLayer;
    ShapeManagerSharedPtr              mpShapeManager;
    ClippingFunctor					   maClippingFunctor;
    bool							   mbSpriteActive;
};

ClippingAnimation::ClippingAnimation(
    const ParametricPolyPolygonSharedPtr&   rPolygon,
    const ShapeManagerSharedPtr&            rShapeManager,
    const TransitionInfo&                   rTransitionInfo,
    bool                                    bDirectionForward,
    bool                                    bModeIn ) :
        mpShape(),
        mpAttrLayer(),
        mpShapeManager( rShapeManager ),
        maClippingFunctor( rPolygon,
                           rTransitionInfo,
                           bDirectionForward,
                           bModeIn ),
        mbSpriteActive(false)
{
    ENSURE_OR_THROW(
        rShapeManager,
        "ClippingAnimation::ClippingAnimation(): Invalid ShapeManager" );
}

ClippingAnimation::~ClippingAnimation()
{
    try
    {
        end_();
    }
    catch (uno::Exception &)
    {
        OSL_ENSURE( false, rtl::OUStringToOString(
                        comphelper::anyToString(
                            cppu::getCaughtException() ),
                        RTL_TEXTENCODING_UTF8 ).getStr() );
    }
}

void ClippingAnimation::prefetch( const AnimatableShapeSharedPtr&,
                                  const ShapeAttributeLayerSharedPtr& )
{
}

void ClippingAnimation::start( const AnimatableShapeSharedPtr& 		rShape,
                               const ShapeAttributeLayerSharedPtr& 	rAttrLayer )
{
    OSL_ENSURE( !mpShape,
                "ClippingAnimation::start(): Shape already set" );
    OSL_ENSURE( !mpAttrLayer,
                "ClippingAnimation::start(): Attribute layer already set" );

    mpShape = rShape;
    mpAttrLayer = rAttrLayer;

    ENSURE_OR_THROW( rShape,
                      "ClippingAnimation::start(): Invalid shape" );
    ENSURE_OR_THROW( rAttrLayer,
                      "ClippingAnimation::start(): Invalid attribute layer" );

    mpShape = rShape;
    mpAttrLayer = rAttrLayer;

    if( !mbSpriteActive )
    {
        mpShapeManager->enterAnimationMode( mpShape );
        mbSpriteActive = true;
    }
}

void ClippingAnimation::end()
{
    end_();
}

void ClippingAnimation::end_()
{
    if( mbSpriteActive )
    {
        mbSpriteActive = false;
        mpShapeManager->leaveAnimationMode( mpShape );

        if( mpShape->isContentChanged() )
            mpShapeManager->notifyShapeUpdate( mpShape );
    }
}

bool ClippingAnimation::operator()( double nValue )
{
    ENSURE_OR_RETURN_FALSE(
        mpAttrLayer && mpShape,
        "ClippingAnimation::operator(): Invalid ShapeAttributeLayer" );

    // set new clip
    mpAttrLayer->setClip( maClippingFunctor( nValue,
                                             mpShape->getDomBounds().getRange() ) );

    if( mpShape->isContentChanged() )
        mpShapeManager->notifyShapeUpdate( mpShape );

    return true;
}

double ClippingAnimation::getUnderlyingValue() const
{
    ENSURE_OR_THROW(
        mpAttrLayer,
        "ClippingAnimation::getUnderlyingValue(): Invalid ShapeAttributeLayer" );

    return 0.0;     // though this should be used in concert with
				    // ActivitiesFactory::createSimpleActivity, better
    				// explicitely name our start value.
				    // Permissible range for operator() above is [0,1]
}

} // anon namespace

AnimationActivitySharedPtr TransitionFactory::createShapeTransition(
    const ActivitiesFactory::CommonParameters&          rParms,
    const AnimatableShapeSharedPtr&                     rShape,
    const ShapeManagerSharedPtr&                        rShapeManager,
    const ::basegfx::B2DVector&                         rSlideSize,
    uno::Reference< animations::XTransitionFilter > const& xTransition )
{
    return createShapeTransition( rParms,
                                  rShape,
                                  rShapeManager,
                                  rSlideSize,
                                  xTransition,
                                  xTransition->getTransition(),
                                  xTransition->getSubtype() );
}

AnimationActivitySharedPtr TransitionFactory::createShapeTransition(
    const ActivitiesFactory::CommonParameters& 				rParms,
    const AnimatableShapeSharedPtr& 						rShape,
    const ShapeManagerSharedPtr& 							rShapeManager,
    const ::basegfx::B2DVector&                             rSlideSize,
    ::com::sun::star::uno::Reference<
    	::com::sun::star::animations::XTransitionFilter > const& xTransition,
    sal_Int16               								nType,
    sal_Int16               								nSubType )
{
    ENSURE_OR_THROW(
        xTransition.is(),
        "TransitionFactory::createShapeTransition(): Invalid XTransition" );

    const TransitionInfo* pTransitionInfo(
        getTransitionInfo( nType, nSubType ) );

    AnimationActivitySharedPtr pGeneratedActivity;
    if( pTransitionInfo != NULL )
    {
        switch( pTransitionInfo->meTransitionClass )
        {
            default:
            case TransitionInfo::TRANSITION_INVALID:
                OSL_ENSURE( false,
                            "TransitionFactory::createShapeTransition(): Invalid transition type. "
                            "Don't ask me for a 0 TransitionType, have no XTransitionFilter node instead!" );
                return AnimationActivitySharedPtr();

            case TransitionInfo::TRANSITION_CLIP_POLYPOLYGON:
            {
                // generate parametric poly-polygon
                ParametricPolyPolygonSharedPtr pPoly(
                    ParametricPolyPolygonFactory::createClipPolyPolygon(
                        nType, nSubType ) );

                // create a clip activity from that
                pGeneratedActivity = ActivitiesFactory::createSimpleActivity(
                    rParms,
                    NumberAnimationSharedPtr(
                        new ClippingAnimation(
                            pPoly,
                            rShapeManager,
                            *pTransitionInfo,
                            xTransition->getDirection(),
                            xTransition->getMode() ) ),
                    true );
            }
            break;

            case TransitionInfo::TRANSITION_SPECIAL:
            {
                switch( nType )
                {
                    case animations::TransitionType::RANDOM:
                    {
                        // select randomly one of the effects from the
                        // TransitionFactoryTable

                        const TransitionInfo* pRandomTransitionInfo( getRandomTransitionInfo() );

                        ENSURE_OR_THROW( pRandomTransitionInfo != NULL,
                                          "TransitionFactory::createShapeTransition(): Got invalid random transition info" );

                        ENSURE_OR_THROW( pRandomTransitionInfo->mnTransitionType != animations::TransitionType::RANDOM,
                                          "TransitionFactory::createShapeTransition(): Got random again for random input!" );

                        // and recurse
                        pGeneratedActivity = createShapeTransition( rParms,
                                                                    rShape,
                                                                    rShapeManager,
                                                                    rSlideSize,
                                                                    xTransition,
                                                                    pRandomTransitionInfo->mnTransitionType,
                                                                    pRandomTransitionInfo->mnTransitionSubType );
                    }
                    break;

                    // TODO(F3): Implement slidewipe for shape
                    case animations::TransitionType::SLIDEWIPE:
                    {
                        sal_Int16 nBarWipeSubType(0);
                        bool	  bDirectionForward(true);

                        // map slidewipe to BARWIPE, for now
                        switch( nSubType )
                        {
                            case animations::TransitionSubType::FROMLEFT:
                                nBarWipeSubType = animations::TransitionSubType::LEFTTORIGHT;
                                bDirectionForward = true;
                                break;

                            case animations::TransitionSubType::FROMRIGHT:
                                nBarWipeSubType = animations::TransitionSubType::LEFTTORIGHT;
                                bDirectionForward = false;
                                break;

                            case animations::TransitionSubType::FROMTOP:
                                nBarWipeSubType = animations::TransitionSubType::TOPTOBOTTOM;
                                bDirectionForward = true;
                                break;

                            case animations::TransitionSubType::FROMBOTTOM:
                                nBarWipeSubType = animations::TransitionSubType::TOPTOBOTTOM;
                                bDirectionForward = false;
                                break;

                            default:
                                ENSURE_OR_THROW( false,
                                                  "TransitionFactory::createShapeTransition(): Unexpected subtype for SLIDEWIPE" );
                                break;
                        }

                        // generate parametric poly-polygon
                        ParametricPolyPolygonSharedPtr pPoly(
                            ParametricPolyPolygonFactory::createClipPolyPolygon(
                                animations::TransitionType::BARWIPE,
                                nBarWipeSubType ) );

                        // create a clip activity from that
                        pGeneratedActivity = ActivitiesFactory::createSimpleActivity(
                            rParms,
                            NumberAnimationSharedPtr(
                                new ClippingAnimation(
                                    pPoly,
                                    rShapeManager,
                                    *getTransitionInfo( animations::TransitionType::BARWIPE,
                                                        nBarWipeSubType ),
                                    bDirectionForward,
                                    xTransition->getMode() ) ),
                            true );
                    }
                    break;

                    default:
                    {
                        // TODO(F1): Check whether there's anything left, anyway,
                        // for _shape_ transitions. AFAIK, there are no special
                        // effects for shapes...

                        // for now, map all to fade effect
                        pGeneratedActivity = ActivitiesFactory::createSimpleActivity(
                            rParms,
                            AnimationFactory::createNumberPropertyAnimation(
                                ::rtl::OUString(
                                    RTL_CONSTASCII_USTRINGPARAM("Opacity") ),
                                rShape,
                                rShapeManager,
                                rSlideSize ),
                            xTransition->getMode() );
                    }
                    break;
                }
            }
            break;
        }
    }

    if( !pGeneratedActivity )
    {
        // No animation generated, maybe no table entry for given
        // transition?
        OSL_TRACE(
            "TransitionFactory::createShapeTransition(): Unknown type/subtype (%d/%d) "
            "combination encountered",
            xTransition->getTransition(),
            xTransition->getSubtype() );
        OSL_ENSURE(
            false,
            "TransitionFactory::createShapeTransition(): Unknown type/subtype "
            "combination encountered" );
    }

    return pGeneratedActivity;
}

}
}


/**
 * @file teaset.cc
 *
 * Copyright (c) 2013 Leon Lynch
 *
 * This file is licensed under the terms of the MIT license.
 * See LICENSE file.
 */

#include "teaset.h"

#include <cstddef>
#include <cstdio>

#include <sstream>
#include <string>
#include <array>
#include <vector>

#include <boost/lexical_cast.hpp>

extern const char teapot_geometry_str[];
extern const char teacup_geometry_str[];
extern const char teaspoon_geometry_str[];

Teaset::~Teaset()
{
}

static std::size_t readCount(std::istringstream& ss)
{
	std::string str;
	while (str.empty())
		std::getline(ss, str);

	return boost::lexical_cast<std::size_t>(str);
}

void Teaset::readData(const char* data, bool data_is_ccw)
{
	std::istringstream ss(data);
	std::size_t count;
	std::vector<std::array<unsigned int,16>> indices;
	std::vector<glm::vec3> vertices;

	// read indices
	count = readCount(ss);
	indices.resize(count);
	for (std::size_t i = 0; i < count; ++i) {
		std::string index_str;
		std::getline(ss, index_str);

		std::sscanf(
			index_str.c_str(),
			"%u,%u,%u,%u,"
			"%u,%u,%u,%u,"
			"%u,%u,%u,%u,"
			"%u,%u,%u,%u",
			&indices[i][0], &indices[i][1], &indices[i][2], &indices[i][3],
			&indices[i][4], &indices[i][5], &indices[i][6], &indices[i][7],
			&indices[i][8], &indices[i][9], &indices[i][10], &indices[i][11],
			&indices[i][12], &indices[i][13], &indices[i][14], &indices[i][15]
		);
	}

	// read vertices
	count = readCount(ss);
	vertices.resize(count);
	for (std::size_t i = 0; i < count; ++i) {
		std::string vertex_str;
		std::getline(ss, vertex_str);

		std::sscanf(
			vertex_str.c_str(),
			"%f,%f,%f",
			&vertices[i][0], &vertices[i][1], &vertices[i][2]
		);
	}

	// lookup control points
	for (auto&& index : indices) {
		BezierPatch patch;

		if (data_is_ccw) {
			for (std::size_t i = 0; i < 16; ++i) {
				patch.k[i/4][i%4] = vertices[index[i] - 1];
			}
		} else {
			for (std::size_t i = 0; i < 16; ++i) {
				patch.k[i/4][3 - (i%4)] = vertices[index[i] - 1];
			}
		}

		patches.push_back(patch);
	}
}

Teapot::Teapot()
{
	readData(teapot_geometry_str, false);
}

Teacup::Teacup()
{
	readData(teacup_geometry_str, true);
}

Teaspoon::Teaspoon()
{
	readData(teaspoon_geometry_str, true);
}