void HandlePollResponse(const std::string & upload_key,
                        FSM & fsm,
                        const mf::api::upload::poll_upload::Response & response)
{
    if (!response.response_data)
    {
        auto timer = fsm.Timer();

        timer->expires_from_now(
                std::chrono::seconds(poll_upload_retry_timeout_seconds));

        timer->async_wait(boost::bind(&RetryPoll<FSM>, upload_key,
                                      fsm.AsFrontShared(),
                                      boost::asio::placeholders::error));
    }
    else
    {
        const auto & response_data = *response.response_data;
        // if result is negative, it indicates a failure
        if (response_data.result < 0)
        {
            fsm.ProcessEvent(
                    event::Error{std::error_code(response_data.result,
                                                 poll_result_category()),
                                 "Poll upload bad response"});
        }
        else if (response_data.fileerror != 0)
        {
            fsm.ProcessEvent(event::Error{
                    std::error_code(response_data.fileerror,
                                    poll_upload_file_error_category()),
                    "Poll upload file error received"});
        }
        else if (response_data.quickkey)
        {
            HandlePollCompleteResponse(fsm, response);
        }
        else
        {
            auto timer = fsm.Timer();

            timer->expires_from_now(
                    std::chrono::seconds(poll_upload_retry_timeout_seconds));

            timer->async_wait(boost::bind(&RetryPoll<FSM>, upload_key,
                                          fsm.AsFrontShared(),
                                          boost::asio::placeholders::error));
        }
    }
}
    void operator()(
            Event const &,
            FSM & fsm,
            SourceState&,
            TargetState&
        )
    {
        namespace instant = mf::api::upload::instant;

        auto request = instant::Request( fsm.filename(), fsm.hash(),
            fsm.filesize());

        switch (fsm.onDuplicateAction())
        {
            case OnDuplicateAction::Fail:
                // This is the default, same as "skip" and doesn't need to be
                // set.
                break;
            case OnDuplicateAction::Replace:
                request.SetActionOnDuplicate(instant::ActionOnDuplicate::Replace);
                break;
            case OnDuplicateAction::AutoRename:
                request.SetActionOnDuplicate(instant::ActionOnDuplicate::Keep);
                break;
            default:
                assert(!"Invalid duplicate action.");
                break;
        }

        UploadTarget target_folder = fsm.targetFolder();
        boost::apply_visitor(TargetSetter(&request), target_folder);

        auto fsmp = fsm.AsFrontShared();

        fsm.GetSessionMaintainer()->Call(
            request,
            [fsmp](const instant::Response & response)
            {
                if (response.error_code)
                {
                    fsmp->ProcessEvent(event::Error{response.error_code,
                        "Failed to instant upload file."});
                }
                else
                {
                    fsmp->ProcessEvent(event::InstantSuccess{ response.quickkey,
                        response.filename });
                }
            });
    }
void StartPoll(const std::string & upload_key, FSM & fsm)
{
    if (upload_key.empty())
    {
        assert(!"Reached poll upload without upload key");
        fsm.ProcessEvent(
                event::Error{make_error_code(uploader::errc::LogicError),
                             "Filsize unavailable."});
        return;
    }

    auto fsmp = fsm.AsFrontShared();

    fsm.GetSessionMaintainer()->Call(
            mf::api::upload::poll_upload::Request(upload_key),
            [fsmp, upload_key](
                    const mf::api::upload::poll_upload::Response & response)
            {
                HandlePollResponse(upload_key, *fsmp, response);
            });
}