/*
 * This file is a part of the LinkAhead Project.
 * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
 * Copyright (C) 2021-2024 IndiScale GmbH <info@indiscale.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 *
 *********************************************************************************
 *
 * This is derived work which is heavily based on
 * https://github.com/NeiRo21/grpcpp-bidi-streaming, Commit
 * cd9cb78e5d6d72806c2ec4c703e5e856b223dc96, Aug 10, 2020
 *
 * The orginal work is licensed as
 *
 * > MIT License
 * >
 * > Copyright (c) 2019 NeiRo21
 * >
 * > Permission is hereby granted, free of charge, to any person obtaining a
 * > copy of this software and associated documentation files (the "Software"),
 * > to deal in the Software without restriction, including without limitation
 * > the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * > and/or sell copies of the Software, and to permit persons to whom the
 * > Software is furnished to do so, subject to the following conditions:
 * >
 * > The above copyright notice and this permission notice shall be included in
 * > all copies or substantial portions of the Software.
 * >
 * > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * > FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * > DEALINGS IN THE SOFTWARE.
 */
#ifndef CAOSDB_FILE_TRANSMISSION_DOWNLOAD_REQUEST_HANDLER_H
#define CAOSDB_FILE_TRANSMISSION_DOWNLOAD_REQUEST_HANDLER_H

#include "linkahead/file_descriptor.h"               // for FileDescriptor
#include "caosdb/entity/v1/main.pb.h"                // for FileTransmissionS...
#include "caosdb/entity/v1/main.grpc.pb.h"           // for FileDownloadResponse
#include "linkahead/file_transmission/file_writer.h" // for FileWriter
#include "linkahead/handler_interface.h"             // for HandlerTag, Handl...
#include <cstdint>                                   // for uint64_t
#include <grpcpp/client_context.h>                   // for ClientContext
#include <grpcpp/completion_queue.h>                 // for CompletionQueue
#include <grpcpp/support/async_stream.h>             // for ClientAsyncReader
#include <grpcpp/support/status.h>                   // for Status
#include <memory>                                    // for unique_ptr

namespace linkahead::transaction {
using caosdb::entity::v1::FileDownloadRequest;
using caosdb::entity::v1::FileDownloadResponse;
using caosdb::entity::v1::FileTransmissionService;
using linkahead::entity::FileDescriptor;
using linkahead::transaction::HandlerInterface;
using linkahead::transaction::HandlerTag;

/*
 * Handler for the download request of a single file
 */
class DownloadRequestHandler final : public HandlerInterface {
public:
  DownloadRequestHandler(HandlerTag tag, FileTransmissionService::Stub *stub,
                         grpc::CompletionQueue *cq, FileDescriptor file_descriptor);

  ~DownloadRequestHandler() override = default;

  DownloadRequestHandler(const DownloadRequestHandler &) = delete;
  DownloadRequestHandler &operator=(const DownloadRequestHandler &) = delete;
  DownloadRequestHandler(DownloadRequestHandler &&) = delete;
  DownloadRequestHandler &operator=(DownloadRequestHandler &&) = delete;

  void Start() override {
    if (state_ == CallState::NewCall) {
      OnNext(true);
    }
  }

  bool OnNext(bool ok) override;

  void Cancel() override;

protected:
  enum class CallState { NewCall, SendingRequest, ReceivingFile, CallComplete };

  void handleNewCallState();
  void handleSendingRequestState();
  void handleReceivingFileState();
  void handleCallCompleteState();

  HandlerTag tag_;

  FileTransmissionService::Stub *stub_;
  grpc::CompletionQueue *cq_;
  grpc::ClientContext ctx_;

  std::unique_ptr<grpc::ClientAsyncReader<FileDownloadResponse>> rpc_;

  FileDownloadRequest *request_;
  FileDownloadResponse *response_;
  grpc::Status status_;

  CallState state_;

  std::unique_ptr<FileWriter> fileWriter_;

  FileDescriptor file_descriptor_;

  uint64_t bytesReceived_;
};

} // namespace linkahead::transaction

#endif