-- ========================================================================= --
--                              SylingTracker                                --
--           https://www.curseforge.com/wow/addons/sylingtracker             --
--                                                                           --
--                               Repository:                                 --
--                   https://github.com/Skamer/SylingTracker                 --
--                                                                           --
-- ========================================================================= --
Syling            "SylingTracker.Options.Elements.Slider"                    ""
-- ========================================================================= --
export {
  GetDecimalCount = SLT.Utils.Math.GetDecimalCount,
  TruncateDecimal = SLT.Utils.Math.TruncateDecimal
}

__Widget__()
class "SUI.MinimalSlider"  { Slider }


__Widget__()
class "SUI.Slider" (function(_ENV)
  inherit "Frame"
  -----------------------------------------------------------------------------
  --                               Events                                    --
  -----------------------------------------------------------------------------
  --- NOTE: This is not the original "OnValueChanged" of slider, but an event build
  --- around of it. The reason is the original may give the value 0.199999 if the 
  --- user sets to 0.2 for example. This may also cause the event to be triggered 
  --- multiple time.

  --- The build event will fix these issues where the real value is given, and triggered 
  --- only when it has changed.
  event "OnValueChanged"
  -----------------------------------------------------------------------------
  --                              Enumerations                               --
  -----------------------------------------------------------------------------
  enum "Label" {
    Left    = "LeftText",
    Right   = "RightText",
    Top     = "TopText",
    Min     = "MinText",
    Max     = "MaxText"
  }
  -----------------------------------------------------------------------------
  --                          Helper functions                               --
  -----------------------------------------------------------------------------
  local function NoModification(value)
	  return value
  end
  -----------------------------------------------------------------------------
  --                               Handlers                                  --
  -----------------------------------------------------------------------------

  local function OnSliderValueChanged(self, new)
    --- (e.g, if the user sets the value to "0.2", the value given may be "0.1999999")
    --- so we need to truncate the decimals, and round it. 
    --- the amount of decimal truncated is based on the value step. 
    --- For example:
    --- value step    number of decimal keeped
    ---   0.2                 1
    ---   0.25                2
    ---   0.002               3
    ---   0.200               1 
    local valueRounded = SLT.Utils.Math.TruncateDecimal(new, self.DecimalCount, true)

    self.Value = valueRounded
  end
  
  local function OnValueChangedHandler(self, new)
    self:GetChild("Slider"):SetValue(new, false)
    self:FormatValue(new)
    self:OnValueChanged(new)
  end


  local function OnMinMaxValuesChangedHandler(self, new)
    Style[self].Slider.minMaxValues = new
  end


  local function OnValueStepChangedHandler(self, new)
    self.DecimalCount = SLT.Utils.Math.GetDecimalCount(new)
    Style[self].Slider.valueStep = new
  end
  -----------------------------------------------------------------------------
  --                               Methods                                   --
  -----------------------------------------------------------------------------

  __Arguments__ { Label, Any/nil }
  function SetLabelFormatter(self, labelType, value)
    if not self.formatters then 
      self.formatters = {}
    end

    local formatter = nil 
    if value == nil then
      formatter = NoModification
    elseif type(value) == "function" then 
      formatter = value
    else 
      formatter = function(v)
        return value 
      end
    end

    self.formatters[labelType] = formatter
  end

  function FormatValue(self, value)
    if not self.formatters then 
      return 
    end

    for labelName, formatter in pairs(self.formatters) do 
      local label = self:GetChild(labelName)
      label:SetText(formatter(value))
      Style[label].visible = true
    end 
  end

  __Arguments__ { Number }
  function SetValue(self, value)
    --- (e.g, if the user sets the value to "0.2", the value given may be "0.1999999")
    --- so we need to truncate the decimals, and round it. 
    --- the amount of decimal truncated is based on the value step. 
    --- For example:
    --- value step    number of decimal keeped
    ---   0.2                 1
    ---   0.25                2
    ---   0.002               3
    ---   0.200               1 
    local valueRounded = SLT.Utils.Math.TruncateDecimal(value, self.DecimalCount, true)

    self.Value = valueRounded
  end

  function GetValue(self)
    return self.Value
  end

  __Arguments__ {  MinMax }
  function SetMinMaxValues(self, minMaxValues)
    self.MinMaxValues = minMaxValues
  end

  __Arguments__ { Number  }
  function SetValueStep(self, valueStep)
    self.ValueStep = valueStep
  end

  function OnAcquire(self)
    self:InstantApplyStyle()
  end

  function OnRelease(self)
    self:SetID(0)
    self:Hide()
    self:ClearAllPoints()
    self:SetParent(nil)

    -- Reset the properties 
    self.Value = nil
    self.MinMaxValues = nil
    self.ValueStep = nil
    self.DecimalCount = nil
  end
  -----------------------------------------------------------------------------
  --                               Properties                                --
  -----------------------------------------------------------------------------
  property "Value" {
    type = Number,
    default = 0,
    handler = OnValueChangedHandler
  }


  property "MinMaxValues" {
    type = MinMax,
    default = MinMax(0, 100),
    handler = OnMinMaxValuesChangedHandler
  }

  property "ValueStep" {
    type    = Number,
    default = 1,
    handler = OnValueStepChangedHandler
  }

  property "DecimalCount" {
    type    = Number,
    default = 0
  }
  -----------------------------------------------------------------------------
  --                            Constructors                                 --
  -----------------------------------------------------------------------------
  __Template__ {
    Slider = SUI.MinimalSlider,
    Back = Button,
    Forward = Button,
    LeftText = FontString,
    RightText = FontString,
    TopText = FontString,
    MinText = FontString,
    MaxText = FontString
  }
  function __ctor(self) 
    local slider = self:GetChild("Slider")
    local back = self:GetChild("Back")
    local forward = self:GetChild("Forward")

    local function OnStepperClicked(forward)
      local value = slider:GetValue()
      local step = slider:GetValueStep()
      if forward then
        slider:SetValue(value + step)
      else
        slider:SetValue(value - step)
	    end
    end

    back.OnClick = back.OnClick + function() OnStepperClicked(false) end 
    forward.OnClick = forward.OnClick + function() OnStepperClicked(true) end

    self.OnSliderValueChanged = function(slider, value) 
      OnSliderValueChanged(self, value)
    end

    slider.OnValueChanged = slider.OnValueChanged + self.OnSliderValueChanged

    --- HACK: We call these handlers manually to avoid issues if the min & max 
    --- values and value step are not correctly init . there is probably a better
    --- solution to do it.
    OnMinMaxValuesChangedHandler(self, self.MinMaxValues, self.MinMaxValues, "MinMaxValues")
    OnValueStepChangedHandler(self, self.ValueStep, self.ValueStep, "ValueStep")
  end 
end)
-------------------------------------------------------------------------------
--                                Styles                                     --
-------------------------------------------------------------------------------
Style.UpdateSkin("Default", {
  [SUI.MinimalSlider] = {
    width = 200,
    height = 10,
    orientation = "HORIZONTAL",
    enableMouse = true,
    obeyStepOnDrag = true,


    LeftBGTexture = {
      atlas = AtlasType("Minimal_SliderBar_Left", true),
      location = {
        Anchor("LEFT")
      }
    },
    RightBGTexture = {
      atlas = AtlasType("Minimal_SliderBar_Right", true),
      location = {
        Anchor("RIGHT")
      }
    },
    MiddleBGTexture = {
      atlas = AtlasType("_Minimal_SliderBar_Middle", true),
      location = {
        Anchor("LEFT", 0, 0, "LeftBGTexture", "RIGHT"),
        Anchor("RIGHT", 0, 0, "RightBGTexture", "LEFT")
      }
    },

    ThumbTexture = {
      atlas = AtlasType("Minimal_SliderBar_Button", true)
    }
  },

  [SUI.Slider] = {
    width   = 250,
    height  = 40,

    Slider = {
      location = {
        Anchor("TOPLEFT", 19, 0),
        Anchor("BOTTOMRIGHT", -19, 0)
      }
    },

    Back = {
      size = Size(11, 19),
      location = {
        Anchor("RIGHT", -4, 0, "Slider", "LEFT")
      },

      BackgroundTexture = {
        atlas = AtlasType("Minimal_SliderBar_Button_Left", true),
        drawLayer = "BACKGROUND",
        setAllPoints = true
      }
    },

    Forward = {
      size = Size(9, 18),
      location = {
        Anchor("LEFT", 4, 0, "Slider", "RIGHT")
      },

      BackgroundTexture = {
        atlas = AtlasType("Minimal_SliderBar_Button_Right", true),
        drawLayer = "BACKGROUND",
        setAllPoints = true
      }
    },

    LeftText = {
      visible = false,
      fontObject = GameFontNormal,
      drawLayer = "OVERLAY",
      location = {
        Anchor("RIGHT", -25, 0, "Slider", "LEFT")
      }
    },
    RightText = {
      visible = false,
      fontObject = GameFontNormal,
      drawLayer = "OVERLAY",
      location = {
        Anchor("LEFT", 25, 0, "Slider", "RIGHT")
      }
    },
    TopText = {
      visible = false,
      fontObject = GameFontNormal,
      drawLayer = "OVERLAY",
      location = {
        Anchor("BOTTOM", 0, -9, "Slider", "TOP")
      }
    },
    MinText = {
      visible = false,
      fontObject = GameFontNormal,
      drawLayer = "OVERLAY",
      location = {
        Anchor("TOP", 0, 6, "Slider", "BOTTOMLEFT")
      }
    },
    MaxText = {
      visible = false,
      fontObject = GameFontNormal,
      drawLayer = "OVERLAY",
      location = {
        Anchor("TOP", 0, 6, "Slider", "BOTTOMRIGHT")
      }
    }
  }
})