Imports System.Windows.Forms
Imports System.Drawing
Imports System.Threading
Imports System.Drawing.Drawing2D

Public Class ScrollControl
    Inherits UserControl

    Const BORDER_THICKNESS As Integer = 1

    Private oLock As Object
    Private buffer1 As Bitmap
    Private buffer2 As Bitmap
    Private moverThread As Thread
    Private position As Double = 0
    Private myWidth As Integer
    Private myHeight As Integer
    Private oldValue1 As Integer = 0
    Private oldValue2 As Integer = 0
    Private oldPos As Integer = 0
    Private bufferSwap As Boolean
    Private backGroundBrush As SolidBrush
    Private value1Pen As Pen
    Private value2Pen As Pen
    Private borderPen As Pen
    Private abort As Boolean = False

    Public Overrides Property BackColor() As System.Drawing.Color
        Get
            Return MyBase.BackColor
        End Get
        Set(ByVal value As System.Drawing.Color)
            MyBase.BackColor = value
            backGroundBrush = New SolidBrush(value)
        End Set
    End Property

    Public Property Speed() As Integer
        Get
            Return speedValue * 1000
        End Get
        Set(ByVal value As Integer)
            speedValue = value / 1000
        End Set
    End Property
    Private speedValue As Double = 10

    Public WriteOnly Property Value1() As Single
        Set(ByVal value As Single)
            If value > maxValue Then value = maxValue
            If value < minValue Then value = minValue
            If maxValue - minValue = 0.0 Then
                value1Value = 0
            Else
                value1Value = (myHeight - ((value - minValue) * myHeight) / (maxValue - minValue)) + BORDER_THICKNESS
            End If
        End Set
    End Property
    Private value1Value As Single = 0

    Public WriteOnly Property Value2() As Single
        Set(ByVal value As Single)
            If value > maxValue Then value = maxValue
            If value < minValue Then value = minValue
            If maxValue - minValue = 0.0 Then
                value2Value = 0
            Else
                value2Value = (myHeight - ((value - minValue) * myHeight) / (maxValue - minValue)) + BORDER_THICKNESS
            End If
        End Set
    End Property
    Private value2Value As Single = 0


    Public Sub AddValue(ByVal val1 As Single)
        AddValue(val1, 0)
    End Sub

    Public Event MaxMinAutoChanged()

    Public Sub AddValue(ByVal val1 As Single, ByVal val2 As Single)
        If Auto Then
            If val1 > maxValue Then
                maxValue = val1
                If Auto Then RaiseEvent MaxMinAutoChanged()
            End If
            If Value2Enabled And val2 > maxValue Then
                maxValue = val2
                If Auto Then RaiseEvent MaxMinAutoChanged()
            End If

            If val1 < minValue Then
                minValue = val1
                If Auto Then RaiseEvent MaxMinAutoChanged()
            End If

            If Value2Enabled And val2 < minValue Then
                minValue = val2
                If Auto Then RaiseEvent MaxMinAutoChanged()
            End If

        End If

        If Mode = ModeEnum.ShiftOnValue Then
            Value1 = val1
            Value2 = val2
            position += 1
            If CInt(position) > (myWidth * 2) Then position -= (myWidth * 2)
            Me.Invalidate()
        End If
    End Sub

    Public Property Value2Enabled() As Boolean
        Get
            Return value2EnabledValue
        End Get
        Set(ByVal value As Boolean)
            value2EnabledValue = value
        End Set
    End Property
    Private value2EnabledValue As Boolean

    Public Property Value1Color() As Color
        Get
            Return value1ColorValue
        End Get
        Set(ByVal value As Color)
            value1ColorValue = value
            If value1Pen Is Nothing Then
                value1Pen = New Pen(value1ColorValue)
            Else
                value1Pen.Color = value1ColorValue
            End If

        End Set
    End Property
    Private value1ColorValue As Color = Color.Red

    Public Property Value2Color() As Color
        Get
            Return value2ColorValue
        End Get
        Set(ByVal value As Color)
            value2ColorValue = value
            If value2Pen Is Nothing Then
                value2Pen = New Pen(value2ColorValue)
            Else
                value2Pen.Color = value2ColorValue
            End If

        End Set
    End Property
    Private value2ColorValue As Color = Color.Blue

    Public Property Auto() As Boolean
        Get
            Return autoValue
        End Get
        Set(ByVal value As Boolean)
            If value Then
                maxValue = -10000000
                minValue = 10000000
            End If
            autoValue = value
        End Set
    End Property
    Private autoValue As Boolean

    Public Property Max() As Single
        Get
            Return maxValue
        End Get
        Set(ByVal value As Single)
            maxValue = value
        End Set
    End Property
    Private maxValue As Single = 100

    Public Property Min() As Single
        Get
            Return minValue
        End Get
        Set(ByVal value As Single)
            minValue = value
        End Set
    End Property
    Private minValue As Single = 0

    Public Enum ModeEnum
        ShiftOnValue
        ShiftOnTimer
    End Enum

    Public Property Mode() As ModeEnum
        Get
            Return modeValue
        End Get
        Set(ByVal value As ModeEnum)
            modeValue = value
        End Set
    End Property
    Private modeValue As ModeEnum = ModeEnum.ShiftOnTimer

    Public Sub New()
        myWidth = Me.Width
        myHeight = Me.Height
        buffer1 = New Bitmap(Me.Width, Me.Height)
        buffer2 = New Bitmap(Me.Width, Me.Height)
        borderPen = New Pen(Color.Black, (BORDER_THICKNESS * 2) - 1)
    End Sub

    Public Sub Start()
        If Mode = ModeEnum.ShiftOnTimer Then
            abort = False
            moverThread = New Thread(AddressOf mover)
            moverThread.IsBackground = True
            moverThread.Priority = ThreadPriority.Normal
            moverThread.Start()
        End If
    End Sub

    Public Sub StopGraph()
        If moverThread IsNot Nothing AndAlso moverThread.IsAlive Then
            abort = True
            moverThread.Abort()
            moverThread.Join()
        End If
    End Sub

    Private Sub FillBmp(ByRef bmp As Bitmap, ByVal brush As Brush)
        Using gfx As Graphics = Graphics.FromImage(bmp)
            gfx.FillRectangle(brush, 0, 0, Width, Height)
        End Using
    End Sub

    Protected Overrides Sub OnPaintBackground(ByVal e As System.Windows.Forms.PaintEventArgs)
        'MyBase.OnPaintBackground(e)
    End Sub

    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias
        Try
            If value1Pen Is Nothing Then
                value1Pen = New Pen(value1ColorValue)
                value2Pen = New Pen(value2ColorValue)
            End If

            Dim newPos As Integer = CInt(position)

            If (newPos <= myWidth) And bufferSwap Then
                bufferSwap = False
                FillBmp(buffer1, backGroundBrush)
                oldPos = 0
            ElseIf newPos > myWidth And bufferSwap = False Then
                bufferSwap = True
                FillBmp(buffer2, backGroundBrush)
            End If

            If newPos <= myWidth Then
                Using gBuffer As Graphics = Graphics.FromImage(buffer1)
                    gBuffer.DrawLine(value1Pen, oldPos, oldValue1, newPos, CInt(value1Value))
                    If value2EnabledValue Then
                        gBuffer.DrawLine(value2Pen, oldPos, oldValue2, newPos, CInt(value2Value))
                    End If
                End Using
                e.Graphics.DrawImage(buffer2, -newPos, 0)
                e.Graphics.DrawImage(buffer1, -newPos + myWidth, 0)
            ElseIf newPos > myWidth Then
                Using gBuffer As Graphics = Graphics.FromImage(buffer2)
                    gBuffer.DrawLine(value1Pen, CInt(oldPos - myWidth), oldValue1, newPos - myWidth, CInt(value1Value))
                    If value2EnabledValue Then
                        gBuffer.DrawLine(value2Pen, CInt(oldPos - myWidth), oldValue2, newPos - myWidth, CInt(value2Value))
                    End If
                End Using
                e.Graphics.DrawImage(buffer1, -newPos + myWidth, 0)
                e.Graphics.DrawImage(buffer2, -newPos + 2 * myWidth, 0)
            ElseIf newPos > 2 * myWidth Then
                Dim s As String = ""
            End If

            oldPos = newPos
            oldValue1 = value1Value
            oldValue2 = value2Value

            e.Graphics.DrawRectangle(borderPen, 0, 0, Me.Width - 1, Me.Height - 1)
        Catch ex As Exception
            ' Do Nothing
        End Try
    End Sub

    Private Sub mover()
        Dim startTicks As Long = Environment.TickCount

        While 1 = 1
            Dim dt As Long = Environment.TickCount - startTicks
            startTicks = Environment.TickCount
            position += speedValue * dt
            If CInt(position) > (myWidth * 2) Then position -= (myWidth * 2)
            If oldPos <> CInt(position) Then Me.Invoke(New EventHandler(AddressOf Invalidate))
            Thread.Sleep(1)
            If abort Then Exit While
        End While
    End Sub

    Protected Overrides Sub OnSizeChanged(ByVal e As System.EventArgs)
        MyBase.OnSizeChanged(e)
        myWidth = Me.Width
        myHeight = Me.Height - 2 * BORDER_THICKNESS
        buffer1.Dispose()
        buffer2.Dispose()
        buffer1 = New Bitmap(Me.Width, Me.Height)
        buffer2 = New Bitmap(Me.Width, Me.Height)
        FillBmp(buffer1, backGroundBrush)
        FillBmp(buffer2, backGroundBrush)
    End Sub
End Class
