#include <scwx/qt/map/alert_layer.hpp>
#include <scwx/qt/gl/draw/geo_lines.hpp>
#include <scwx/qt/manager/text_event_manager.hpp>
#include <scwx/qt/manager/timeline_manager.hpp>
#include <scwx/qt/settings/palette_settings.hpp>
#include <scwx/qt/util/color.hpp>
#include <scwx/qt/util/tooltip.hpp>
#include <scwx/util/logger.hpp>

#include <chrono>
#include <mutex>
#include <ranges>
#include <unordered_map>
#include <unordered_set>

#include <boost/algorithm/string/join.hpp>
#include <boost/asio/system_timer.hpp>
#include <boost/asio/thread_pool.hpp>
#include <boost/container/stable_vector.hpp>
#include <boost/container_hash/hash.hpp>
#include <QEvent>

namespace scwx
{
namespace qt
{
namespace map
{

static const std::string logPrefix_ = "scwx::qt::map::alert_layer";
static const auto        logger_    = scwx::util::Logger::Create(logPrefix_);

static const boost::gil::rgba32f_pixel_t kBlack_ {0.0f, 0.0f, 0.0f, 1.0f};

template<class Key>
struct AlertTypeHash;

template<>
struct AlertTypeHash<std::pair<awips::Phenomenon, bool>>
{
   size_t operator()(const std::pair<awips::Phenomenon, bool>& x) const;
};

static bool IsAlertActive(const std::shared_ptr<const awips::Segment>& segment);

class AlertLayerHandler : public QObject
{
   Q_OBJECT
public:
   struct SegmentRecord
   {
      std::shared_ptr<const awips::Segment>            segment_;
      types::TextEventKey                              key_;
      std::shared_ptr<const awips::TextProductMessage> message_;
      std::chrono::system_clock::time_point            segmentBegin_;
      std::chrono::system_clock::time_point            segmentEnd_;

      SegmentRecord(
         const std::shared_ptr<const awips::Segment>&            segment,
         const types::TextEventKey&                              key,
         const std::shared_ptr<const awips::TextProductMessage>& message) :
          segment_ {segment},
          key_ {key},
          message_ {message},
          segmentBegin_ {segment->event_begin()},
          segmentEnd_ {segment->event_end()}
      {
      }
   };

   explicit AlertLayerHandler()
   {
      connect(textEventManager_.get(),
              &manager::TextEventManager::AlertUpdated,
              this,
              [this](const types::TextEventKey& key, std::size_t messageIndex)
              { HandleAlert(key, messageIndex); });
   }
   ~AlertLayerHandler()
   {
      disconnect(textEventManager_.get(), nullptr, this, nullptr);

      std::unique_lock lock(alertMutex_);
   }

   std::unordered_map<
      std::pair<awips::Phenomenon, bool>,
      boost::container::stable_vector<std::shared_ptr<SegmentRecord>>,
      AlertTypeHash<std::pair<awips::Phenomenon, bool>>>
      segmentsByType_ {};

   std::unordered_map<
      types::TextEventKey,
      boost::container::stable_vector<std::shared_ptr<SegmentRecord>>,
      types::TextEventHash<types::TextEventKey>>
      segmentsByKey_ {};

   void HandleAlert(const types::TextEventKey& key, size_t messageIndex);

   static AlertLayerHandler& Instance();

   std::shared_ptr<manager::TextEventManager> textEventManager_ {
      manager::TextEventManager::Instance()};

   std::shared_mutex alertMutex_ {};

signals:
   void AlertAdded(const std::shared_ptr<SegmentRecord>& segmentRecord,
                   awips::Phenomenon                     phenomenon);
   void AlertUpdated(const std::shared_ptr<SegmentRecord>& segmentRecord);
   void AlertsUpdated(awips::Phenomenon phenomenon, bool alertActive);
};

class AlertLayer::Impl
{
public:
   struct LineData
   {
      boost::gil::rgba32f_pixel_t borderColor_ {};
      boost::gil::rgba32f_pixel_t highlightColor_ {};
      boost::gil::rgba32f_pixel_t lineColor_ {};

      std::size_t borderWidth_ {};
      std::size_t highlightWidth_ {};
      std::size_t lineWidth_ {};
   };

   explicit Impl(AlertLayer*                 self,
                 std::shared_ptr<MapContext> context,
                 awips::Phenomenon           phenomenon) :
       self_ {self},
       phenomenon_ {phenomenon},
       ibw_ {awips::ibw::GetImpactBasedWarningInfo(phenomenon)},
       geoLines_ {{false, std::make_shared<gl::draw::GeoLines>(context)},
                  {true, std::make_shared<gl::draw::GeoLines>(context)}}
   {
      UpdateLineData();
      ConnectSignals();
      ScheduleRefresh();
   }
   ~Impl()
   {
      std::unique_lock refreshLock(refreshMutex_);
      refreshTimer_.cancel();
      refreshLock.unlock();

      threadPool_.join();

      receiver_ = nullptr;

      std::unique_lock lock(linesMutex_);
   };

   void AddAlert(
      const std::shared_ptr<AlertLayerHandler::SegmentRecord>& segmentRecord);
   void UpdateAlert(
      const std::shared_ptr<AlertLayerHandler::SegmentRecord>& segmentRecord);
   void ConnectAlertHandlerSignals();
   void ConnectSignals();
   void HandleGeoLinesEvent(std::shared_ptr<gl::draw::GeoLineDrawItem>& di,
                            QEvent*                                     ev);
   void HandleGeoLinesHover(std::shared_ptr<gl::draw::GeoLineDrawItem>& di,
                            const QPointF& mouseGlobalPos);
   void ScheduleRefresh();

   LineData& GetLineData(const std::shared_ptr<const awips::Segment>& segment,
                         bool alertActive);
   void      UpdateLineData();

   void AddLine(std::shared_ptr<gl::draw::GeoLines>&        geoLines,
                std::shared_ptr<gl::draw::GeoLineDrawItem>& di,
                const common::Coordinate&                   p1,
                const common::Coordinate&                   p2,
                boost::gil::rgba32f_pixel_t                 color,
                float                                       width,
                std::chrono::system_clock::time_point       startTime,
                std::chrono::system_clock::time_point       endTime,
                bool                                        enableHover);
   void AddLines(std::shared_ptr<gl::draw::GeoLines>&   geoLines,
                 const std::vector<common::Coordinate>& coordinates,
                 boost::gil::rgba32f_pixel_t            color,
                 float                                  width,
                 std::chrono::system_clock::time_point  startTime,
                 std::chrono::system_clock::time_point  endTime,
                 bool                                   enableHover,
                 boost::container::stable_vector<
                    std::shared_ptr<gl::draw::GeoLineDrawItem>>& drawItems);
   void UpdateLines();

   static LineData CreateLineData(const settings::LineSettings& lineSettings);

   boost::asio::thread_pool threadPool_ {1u};

   AlertLayer* self_;

   boost::asio::system_timer refreshTimer_ {threadPool_};
   std::mutex                refreshMutex_;

   const awips::Phenomenon                   phenomenon_;
   const awips::ibw::ImpactBasedWarningInfo& ibw_;

   std::unique_ptr<QObject> receiver_ {std::make_unique<QObject>()};

   std::unordered_map<bool, std::shared_ptr<gl::draw::GeoLines>> geoLines_;

   std::unordered_map<std::shared_ptr<const AlertLayerHandler::SegmentRecord>,
                      boost::container::stable_vector<
                         std::shared_ptr<gl::draw::GeoLineDrawItem>>>
      linesBySegment_ {};
   std::unordered_map<std::shared_ptr<const gl::draw::GeoLineDrawItem>,
                      std::shared_ptr<const AlertLayerHandler::SegmentRecord>>
              segmentsByLine_;
   std::mutex linesMutex_ {};

   std::unordered_map<awips::ibw::ThreatCategory, LineData>
            threatCategoryLineData_;
   LineData observedLineData_ {};
   LineData tornadoPossibleLineData_ {};
   LineData inactiveLineData_ {};

   std::chrono::system_clock::time_point selectedTime_ {};

   std::shared_ptr<const gl::draw::GeoLineDrawItem> lastHoverDi_ {nullptr};
   std::string                                      tooltip_ {};

   std::vector<boost::signals2::scoped_connection> connections_ {};
};

AlertLayer::AlertLayer(std::shared_ptr<MapContext> context,
                       awips::Phenomenon           phenomenon) :
    DrawLayer(context), p(std::make_unique<Impl>(this, context, phenomenon))
{
   for (auto alertActive : {false, true})
   {
      auto& geoLines = p->geoLines_.at(alertActive);

      AddDrawItem(geoLines);
   }
}

AlertLayer::~AlertLayer() = default;

void AlertLayer::InitializeHandler()
{
   static bool ftt = true;

   if (ftt)
   {
      logger_->debug("Initializing handler");
      AlertLayerHandler::Instance();
      ftt = false;
   }
}

void AlertLayer::Initialize()
{
   logger_->debug("Initialize: {}", awips::GetPhenomenonText(p->phenomenon_));

   DrawLayer::Initialize();

   auto& alertLayerHandler = AlertLayerHandler::Instance();

   // Take a shared lock to prevent handling additional alerts while populating
   // initial lists
   std::shared_lock lock {alertLayerHandler.alertMutex_};

   for (auto alertActive : {false, true})
   {
      auto& geoLines = p->geoLines_.at(alertActive);

      geoLines->StartLines();

      // Populate initial segments
      auto segmentsIt =
         alertLayerHandler.segmentsByType_.find({p->phenomenon_, alertActive});
      if (segmentsIt != alertLayerHandler.segmentsByType_.cend())
      {
         for (auto& segment : segmentsIt->second)
         {
            p->AddAlert(segment);
         }
      }

      geoLines->FinishLines();
   }

   p->ConnectAlertHandlerSignals();
}

void AlertLayer::Render(const QMapLibre::CustomLayerRenderParameters& params)
{
   gl::OpenGLFunctions& gl = context()->gl();

   for (auto alertActive : {false, true})
   {
      p->geoLines_.at(alertActive)->set_selected_time(p->selectedTime_);
   }

   DrawLayer::Render(params);

   SCWX_GL_CHECK_ERROR();
}

void AlertLayer::Deinitialize()
{
   logger_->debug("Deinitialize: {}", awips::GetPhenomenonText(p->phenomenon_));

   DrawLayer::Deinitialize();
}

bool IsAlertActive(const std::shared_ptr<const awips::Segment>& segment)
{
   auto& vtec        = segment->header_->vtecString_.front();
   auto  action      = vtec.pVtec_.action();
   bool  alertActive = (action != awips::PVtec::Action::Canceled);

   return alertActive;
}

void AlertLayerHandler::HandleAlert(const types::TextEventKey& key,
                                    size_t                     messageIndex)
{
   logger_->trace("HandleAlert: {}", key.ToString());

   std::unordered_set<std::pair<awips::Phenomenon, bool>,
                      AlertTypeHash<std::pair<awips::Phenomenon, bool>>>
      alertsUpdated {};

   auto message = textEventManager_->message_list(key).at(messageIndex);

   // Determine start time for first segment
   std::chrono::system_clock::time_point segmentBegin {};
   if (message->segment_count() > 0)
   {
      segmentBegin = message->segment(0)->event_begin();
   }

   // Take a unique mutex before modifying segments
   std::unique_lock lock {alertMutex_};

   // Update any existing segments with new end time
   auto& segmentsForKey = segmentsByKey_[key];
   for (auto& segmentRecord : segmentsForKey)
   {
      if (segmentRecord->segmentEnd_ > segmentBegin)
      {
         segmentRecord->segmentEnd_ = segmentBegin;

         Q_EMIT AlertUpdated(segmentRecord);
      }
   }

   // Process new segments
   for (auto& segment : message->segments())
   {
      if (!segment->codedLocation_.has_value())
      {
         // Cannot handle a segment without a location
         continue;
      }

      auto&             vtec        = segment->header_->vtecString_.front();
      awips::Phenomenon phenomenon  = vtec.pVtec_.phenomenon();
      bool              alertActive = IsAlertActive(segment);

      auto& segmentsForType = segmentsByType_[{key.phenomenon_, alertActive}];

      // Insert segment into lists
      std::shared_ptr<SegmentRecord> segmentRecord =
         std::make_shared<SegmentRecord>(segment, key, message);

      segmentsForKey.push_back(segmentRecord);
      segmentsForType.push_back(segmentRecord);

      Q_EMIT AlertAdded(segmentRecord, phenomenon);

      alertsUpdated.emplace(phenomenon, alertActive);
   }

   // Release the lock after completing segment updates
   lock.unlock();

   for (auto& alert : alertsUpdated)
   {
      // Emit signal for each updated alert type
      Q_EMIT AlertsUpdated(alert.first, alert.second);
   }
}

void AlertLayer::Impl::ConnectAlertHandlerSignals()
{
   auto& alertLayerHandler = AlertLayerHandler::Instance();

   QObject::connect(
      &alertLayerHandler,
      &AlertLayerHandler::AlertAdded,
      receiver_.get(),
      [this](
         const std::shared_ptr<AlertLayerHandler::SegmentRecord>& segmentRecord,
         awips::Phenomenon                                        phenomenon)
      {
         if (phenomenon == phenomenon_)
         {
            AddAlert(segmentRecord);
         }
      });
   QObject::connect(
      &alertLayerHandler,
      &AlertLayerHandler::AlertUpdated,
      receiver_.get(),
      [this](
         const std::shared_ptr<AlertLayerHandler::SegmentRecord>& segmentRecord)
      {
         if (segmentRecord->key_.phenomenon_ == phenomenon_)
         {
            UpdateAlert(segmentRecord);
         }
      });
}

void AlertLayer::Impl::ConnectSignals()
{
   auto& alertPaletteSettings =
      settings::PaletteSettings::Instance().alert_palette(phenomenon_);
   auto timelineManager = manager::TimelineManager::Instance();

   QObject::connect(timelineManager.get(),
                    &manager::TimelineManager::SelectedTimeUpdated,
                    receiver_.get(),
                    [this](std::chrono::system_clock::time_point dateTime)
                    { selectedTime_ = dateTime; });

   connections_.push_back(alertPaletteSettings.changed_signal().connect(
      [this]()
      {
         UpdateLineData();
         UpdateLines();
      }));
}

void AlertLayer::Impl::ScheduleRefresh()
{
   using namespace std::chrono_literals;

   // Take a unique lock before refreshing
   std::unique_lock lock(refreshMutex_);

   // Expires at the top of the next minute
   std::chrono::system_clock::time_point now =
      std::chrono::floor<std::chrono::minutes>(
         std::chrono::system_clock::now());
   refreshTimer_.expires_at(now + 1min);

   refreshTimer_.async_wait(
      [this](const boost::system::error_code& e)
      {
         if (e == boost::asio::error::operation_aborted)
         {
            logger_->debug("Refresh timer cancelled");
         }
         else if (e != boost::system::errc::success)
         {
            logger_->warn("Refresh timer error: {}", e.message());
         }
         else
         {
            Q_EMIT self_->NeedsRendering();
            ScheduleRefresh();
         }
      });
}

void AlertLayer::Impl::AddAlert(
   const std::shared_ptr<AlertLayerHandler::SegmentRecord>& segmentRecord)
{
   auto& segment = segmentRecord->segment_;

   bool  alertActive = IsAlertActive(segment);
   auto& startTime   = segmentRecord->segmentBegin_;
   auto& endTime     = segmentRecord->segmentEnd_;

   auto& lineData = GetLineData(segment, alertActive);
   auto& geoLines = geoLines_.at(alertActive);

   const auto& coordinates = segment->codedLocation_->coordinates();

   // Take a mutex before modifying lines by segment
   std::unique_lock lock {linesMutex_};

   // Add draw items only if the segment has not already been added
   auto drawItems = linesBySegment_.try_emplace(
      segmentRecord,
      boost::container::stable_vector<
         std::shared_ptr<gl::draw::GeoLineDrawItem>> {});

   // If draw items were added
   if (drawItems.second)
   {
      const float borderWidth    = lineData.borderWidth_;
      const float highlightWidth = lineData.highlightWidth_;
      const float lineWidth      = lineData.lineWidth_;

      const float totalHighlightWidth = lineWidth + (highlightWidth * 2.0f);
      const float totalBorderWidth = totalHighlightWidth + (borderWidth * 2.0f);

      constexpr bool borderHover    = true;
      constexpr bool highlightHover = false;
      constexpr bool lineHover      = false;

      // Add border
      AddLines(geoLines,
               coordinates,
               lineData.borderColor_,
               totalBorderWidth,
               startTime,
               endTime,
               borderHover,
               drawItems.first->second);

      // Add border to segmentsByLine_
      for (auto& di : drawItems.first->second)
      {
         segmentsByLine_.insert({di, segmentRecord});
      }

      // Add highlight
      AddLines(geoLines,
               coordinates,
               lineData.highlightColor_,
               totalHighlightWidth,
               startTime,
               endTime,
               highlightHover,
               drawItems.first->second);

      // Add line
      AddLines(geoLines,
               coordinates,
               lineData.lineColor_,
               lineWidth,
               startTime,
               endTime,
               lineHover,
               drawItems.first->second);
   }
}

void AlertLayer::Impl::UpdateAlert(
   const std::shared_ptr<AlertLayerHandler::SegmentRecord>& segmentRecord)
{
   // Take a mutex before referencing lines iterators and stable vector
   std::unique_lock lock {linesMutex_};

   auto it = linesBySegment_.find(segmentRecord);
   if (it != linesBySegment_.cend())
   {
      auto& segment     = segmentRecord->segment_;
      bool  alertActive = IsAlertActive(segment);

      auto& geoLines = geoLines_.at(alertActive);

      auto& lines = it->second;
      for (auto& line : lines)
      {
         geoLines->SetLineStartTime(line, segmentRecord->segmentBegin_);
         geoLines->SetLineEndTime(line, segmentRecord->segmentEnd_);
      }
   }
}

void AlertLayer::Impl::AddLines(
   std::shared_ptr<gl::draw::GeoLines>&   geoLines,
   const std::vector<common::Coordinate>& coordinates,
   boost::gil::rgba32f_pixel_t            color,
   float                                  width,
   std::chrono::system_clock::time_point  startTime,
   std::chrono::system_clock::time_point  endTime,
   bool                                   enableHover,
   boost::container::stable_vector<std::shared_ptr<gl::draw::GeoLineDrawItem>>&
      drawItems)
{
   for (std::size_t i = 0, j = 1; i < coordinates.size(); ++i, ++j)
   {
      if (j >= coordinates.size())
      {
         j = 0;

         // Ignore repeated coordinates at the end
         if (coordinates[i] == coordinates[j])
         {
            break;
         }
      }

      auto di = geoLines->AddLine();
      AddLine(geoLines,
              di,
              coordinates[i],
              coordinates[j],
              color,
              width,
              startTime,
              endTime,
              enableHover);

      drawItems.push_back(di);
   }
}

void AlertLayer::Impl::AddLine(std::shared_ptr<gl::draw::GeoLines>& geoLines,
                               std::shared_ptr<gl::draw::GeoLineDrawItem>& di,
                               const common::Coordinate&                   p1,
                               const common::Coordinate&                   p2,
                               boost::gil::rgba32f_pixel_t           color,
                               float                                 width,
                               std::chrono::system_clock::time_point startTime,
                               std::chrono::system_clock::time_point endTime,
                               bool enableHover)
{
   geoLines->SetLineLocation(
      di, p1.latitude_, p1.longitude_, p2.latitude_, p2.longitude_);
   geoLines->SetLineModulate(di, color);
   geoLines->SetLineWidth(di, width);
   geoLines->SetLineStartTime(di, startTime);
   geoLines->SetLineEndTime(di, endTime);

   if (enableHover)
   {
      geoLines->SetLineHoverCallback(
         di,
         std::bind(&AlertLayer::Impl::HandleGeoLinesHover,
                   this,
                   std::placeholders::_1,
                   std::placeholders::_2));

      gl::draw::GeoLines::RegisterEventHandler(
         di,
         std::bind(&AlertLayer::Impl::HandleGeoLinesEvent,
                   this,
                   di,
                   std::placeholders::_1));
   }
}

void AlertLayer::Impl::UpdateLines()
{
   std::unique_lock lock {linesMutex_};

   for (auto& segmentLine : linesBySegment_)
   {
      auto& segmentRecord    = segmentLine.first;
      auto& geoLineDrawItems = segmentLine.second;
      auto& segment          = segmentRecord->segment_;
      bool  alertActive      = IsAlertActive(segment);
      auto& lineData         = GetLineData(segment, alertActive);
      auto& geoLines         = geoLines_.at(alertActive);

      const float borderWidth    = lineData.borderWidth_;
      const float highlightWidth = lineData.highlightWidth_;
      const float lineWidth      = lineData.lineWidth_;

      const float totalHighlightWidth = lineWidth + (highlightWidth * 2.0f);
      const float totalBorderWidth = totalHighlightWidth + (borderWidth * 2.0f);

      // Border, highlight and line
      std::size_t linesPerType = geoLineDrawItems.size() / 3;

      // Border
      for (auto& borderLine : geoLineDrawItems | std::views::take(linesPerType))
      {
         geoLines->SetLineModulate(borderLine, lineData.borderColor_);
         geoLines->SetLineWidth(borderLine, totalBorderWidth);
      }

      // Highlight
      for (auto& highlightLine : geoLineDrawItems |
                                    std::views::drop(linesPerType) |
                                    std::views::take(linesPerType))
      {
         geoLines->SetLineModulate(highlightLine, lineData.highlightColor_);
         geoLines->SetLineWidth(highlightLine, totalHighlightWidth);
      }

      // Line
      for (auto& line : geoLineDrawItems | std::views::drop(linesPerType * 2))
      {
         geoLines->SetLineModulate(line, lineData.lineColor_);
         geoLines->SetLineWidth(line, lineWidth);
      }
   }
}

void AlertLayer::Impl::HandleGeoLinesEvent(
   std::shared_ptr<gl::draw::GeoLineDrawItem>& di, QEvent* ev)
{
   switch (ev->type())
   {
   case QEvent::Type::MouseButtonPress:
   {
      auto it = segmentsByLine_.find(di);
      if (it != segmentsByLine_.cend())
      {
         // Display alert dialog
         logger_->debug("Selected alert: {}", it->second->key_.ToString());
         Q_EMIT self_->AlertSelected(it->second->key_);
      }
      break;
   }

   default:
      break;
   }
}

void AlertLayer::Impl::HandleGeoLinesHover(
   std::shared_ptr<gl::draw::GeoLineDrawItem>& di,
   const QPointF&                              mouseGlobalPos)
{
   if (di != lastHoverDi_)
   {
      auto it = segmentsByLine_.find(di);
      if (it != segmentsByLine_.cend())
      {
         tooltip_ =
            boost::algorithm::join(it->second->segment_->productContent_, "\n");
      }
      else
      {
         tooltip_.clear();
      }

      lastHoverDi_ = di;
   }

   if (!tooltip_.empty())
   {
      util::tooltip::Show(tooltip_, mouseGlobalPos);
   }
}

AlertLayer::Impl::LineData
AlertLayer::Impl::CreateLineData(const settings::LineSettings& lineSettings)
{
   return LineData {
      .borderColor_ {lineSettings.GetBorderColorRgba32f()},
      .highlightColor_ {lineSettings.GetHighlightColorRgba32f()},
      .lineColor_ {lineSettings.GetLineColorRgba32f()},
      .borderWidth_ =
         static_cast<std::size_t>(lineSettings.border_width().GetValue()),
      .highlightWidth_ =
         static_cast<std::size_t>(lineSettings.highlight_width().GetValue()),
      .lineWidth_ =
         static_cast<std::size_t>(lineSettings.line_width().GetValue())};
}

void AlertLayer::Impl::UpdateLineData()
{
   auto& alertPalette =
      settings::PaletteSettings().Instance().alert_palette(phenomenon_);

   for (auto threatCategory : ibw_.threatCategories_)
   {
      auto& palette = alertPalette.threat_category(threatCategory);
      threatCategoryLineData_.insert_or_assign(threatCategory,
                                               CreateLineData(palette));
   }

   if (ibw_.hasObservedTag_)
   {
      observedLineData_ = CreateLineData(alertPalette.observed());
   }

   if (ibw_.hasTornadoPossibleTag_)
   {
      tornadoPossibleLineData_ =
         CreateLineData(alertPalette.tornado_possible());
   }

   inactiveLineData_ = CreateLineData(alertPalette.inactive());
}

AlertLayer::Impl::LineData& AlertLayer::Impl::GetLineData(
   const std::shared_ptr<const awips::Segment>& segment, bool alertActive)
{
   if (!alertActive)
   {
      return inactiveLineData_;
   }

   for (auto& threatCategory : ibw_.threatCategories_)
   {
      if (segment->threatCategory_ == threatCategory)
      {
         if (threatCategory == awips::ibw::ThreatCategory::Base)
         {
            if (ibw_.hasObservedTag_ && segment->observed_)
            {
               return observedLineData_;
            }

            if (ibw_.hasTornadoPossibleTag_ && segment->tornadoPossible_)
            {
               return tornadoPossibleLineData_;
            }
         }

         return threatCategoryLineData_.at(threatCategory);
      }
   }

   return threatCategoryLineData_.at(awips::ibw::ThreatCategory::Base);
};

AlertLayerHandler& AlertLayerHandler::Instance()
{
   static AlertLayerHandler alertLayerHandler_ {};
   return alertLayerHandler_;
}

size_t AlertTypeHash<std::pair<awips::Phenomenon, bool>>::operator()(
   const std::pair<awips::Phenomenon, bool>& x) const
{
   size_t seed = 0;
   boost::hash_combine(seed, x.first);
   boost::hash_combine(seed, x.second);
   return seed;
}

} // namespace map
} // namespace qt
} // namespace scwx

#include "alert_layer.moc"
