/*----------------------------------------------------------------------
|    BLT_DecoderServer::UpdateStatus
+---------------------------------------------------------------------*/
BLT_Result
BLT_DecoderServer::UpdateStatus()
{
    BLT_DecoderStatus status;
    BLT_Result        result;

    // get the decoder status
    result = BLT_Decoder_GetStatus(m_Decoder, &status);
    if (BLT_FAILED(result)) return result;

    // notify if the time has changed by more than the update threshold
    // NOTE: current and previous here are measured in milliseconds
    ATX_UInt64 previous = BLT_TimeStamp_ToNanos(m_DecoderStatus.time_stamp)/1000000;
    ATX_UInt64 current  = BLT_TimeStamp_ToNanos(status.time_stamp)/1000000;
    if (m_TimeStampUpdateQuantum) {
        // make the new time stamp a multiple of the update quantum
        current /= m_TimeStampUpdateQuantum;
        current *= m_TimeStampUpdateQuantum;
    }
    if (current != previous) {
        m_DecoderStatus.time_stamp = BLT_TimeStamp_FromMillis(current);
        NotifyTimeCode();
    }

    // convert the stream position into a decoder position 
    if (m_PositionUpdateRange != 0) {
        ATX_UInt64 ratio = status.position.range/m_PositionUpdateRange;
        ATX_UInt64 offset;
        if (ratio == 0) {
            offset = 0;
        } else {
            offset = status.position.offset/ratio;
        }
        if (offset != m_DecoderStatus.position.offset) {
            m_DecoderStatus.position.offset = offset;
            NotifyPosition();
        }
    }

    return BLT_SUCCESS;
}
void StepperMotor::OnStepped ()
{
    stepCount++;

    if (stepCount > decelerationLimit)
    {
        if (state != State::Decelerating)
        {
            state = State::Decelerating;

            if (!fullSpeed)
            {
                decelTime =
                GetTime (frequency, &decelerationGradient, &decelerationOffset);
            }
        }

        // we are decelerating
        frequency =
        GetFrequency (decelTime, &decelerationGradient, &decelerationOffset);

        if (stepCount == stepLimit || frequency <= 0)
        {
            frequencyChannel.Stop ();

            actions.Disable ();
            state = State::Stopped;

            if (MoveCompleted != nullptr)
            {
                EventArgs args;
                MoveCompleted (this, args);
            }
        }

        decelTime += 1.0f / frequency;
    }
    else if (stepCount < accelerationSteps)
    {
        // we are accelerating.
        frequency =
        GetFrequency (time, &accelerationGradient, &accelerationOffset);

        time += 1.0f / frequency;
    }
    else
    {
        if (state != State::Running)
        {
            state = State::Running;
        }

        // we are at constant speed.
        frequency =
        GetFrequency (time, &accelerationGradient, &accelerationOffset);
    }

    this->frequencyChannel.SetFrequency (frequency);

    switch (direction)
    {
    case Direction::Forward:
        if (position == maximum)
        {
            position = 0;
        }

        position++;
        break;

    case Direction::Reverse:
        position--;

        if (position == -1)
        {
            position = maximum;
        }
        break;
    }

    if (notificationsEnabled && position == notificationPosition &&
        NotifyPosition != nullptr)
    {
        EventArgs args;

        NotifyPosition (this, args);
    }
}
/*----------------------------------------------------------------------
|    BLT_DecoderServer::Run
+---------------------------------------------------------------------*/
void
BLT_DecoderServer::Run()
{
    BLT_Result result;

    ATX_LOG_INFO("running");
    
    // create the decoder
    result = BLT_Decoder_Create(&m_Decoder);
    if (BLT_FAILED(result)) {
        m_Client->PostMessage(new BLT_DecoderClient_DecoderEventNotificationMessage(
            BLT_DecoderServer::DecoderEvent::EVENT_TYPE_INIT_ERROR,
            result, 
            "error from BLT_Decoder_Create"));
        WaitForTerminateMessage();
        return;
    }
        
    // register as the event handler
    BLT_Decoder_SetEventListener(m_Decoder, 
                                 &ATX_BASE(&m_EventListener, 
                                           BLT_EventListener));

    // listen to core property changes
    {
        ATX_Properties* properties;
        BLT_Decoder_GetProperties(m_Decoder, &properties);
        ATX_Properties_AddListener(properties, NULL, &ATX_BASE(&m_CorePropertyListener, ATX_PropertyListener), NULL);
    }

    // listen to stream property changes
    {
        ATX_Properties* properties;
        BLT_Decoder_GetStreamProperties(m_Decoder, &properties);
        ATX_Properties_AddListener(properties, NULL, &ATX_BASE(&m_StreamPropertyListener, ATX_PropertyListener), NULL);
    }

    // register builtins 
    result = BLT_Decoder_RegisterBuiltins(m_Decoder);
    if (BLT_FAILED(result)) {
        m_Client->PostMessage(new BLT_DecoderClient_DecoderEventNotificationMessage(
            BLT_DecoderServer::DecoderEvent::EVENT_TYPE_INIT_ERROR,
            result, 
            "error from BLT_Decoder_RegisterBuiltins"));
        WaitForTerminateMessage();
        return;
    }

    // set default output, default type
    result = BLT_Decoder_SetOutput(m_Decoder, 
                                   BLT_DECODER_DEFAULT_OUTPUT_NAME, 
                                   NULL);
    if (BLT_FAILED(result)) {
        m_Client->PostMessage(new BLT_DecoderClient_DecoderEventNotificationMessage(
            BLT_DecoderServer::DecoderEvent::EVENT_TYPE_INIT_ERROR,
            result, 
            "error from BLT_Decoder_SetOutput"));
        //WaitForTerminateMessage();
        //return;
    }
    
    // notify the client of the initial state
    m_Client->PostMessage(
        new BLT_DecoderClient_DecoderStateNotificationMessage(STATE_STOPPED));

    // initial status
    BLT_Decoder_GetStatus(m_Decoder, &m_DecoderStatus);
    m_DecoderStatus.position.range = m_PositionUpdateRange;
    NotifyTimeCode();
    NotifyPosition();

    // initial volume
    float volume=0.0f;
    result = BLT_Decoder_GetVolume(m_Decoder, &volume);
    if (BLT_SUCCEEDED(result)) {
        m_Client->PostMessage(new BLT_DecoderClient_VolumeNotificationMessage(volume));
    }

    SetupIsComplete();

    // decoding loop
    do {
        do {
            result = m_MessageQueue->PumpMessage(0); // non-blocking
        } while (BLT_SUCCEEDED(result));
        
        if (result != NPT_ERROR_LIST_EMPTY) {
            break;
        }

        if (m_State == STATE_PLAYING) {
            result = BLT_Decoder_PumpPacketWithOptions(m_Decoder, BLT_DECODER_PUMP_OPTION_NON_BLOCKING);
            if (BLT_FAILED(result)) {
                if (result == BLT_ERROR_WOULD_BLOCK || result == BLT_ERROR_PORT_HAS_NO_DATA) {
                    /* not fatal, just wait and try again later */
                    ATX_LOG_FINER("pump would block, waiting a short time");
                    result = m_MessageQueue->PumpMessage(BLT_PLAYER_LOOP_WAIT_DURATION);
                } else {
                    ATX_LOG_FINE_1("stopped on %d", result);
                    if (result != BLT_ERROR_EOS) {
                        m_Client->PostMessage(new BLT_DecoderClient_DecoderEventNotificationMessage(
                            BLT_DecoderServer::DecoderEvent::EVENT_TYPE_DECODING_ERROR,
                            result, 
                            "error from BLT_Decoder_PumpPacketWithOptions"));
                    }
                    SetState(STATE_EOS);
                    result = BLT_SUCCESS;
                }
            } else {
                UpdateStatus();
            }
        } else {
            ATX_LOG_FINE("waiting for message");
            result = m_MessageQueue->PumpMessage(NPT_TIMEOUT_INFINITE);
            ATX_LOG_FINE("got message");
        }
    } while (BLT_SUCCEEDED(result) || result == NPT_ERROR_TIMEOUT);

    ATX_LOG_FINE("received Terminate Message");

    // unregister as an event listener
    BLT_Decoder_SetEventListener(m_Decoder, NULL);

    // destroy the decoder
    if (m_Decoder != NULL) {
        BLT_Decoder_Destroy(m_Decoder);
    }  
    
    // we're done
    SetState(STATE_TERMINATED);
}