// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/curvecp/test_server.h"

#include <string>

#include "base/logging.h"
#include "base/message_loop.h"
#include "net/base/io_buffer.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
#include "net/base/net_util.h"
#include "net/curvecp/curvecp_server_socket.h"

namespace net {

TestServer::TestServer()
    : socket_(NULL),
      errors_(0) {
}

TestServer::~TestServer() {
  if (socket_) {
    socket_->Close();
    socket_ = NULL;
  }
}

bool TestServer::Start(int port) {
  IPAddressNumber ip_number;
  std::string ip_str("0.0.0.0");
  if (!ParseIPLiteralToNumber(ip_str, &ip_number)) {
    LOG(ERROR) << "Bad IP Address";
    return false;
  }
  IPEndPoint bind_address(ip_number, port);

  DCHECK(!socket_);
  socket_ = new CurveCPServerSocket(NULL, NetLog::Source());
  int rv = socket_->Listen(bind_address, this);
  if (rv < ERR_IO_PENDING) {
    LOG(ERROR) << "Listen on port " << port << " failed: " << rv;
    return false;
  }
  return true;
}

void TestServer::RunWithParams(const Tuple1<int>& params) {
  int status = params.a;
  LOG(INFO) << "Callback! " << status;
  if (status < 0)
    MessageLoop::current()->Quit();
}

void TestServer::OnAccept(CurveCPServerSocket* new_socket) {
  DCHECK(new_socket);
  LOG(ERROR) << "Accepted socket! Starting Echo Server";
  EchoServer* new_server = new EchoServer();
  new_server->Start(new_socket);
}

EchoServer::EchoServer()
    : socket_(NULL),
      bytes_received_(0),
      ALLOW_THIS_IN_INITIALIZER_LIST(
          read_callback_(this, &EchoServer::OnReadComplete)),
      ALLOW_THIS_IN_INITIALIZER_LIST(
          write_callback_(this, &EchoServer::OnWriteComplete)) {
}

EchoServer::~EchoServer() {
}

void EchoServer::Start(CurveCPServerSocket* socket) {
  DCHECK(!socket_);
  socket_ = socket;

  ReadData();
  // Note:  |this| could be deleted here.
}

void EchoServer::OnReadComplete(int result) {
  LOG(INFO) << "Read complete: " << result;
  if (result <= 0) {
    delete this;
    return;
  }

  bytes_received_ += result;
  LOG(INFO) << "Server received " << result << "(" << bytes_received_ << ")";

  if (!received_stream_.VerifyBytes(read_buffer_->data(), result)) {
    LOG(ERROR) << "Server Received corrupt receive data!";
    delete this;
    return;
  }

  // Echo the read data back here.
  DCHECK(!write_buffer_.get());
  write_buffer_ = new DrainableIOBuffer(read_buffer_, result);
  int rv = socket_->Write(write_buffer_, result, &write_callback_);
  if (rv == ERR_IO_PENDING)
    return;
  OnWriteComplete(rv);
}

void EchoServer::OnWriteComplete(int result) {
  if (result <= 0) {
    delete this;
    return;
  }

  write_buffer_->DidConsume(result);
  while (write_buffer_->BytesRemaining()) {
    int rv = socket_->Write(write_buffer_,
                            write_buffer_->BytesRemaining(),
                            &write_callback_);
    if (rv == ERR_IO_PENDING)
      return;
    OnWriteComplete(rv);
  }

  // Now we can read more data.
  write_buffer_ = NULL;
  // read_buffer_ = NULL;
  // ReadData();
}

void EchoServer::ReadData() {
  DCHECK(!read_buffer_.get());
  read_buffer_ = new IOBuffer(kMaxMessage);

  int rv;
  do {
    rv = socket_->Read(read_buffer_, kMaxMessage, &read_callback_);
    if (rv == ERR_IO_PENDING)
      return;
    OnReadComplete(rv);  // Complete the read manually
  } while (rv > 0);
}

}  // namespace net