//! Map editor interface.

use super::glib::*;
use gobject_sys::*;
use gtk_sys::*;
use maraiah::{durandal::ffi, marathon::map, rozinante::editor};

// Iterates over each flag widget with its respective ordered flag object.
fn each_flag<T, F>(buttons: &[PropFlag], ordering: &[T], mut f: F)
   where F: FnMut(&PropFlag, &T)
{
   for (flg, ord) in buttons.iter().zip(ordering) {
      f(flg, ord);
   }
}

// Creates a closure which sets if a button is toggled.
fn r_flg<T, F>(mut f: F) -> impl FnMut(&PropFlag, &T)
   where F: FnMut(&T) -> bool
{
   move |flg, ord| {
      unsafe {
         // we have to block the signal handler so that it doesn't cause an
         // infinite recursion of updating
         g_signal_handler_block(*flg.w as _, flg.h);

         gtk_toggle_button_set_active(*flg.w, f(ord).into());

         g_signal_handler_unblock(*flg.w as _, flg.h);
      }
   }
}

// Creates a closure which checks if a button is toggled.
fn s_flg<T, F>(mut f: F) -> impl FnMut(&PropFlag, &T)
   where F: FnMut(&T, bool)
{
   move |flg, ord| f(ord, unsafe {gtk_toggle_button_get_active(*flg.w)} != 0)
}

impl MapEditor
{
   /// Propagates all updated map information to widgets.
   pub fn cause_refresh(&self)
   {
      self.cause_refresh_view();
      self.cause_refresh_props();
   }

   /// Propagates updated map view information to widgets.
   pub fn cause_refresh_view(&self)
   {
      unsafe {
         gtk_widget_queue_draw(*self.draw);
      }
   }

   /// Propagates updated map property information to widgets.
   pub fn cause_refresh_props(&self)
   {
      let inf = &self.cur_block().info;

      each_flag(&self.fent, &O_ENT, r_flg(|&f| inf.entr_flags.contains(f)));
      each_flag(&self.fenv, &O_ENV, r_flg(|&f| inf.envi_flags.contains(f)));
      each_flag(&self.fmsn, &O_MSN, r_flg(|&f| inf.miss_flags.contains(f)));
   }

   /// Propagates updated map property information to the editor state.
   pub fn cause_update_props(&mut self)
   {
      let mut blk = self.cur_block().clone();
      let inf = &mut blk.info;

      each_flag(&self.fent, &O_ENT, s_flg(|&f, s| inf.entr_flags.set(f, s)));
      each_flag(&self.fenv, &O_ENV, s_flg(|&f, s| inf.envi_flags.set(f, s)));
      each_flag(&self.fmsn, &O_MSN, s_flg(|&f, s| inf.miss_flags.set(f, s)));

      self.push_block(blk);
      self.cause_refresh_view();
   }
}

impl std::ops::Deref for MapEditor
{
   type Target = editor::MapEditor;

   #[inline]
   fn deref(&self) -> &Self::Target {&self.edit}
}

impl std::ops::DerefMut for MapEditor
{
   #[inline]
   fn deref_mut(&mut self) -> &mut Self::Target {&mut self.edit}
}

/// Specialized map editor which has callbacks for frontend purposes.
pub struct MapEditor
{
   pub edit: editor::MapEditor,
   pub draw: Refc<'static, GtkWidget>,
   pub fent: Vec<PropFlag>,
   pub fenv: Vec<PropFlag>,
   pub fmsn: Vec<PropFlag>,
}

/// A runtime reference to the map editor.
pub type MapEditorRef = std::cell::RefCell<MapEditor>;

pub struct PropFlag
{
   pub w: Refc<'static, GtkToggleButton>,
   pub h: ffi::c_ulong,
}

// NOTE: this is flipped because of GTK weirdness. don't touch
const O_ENT: [map::minf::EntFlags; 8] = [
   map::minf::EntFlags::CTF,
   map::minf::EntFlags::RUGBY,
   map::minf::EntFlags::DEFENSE,
   map::minf::EntFlags::KOTH,
   map::minf::EntFlags::KTMWTB,
   map::minf::EntFlags::CARNAGE,
   map::minf::EntFlags::CO_OP,
   map::minf::EntFlags::SOLO,
];

const O_ENV: [map::minf::EnvFlags; 11] = [
   map::minf::EnvFlags::VACUUM,
   map::minf::EnvFlags::MAGNETIC,
   map::minf::EnvFlags::REBELLION,
   map::minf::EnvFlags::LOW_GRAV,
   map::minf::EnvFlags::M1_GLUE,
   map::minf::EnvFlags::LAVA_FLOOR,
   map::minf::EnvFlags::REBELLION2,
   map::minf::EnvFlags::MUSIC,
   map::minf::EnvFlags::TERM_PAUSE,
   map::minf::EnvFlags::M1_MONSTER,
   map::minf::EnvFlags::M1_WEPS,
];

const O_MSN: [map::minf::MsnFlags; 8] = [
   map::minf::MsnFlags::EXTERMINATION,
   map::minf::MsnFlags::EXPLORATION,
   map::minf::MsnFlags::RETRIEVAL,
   map::minf::MsnFlags::REPAIR,
   map::minf::MsnFlags::RESCUE,
   map::minf::MsnFlags::M1_EXPLORATION,
   map::minf::MsnFlags::M1_RESCUE,
   map::minf::MsnFlags::M1_REPAIR,
];

// EOF
