spear_throw = {}

local self = spear_throw

self.params = {
   spear_type =
   {
      {
         weight = 2.9,
         id_image = "scenes_scene04_02_minigame_minigame03_dart01",
         id_clip = "minigame02_darts03"
      },
      {
         weight = 3.1,
         id_image = "scenes_scene04_02_minigame_minigame03_dart02",
         id_clip = "minigame02_darts01"
      },
      {
         weight = 2.7,
         id_image = "scenes_scene04_02_minigame_minigame03_dart03",
         id_clip = "minigame02_darts02"
      }
   },
   scale = { -- Сколько пикселей в метре
      X = 25,
      Y = 25
   },
   speed = 1,
   g = 9.8,
   spear_create_time = 1000,

   spear_start_pos =
   {
      X = 800,
      Y = 600
   },
   
   spear_start_angle = math.pi / 4,
   spear_min_angle = 0,
   spear_max_angle = math.pi /2,
   spear_start_angle_change = 1, --      1 ... -1
   spear_angle_change_speed = 0.0003, 
   
   spear_start_force = 115,  -- Ньютоны
   spear_min_force   = 80,   -- Ньютоны
   spear_max_force   = 150,  -- Ньютоны
   spear_start_force_change = 1, --      1 ... -1
   spear_force_change_speed = 0.03,
   win = 5,
   win_time = 2500,
   rotate_point =
   {
      X = 172,
      Y = 40
   },
   alpha_insets =
   {
      top = 20,
      bottom = 10,
      left = 10,
      right = 10
   },
   strike_sound = "mini_game_mini_game_spear_throw_01",
   rotate_sound = "",
   power_sound = ""
}

function self.Init(_owner)
   sminigames.SetStandartScriptName(_owner, spear_throw)

   self.owner = _owner
   self.timer = __create_timer ()
   self.effects = {}
   self.darts = _owner.GetWidget ("darts", true)
   self.win = 0

   self.state = -1
   self.next_state = 0
   
   self.force_progress =
   {
      orient = "vertical",
      widget = __cast(_owner.GetWidget(_T("force_progress")), sf.gui.CImageWidget),
      background = __cast(_owner.GetWidget(_T("force_progress_background")), sf.gui.CImageWidget)
   }
   self.win_progress =
   {
      orient = "horizontal",
      widget = __cast(_owner.GetWidget(_T("win_progress")), sf.gui.CImageWidget),
      background = __cast(_owner.GetWidget(_T("win_progress_background")), sf.gui.CImageWidget)
   }
   self.SetProgressAlpha (self.force_progress, 0)
   self.SetProgress (self.win_progress, self.win / self.params.win)
-- 0 Создание дротика
-- 1 Выбор направления (угла)
-- 2 Выбор силы
-- 3 Полёт
-- 4 Победа (падение мишени)
end

function self.SetProgress (_progress_bar, _k)
   if _k < 0 then
      _k = 0
   end
   if _progress_bar.orient == "horizontal" then
      if _k == 0 then
         _progress_bar.widget.AddFlags(sf.gui.CBaseWidget.FlagHidden)
      else
         _progress_bar.widget.RemFlags(sf.gui.CBaseWidget.FlagHidden)
         local image = _progress_bar.widget.GetImage()
         local size = _progress_bar.widget.GetSize()
         local rect = sf.misc.FloatRect(0, 0, size.X * _k, size.Y)
         image.SetRect(rect)
         image.SetSourceRect(rect)
      end
   end
   if _progress_bar.orient == "vertical" then
      if _k == 0 then
         _progress_bar.widget.AddFlags(sf.gui.CBaseWidget.FlagHidden)
      else
         _progress_bar.widget.RemFlags(sf.gui.CBaseWidget.FlagHidden)
         local image = _progress_bar.widget.GetImage()
         local size = _progress_bar.widget.GetSize()
         local rect = sf.misc.FloatRect(0, size.Y * (1 - _k), size.X, size.Y * _k)
         image.SetRect(rect)
         image.SetSourceRect(rect)
      end
   end
end

function self.CreateEffect (_type, _live_time, _params)
   local effects = self.effects
   local i = #effects + 1;
   effects[i] = 
   {
      params = _params,
      type =   _type,
      live_time = _live_time
   };
   effects[i].start_time = self.timer.Get().GetTime();
end

function self.CheckState ()
   if self.next_state ~= nil then
      self.state = self.next_state
      self.next_state = nil
   end
end

function self.SetAlpha (_widget, _alpha)
   local color = sf.graphics.Color (_widget.GetColor());
   color.Alpha = _alpha;
   _widget.SetColor(color);
end

function self.SetProgressAlpha (_progress_bar, _alpha)
   self.SetAlpha (_progress_bar.widget, _alpha)
   self.SetAlpha (_progress_bar.background, _alpha)
end

function self.OnUpdate (_owner)
   local cur_time = self.timer.Get().GetTime()
   local frame_delta = self.timer.Get().GetFrameDelta()
   local remove = nil
   local effects = self.effects
   for i = 1, #effects do
      local time = cur_time -  effects[i].start_time;
      local dead = false
      if effects[i].live_time ~= 0 and time >= effects[i].live_time then
         time = effects[i].live_time
         if remove == nil then
            remove = {}
            remove[1] = i
         else
            remove[#remove + 1] = i
         end
         dead = true
      end
      local k = 1.0;
      if effects[i].live_time ~= 0 then
         k = time / effects[i].live_time;
      end 
      if effects[i].type == "RemoveWidget" then
         if dead then
            effects[i].params.owner.RemoveWidget (effects[i].params.widget)
         end
      end
      if effects[i].type == "ChangeAlpha" then
         local alpha = nil
         if effects[i].params.type == "inc" then
            alpha = 255.0 * k
         else
            alpha = 255.0 * (1.0 - k)
         end
         if effects[i].params.progress ~= nil then
            self.SetProgressAlpha (effects[i].params.progress, alpha)
         else
            self.SetAlpha (effects[i].params.widget, alpha)
         end
      end
      if effects[i].type == "DartsDown" then
         local t = (cur_time - effects[i].params.start_time) / 1000
         local s = self.params.g * t * t
         local y = effects[i].params.start_pos.Y + s * self.params.scale.X   
         effects[i].params.widget.SetOffset (effects[i].params.widget.GetOffset().X, y)
      end
      if effects[i].type == "NextState" then
         if dead then
            self.next_state = effects[i].params.next_state
         end
      end
      if effects[i].type == "Win" then
         if dead then
            _owner.SetGameResult(1)
            _owner.OnEndGame()
         end
      end
   end
   if remove ~= nil then
      for i = 1, #remove do
         local j = #remove - i + 1
         table.remove (effects, remove[j])
      end
   end

   local last_state = self.state
   self.CheckState ()
   if self.state == 0 then
      if self.state ~= last_state then
         self.spear =
         {
            spear_type = math.random(#self.params.spear_type),
            X = self.params.spear_start_pos.X,
            Y = self.params.spear_start_pos.Y,
            angle = self.params.spear_start_angle
         }
         self.spear.widget = sf.gui.CImageWidget (self.params.spear_type[self.spear.spear_type].id_image, "", 2, 0)
         self.spear.widget.SetOffset (self.spear.X, self.spear.Y)
         self.ApplyAngle (self.spear)
         self.SetAlpha (self.spear.widget, 0);
         self.owner.AddWidget (self.spear.widget)
         self.CreateEffect ("ChangeAlpha", self.params.spear_create_time, { widget = self.spear.widget, type  = "inc"})
         self.CreateEffect ("NextState",   self.params.spear_create_time, { next_state = 1 })
         self.throw =
         {
            X = self.spear.X,
            Y = self.spear.Y,
            time = 1,
            angle = self.spear.angle,
            angle_change = self.params.spear_start_angle_change,
            force = self.params.spear_start_force,
            force_change = self.params.spear_start_force_change
         }
      end
   end
   if self.state == 1 then
      if self.state ~= last_state then
         sf.core.g_Application.GetAudioManager().Play(self.params.rotate_sound, -2, -2, -2, -1, -2)
      end
      if self.state == last_state then
         self.throw.angle = self.throw.angle + frame_delta * self.params.spear_angle_change_speed * math.pi * self.throw.angle_change
         if self.throw.angle > self.params.spear_max_angle then
            self.throw.angle = self.params.spear_max_angle
            self.throw.angle_change = self.throw.angle_change * -1
         end
         if self.throw.angle < self.params.spear_min_angle then
            self.throw.angle = self.params.spear_min_angle
            self.throw.angle_change = self.throw.angle_change * -1
         end
         self.spear.angle = self.throw.angle
         self.ApplyAngle (self.spear)
      end
   end
   if self.state == 2 then
      if self.state ~= last_state then
         sf.core.g_Application.GetAudioManager().Stop(_T(self.params.rotate_sound), 0)
         sf.core.g_Application.GetAudioManager().Play(self.params.power_sound, -2, -2, -2, -1, -2)
      end
      if self.state == last_state then
         self.throw.force = self.throw.force + frame_delta * self.params.spear_force_change_speed * self.throw.force_change
         if self.throw.force > self.params.spear_max_force then
            self.throw.force = self.params.spear_max_force
            self.throw.force_change = self.throw.force_change * -1
         end
         if self.throw.force < self.params.spear_min_force then
            self.throw.force = self.params.spear_min_force
            self.throw.force_change = self.throw.force_change * -1
         end
      else
         self.CreateEffect ("ChangeAlpha", self.params.spear_create_time, { progress = self.force_progress, type  = "inc"})
      end
      self.SetProgress (self.force_progress, (self.throw.force - self.params.spear_min_force) / (self.params.spear_max_force - self.params.spear_min_force))
   end
   if self.state == 3 then
      if self.state ~= last_state then
         sf.core.g_Application.GetAudioManager().Stop(_T(self.params.power_sound), 0)
         self.throw.angle = - self.throw.angle
         self.throw.start_time = self.timer.Get ().GetTime ()
         self.CreateEffect ("ChangeAlpha", self.params.spear_create_time, { progress = self.force_progress, type  = "dec"})
      end
      self.UpdatePos (self.spear, self.throw)
      if self.spear.X < -200  or self.spear.Y > 1000 then
         self.next_state = 0
         self.owner.RemoveWidget (self.spear.widget)
      end
      
      local poly = self.spear.widget.GetPoly()
      local point =
      {
         X = ( poly.Point(0).X + poly.Point(3).X ) / 2,
         Y = ( poly.Point(0).Y + poly.Point(3).Y ) / 2
      }
      if point.X <= self.darts.GetOffset ().X + self.darts.GetSize ().X - self.params.alpha_insets.right and
         point.X >  self.darts.GetOffset ().X + self.params.alpha_insets.left and
         point.Y <= self.darts.GetOffset ().Y + self.darts.GetSize ().Y - self.params.alpha_insets.bottom and
         point.Y >  self.darts.GetOffset ().Y + self.params.alpha_insets.top
         then
         local clip = sf.gui.CClipWidget ("", 0, 0)
         clip.GetClip ().Load (self.params.spear_type[self.spear.spear_type].id_clip, true)
         --local clip = sf.gui.CImageWidget (self.params.spear_type[self.spear.spear_type].id_image, "", 2, 0)
         self.owner.RemoveWidget (self.spear.widget)

         self.spear.widget = clip
         self.ApplyAngle (self.spear)
         self.darts.AddWidget (clip)
         local pos =
         {
            X = self.spear.widget.GetOffset ().X - self.darts.GetOffset ().X,
            Y = self.spear.widget.GetOffset ().Y - self.darts.GetOffset ().Y
         }
         self.spear.widget.SetOffset (pos.X, pos.Y)

         self.win = self.win + 1
         sf.core.g_Application.GetAudioManager().Play(self.params.strike_sound, -2, -2, -2, -2, -2)
         
         self.SetProgress (self.win_progress, self.win / self.params.win)

         if self.win >= self.params.win then
            self.next_state = 4
         else
            self.next_state = 0
         end
      end
   end
   if self.state == 4 then
       -- Эффект падения мишени
      if self.state ~= last_state then
         self.CreateEffect ("Win",   self.params.win_time)
         self.CreateEffect ("DartsDown", self.params.win_time, { widget = self.darts, start_pos = { Y = self.darts.GetOffset ().Y }, start_time = cur_time})
      end
   end
   
   return true
end

function self.OnMouseDown (_owner, _pos, _button, _keyboard_state, _broadcast)
   if self.state == 1 then
      self.next_state = 2
      return true
   end
   if self.state == 2 then
      self.next_state = 3
      return true
   end
   return false
end

function self.ApplyAngle (_spear)
   local pos =
   {
      X = _spear.X,
      Y = _spear.Y
   }
   local size =
   {
      X = _spear.widget.GetSize ().X,
      Y = _spear.widget.GetSize ().Y
   }
   --_spear.widget.SetRotation (sf.misc.MatrixRotation(_spear.angle, size.X, size.Y / 2))
   _spear.widget.SetRotation (sf.misc.MatrixRotation(_spear.angle, self.params.rotate_point.X, self.params.rotate_point.Y))
   _spear.widget.SetOffset (pos.X, pos.Y)
end

function self.UpdatePos (_spear, _throw)
   local t = (self.timer.Get ().GetTime () - _throw.start_time ) / 1000 * self.params.speed
   
   local v = _throw.force / self.params.spear_type[_spear.spear_type].weight * _throw.time / 2
   
   local a = - v * math.cos (_throw.angle)
   local b = _throw.X / self.params.scale.X
   local c = self.params.g / 2
   local d = v * math.sin (_throw.angle)
   local e = _throw.Y / self.params.scale.Y
   
   local a2 = 1 / a / a
   
-- f(x) = A * x^2 + B * x + C
   local A = c * a2
   local B = (a * d - 2 * b * c) * a2
   local C = (b * b * c - a * b * d + a * a * e) * a2 
   
-- f'(x) = A_ * x + B_
   local A_ = 2 * A
   local B_ = B

   local x = a * t + b    
   _spear.X = x * self.params.scale.X
   _spear.Y = (c * t * t + d * t + e) * self.params.scale.Y

   _spear.angle = math.atan (A_* x + B_)

   self.ApplyAngle (_spear)
end
