#pragma once

#include "indexer/drawing_rule_def.hpp"
#include "indexer/drules_selector.hpp"

#include "std/target_os.hpp"

#include <memory>
#include <string>
#include <unordered_map>
#include <vector>

class LineRuleProto;
class AreaRuleProto;
class SymbolRuleProto;
class CaptionRuleProto;
class PathTextRuleProto;
class ShieldRuleProto;
class ContainerProto;
class FeatureType;

namespace drule
{
class BaseRule
{
public:
  BaseRule() = default;
  virtual ~BaseRule() = default;

  virtual LineRuleProto const * GetLine() const;
  virtual AreaRuleProto const * GetArea() const;
  virtual SymbolRuleProto const * GetSymbol() const;
  virtual CaptionRuleProto const * GetCaption() const;
  virtual PathTextRuleProto const * GetPathtext() const;
  virtual ShieldRuleProto const * GetShield() const;

  // Test feature by runtime feature style selector
  // Returns true if rule is applicable for feature, otherwise it returns false
  bool TestFeature(FeatureType & ft, int zoom) const;

  // Set runtime feature style selector
  void SetSelector(std::unique_ptr<ISelector> && selector);

private:
  std::unique_ptr<ISelector> m_selector;
};

class RulesHolder
{
public:
  RulesHolder();
  ~RulesHolder();

  Key AddRule(int scale, TypeT type, BaseRule * p);

  BaseRule const * Find(Key const & k) const;

  uint32_t GetBgColor(int scale) const;
  uint32_t GetColor(std::string const & name) const;

#ifdef OMIM_OS_DESKTOP
  void LoadFromTextProto(std::string const & buffer);
  static void SaveToBinaryProto(std::string const & buffer, std::ostream & s);
#endif

  void LoadFromBinaryProto(std::string const & s);

  template <class ToDo>
  void ForEachRule(ToDo && toDo)
  {
    for (auto const dRule : m_dRules)
      toDo(dRule);
  }

private:
  void InitBackgroundColors(ContainerProto const & cp);
  void InitColors(ContainerProto const & cp);
  void Clean();

  /// background color for scales in range [0...scales::UPPER_STYLE_SCALE]
  std::vector<uint32_t> m_bgColors;
  std::unordered_map<std::string, uint32_t> m_colors;
  std::vector<BaseRule *> m_dRules;
};

RulesHolder & GetCurrentRules();
RulesHolder & GetOutdoorRules();

void LoadRules();
}  // namespace drule
