' *
' * The project site is at: http://sourceforge.jp/projects/fishbornas/
' *
' * First author tiritomato 2012.
' *
' * Distributed under the FishbornArchiveShelf License (See
' *  file "Licenses/License.txt" contained in a project, or the following link.
' *  http://sourceforge.jp/projects/fishbornas/scm/svn/blobs/head/trunk/Licenses/License.txt)
' *
' * 2012.06.07 Initial Revision (tiritomato)
' *

Public Class NumericTextBox

    ' public peoprty ////////////////////////////////////////////////////////////////////////////////////////////

    Public Property Increment As Long
        Get
            Return m_Increment
        End Get
        Set(ByVal value As Long)
            m_Increment = System.Math.Max(1, System.Math.Abs(value))
        End Set
    End Property

    Public Property Maximum As Nullable(Of Long)
        Get
            Return m_Maximum
        End Get
        Set(ByVal value As Nullable(Of Long))
            m_Maximum = value
            If Not value Is Nothing Then
                m_Value = System.Math.Min(value.Value, m_Value)
                m_RegistedValue = System.Math.Min(value.Value, m_RegistedValue)
                InnerTextBox_Flush()
            End If
        End Set
    End Property

    Public Property Minimum As Nullable(Of Long)
        Get
            Return m_Minimum
        End Get
        Set(ByVal value As Nullable(Of Long))
            m_Minimum = value
            If Not m_Minimum Is Nothing Then
                m_Value = System.Math.Max(value.Value, m_Value)
                m_RegistedValue = System.Math.Max(value.Value, m_RegistedValue)
                InnerTextBox_Flush()
            End If
        End Set
    End Property

    <System.ComponentModel.DefaultValue(False)> _
    Public Property [ReadOnly] As Boolean
        Get
            Return InnerTextBox.ReadOnly
        End Get
        Set(ByVal value As Boolean)
            InnerTextBox.ReadOnly = value
            BackColor = InnerTextBox.BackColor
        End Set
    End Property

    Public Overrides Property Text As String
        Get
            Return InnerTextBox.Text
        End Get
        Set(ByVal value As String)
            If value Is Nothing Then
                m_RegistedValue = m_Minimum
                m_Value = m_Minimum
                InnerTextBox.Text = Nothing
            Else
                Dim v As Long = RoundRange(TryParse(value, m_Minimum))
                If v <> m_Value Then
                    m_RegistedValue = v
                    Me.Value = v
                End If
            End If

        End Set
    End Property

    Public Property Unit As String
        Get
            Return m_Unit
        End Get
        Set(ByVal value As String)
            m_Unit = value
            InnerTextBox_Flush()
        End Set
    End Property

    Public Property Value As Long
        Get
            Return m_Value
        End Get
        Set(ByVal value As Long)
            m_Value = value
            InnerTextBox_Flush()
            RaiseEvent ValueChanged(Me, New System.EventArgs)
        End Set
    End Property

    ' public events ///////////////////////////////////////////////////////////////////////////////////////////////

    Public Event ValueChanged(ByVal sender As Object, ByVal e As System.EventArgs)
    Public Event ValueValidated(ByVal sender As Object, ByVal e As System.EventArgs)

    ' private implements //////////////////////////////////////////////////////////////////////////////////////////

    Private m_Unit As String
    Private m_RegistedValue As Long
    Private m_Value As Long
    Private m_Maximum As Nullable(Of Long)
    Private m_Minimum As Nullable(Of Long)
    Private m_Increment As Long = 1

    Private Function RoundRange(ByVal v As Long) As Long
        If Not m_Maximum Is Nothing Then v = System.Math.Min(m_Maximum.Value, v)
        If Not m_Minimum Is Nothing Then v = System.Math.Max(m_Minimum.Value, v)
        Return v
    End Function

    Private Sub InnerTextBox_Flush()
        Dim RegistedSelectionStartIsTrail As Boolean = (InnerTextBox.SelectionStart = InnerTextBox.Text.Length)
        Dim RegistedSelectionStart As Integer = InnerTextBox.SelectionStart
        Dim RegistedSelectionLength As Integer = InnerTextBox.SelectionLength
        If String.IsNullOrWhiteSpace(Unit) Then
            InnerTextBox.Text = m_Value
        Else
            InnerTextBox.Text = m_Value & " " & Unit
        End If
        If RegistedSelectionStartIsTrail Or InnerTextBox.Text.Length < RegistedSelectionStart Then
            InnerTextBox.SelectionStart = InnerTextBox.Text.Length
            InnerTextBox.SelectionLength = 0
        Else
            InnerTextBox.SelectionStart = RegistedSelectionStart
            InnerTextBox.SelectionLength = System.Math.Min(RegistedSelectionStart + RegistedSelectionLength, InnerTextBox.Text.Length) - RegistedSelectionStart
        End If
    End Sub

    Private Function TryParse(Optional ByVal s As String = Nothing, Optional ByVal FailedValue As Nullable(Of Long) = Nothing) As Long
        If s Is Nothing Then s = InnerTextBox.Text
        If Not String.IsNullOrWhiteSpace(Unit) Then s = s.Replace(Unit, Nothing)
        Dim InputNumeric As Long
        If Long.TryParse(s, InputNumeric) Then Return InputNumeric
        If FailedValue Is Nothing Then Return m_RegistedValue
        Return FailedValue.Value
    End Function

    Private Sub InnerTextBox_GotFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles InnerTextBox.GotFocus
        m_RegistedValue = m_Value
    End Sub

    Private Sub InnerTextBox_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles InnerTextBox.KeyDown
        Select Case e.KeyCode
            Case Keys.Up
                Value = RoundRange(TryParse() + Increment)
                e.SuppressKeyPress = True
            Case Keys.Down
                Value = RoundRange(TryParse() - Increment)
                e.SuppressKeyPress = True
            Case Keys.Escape
                Value = m_RegistedValue
        End Select
    End Sub

    Private Sub InnerTextBox_Validated(ByVal sender As Object, ByVal e As System.EventArgs) Handles InnerTextBox.Validated
        Value = TryParse()
        RaiseEvent ValueValidated(Me, e)
    End Sub

End Class
