# Fast version with BasicCounting algorithm

include("./LinkedList.jl")
#using ListItem

mutable struct AdwinObject
    DELTA                   :: Float64
    mintMinimLongitudWindow :: Int
    mdbldelta               :: Float64
    mintTime                :: Int
    mintClock               :: Int
    mdblWidth               :: Float64  # Mean of Width = mdblWidth/Number of items
    #BUCKET
    MAXBUCKETS              :: Int
    lastBucketRow           :: Int
    TOTAL                   :: Float64
    VARIANCE                :: Float64
    WIDTH                   :: Int
    BucketNumber            :: Int
    #
    Detect                  :: Int
    numberDetections        :: Int
    DetectTwice             :: Int
    blnBucketDeleted        :: Bool
    BucketNumberMAX         :: Int
    mintMinWinLength        :: Int
    listRowBuckets          :: List
    AdwinObject() = new()
end

function initAdwinObject!(AO)
    AO.DELTA = 0.002
    AO.mintMinimLongitudWindow = 10
    AO.mdbldelta = 0.002
    AO.mintTime = 0
    AO.mintClock = 32
    AO.mdblWidth = 0 # Mean of Width = mdblWidth/Number of items
    #BUCKET
    AO.MAXBUCKETS = 5
    AO.lastBucketRow = 0
    AO.TOTAL = 0
    AO.VARIANCE = 0
    AO.WIDTH = 0
    AO.BucketNumber = 0
    AO.Detect = 0
    AO.numberDetections = 0
    AO.DetectTwice  = 0
    AO.blnBucketDeleted = false
    AO.BucketNumberMAX = 0
    AO.mintMinWinLength = 5
    #AO.listRowBuckets = List()
end

function initBuckets!(ao::AdwinObject)
    ao.listRowBuckets = List()
    ao.lastBucketRow = 0
    ao.TOTAL = 0
    ao.VARIANCE = 0
    ao.WIDTH = 0
    ao.BucketNumber = 0
end

function setInput!(ao::AdwinObject, intEntrada::Float64, delta::Float64)
    blnChange = false
    blnExit = false
    cursor :: ListIt = ListIt()
    ao.mintTime += 1
    # 		//1,2)Increment window in one element
    insertElement!(ao, intEntrada) # -- 1
    bitBucketDeleted = false
    #		//3)Reduce  window
    if ao.mintTime % ao.mintClock == 0 && ao.WIDTH > ao.mintMinimLongitudWindow #line 401
      # println("... setInput!: Enter first If")
      blnReduceWidth= true
      while blnReduceWidth
        # println("... setInput!: Enter While")
        blnReduceWidth = false
        blnExit = false # line 406
        n0=0
        n1=ao.WIDTH
        u0=0
        u1= ao.TOTAL # getTotal();
        v0=0
        v1=ao.VARIANCE
        n2=0
        u2=0

        cursor = ao.listRowBuckets.tail
        i = ao.lastBucketRow
        while !blnExit && !(cursor == cursor.next) # line 418
          println("... setInput!: Enter second While")
          for k = 0 : cursor.bucketSizeRow  # line 419
            println(k)
          end
        end
        # println(myCounter(ao.listRowBuckets.head))
      end
    end
end

function setInput!(ao::AdwinObject, intEntrada::Float64)
    setInput!(ao, intEntrada, ao.mdbldelta)
end

function insertElementBucket!(ao::AdwinObject, value::Float64, variance::Float64, node::Node)
    insertBucket!(node, value, variance) # todo -- 3
    ao.BucketNumber += 1
    if ao.BucketNumber > ao.BucketNumberMAX
      ao.BucketNumberMAX = ao.BucketNumber
    end
end

# function compressBuckets!(ao::AdwinObject)
#     # Traverse the list of buckets in increasing order
#     n1::Int
#     n2::Int
#     u1::Float64
#     u2::Float64
#     incVariance::Float64
#     cursor::ListIt
#     nextNode::ListIt
#     cursor = ao.listRowBuckets.head
#     i::Int = 0
#     while !isnull(cursor)
#         # Find the number of buckets in a row
#         k = cursor.bucketSizeRow
#         # If the row is full, merge buckets
#         if k == ao.MAXBUCKETS+1
#             nextNode = cursor.next
#             if isnull(nextNode)
#                 ao.listRowBuckets # line 365
#             end
#         end
#     end
# end

function compressBuckets!(ao::AdwinObject) # --2
  # 		//Traverse the list of buckets in increasing order
  n1::Int=0
  n2::Int=0
  u1::Float64=0
  u2::Float64=0
  cursor :: Node = ao.listRowBuckets.head
  nextNode:: Node = EmptyNode()
  i=0
  while ! typeof(cursor) <: EmptyNode # --3
    # //Find the number of buckets in a row
    #k = cursor.bucketSizeRow
    # 			//If the row is full, merge buckets
    #if k == ao.MAXBUCKETS
    #  nextNode = cursor.next
    #else
    #  break
    #end
    cursor = cursor.next
  end
end


function insertElement!(ao::AdwinObject, value::Float64)
    ao.WIDTH += 1
    insertElementBucket!(ao, 0.0, value, ao.listRowBuckets.head)
    incVariance::Float64 = 0.0
    if ao.WIDTH > 1
        incVariance = (ao.WIDTH-1)*(value-ao.TOTAL/(ao.WIDTH-1))*(value-ao.TOTAL/(ao.WIDTH-1))/ao.WIDTH
    end
    ao.VARIANCE += incVariance
    ao.TOTAL += value
    # println("w: $value")
    # println(myCounter(ao.listRowBuckets.head))
    #compressBuckets!(ao)
end


function run_adwin()
    AO = AdwinObject()
    initAdwinObject!(AO)
    initBuckets!(AO)
    for p in 0:999
        x = p < 500 ? 1000.0 : 500.0
        setInput!(AO, x)
    end
    println("... run_adwin(): OK")
end
