<commit_before>#include "base64.h"
#include "strings.h"

#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/optional.hpp>

#include <atomic>
#include <chrono>
#include <fstream>
#include <iostream>
#include <memory>
#include <numeric>
#include <string>
#include <random>
#include <thread>

using namespace std;
using boost::asio::ip::tcp;

enum ProblemType {
  MAZE,
  SUDOKU,
  TREE,
  ARRAY,
  PASSWORD,
  RLE,

  NB_ELEMS
};

class BaseProblem {
public:
  virtual ~BaseProblem() {}

public:
  template <class T> void init(bool answer, vector<T> &&data, const boost::optional<int>& expectedValue);

  virtual bool getAnswer() const = 0;
  virtual boost::optional<size_t> getExpectedValue() const = 0;
  virtual size_t size() const = 0;

  template <class T> const vector<T> &getData() const;
};

template <class T> class ConcreteProblem : public BaseProblem {
public:
  virtual ~ConcreteProblem() {}

public:
    void init(bool answer, vector<T> &&data, const boost::optional<int>& expectedValue)
    {
        mAnswer = answer;
        mData = data;
        mExpected = expectedValue;
    }

    bool getAnswer() const override { return mAnswer; }
    boost::optional<size_t> getExpectedValue() const override {
      return mExpected;
    }
    size_t size() const override { return mData.size(); }
    const vector<T> &getData() const { return mData; }

private:
  bool mAnswer;
  boost::optional<size_t> mExpected;
  vector<T> mData;
};

template <class T> const vector<T> &BaseProblem::getData() const {
    return dynamic_cast<const ConcreteProblem<T>&>(*this).getData();
}

template <class T> void BaseProblem::init(bool answer, vector<T> &&data,
                                          const boost::optional<int>& expectedValue) {
    return dynamic_cast<const ConcreteProblem<T>&>(*this).init();
}

class ProblemContainer {
public:
  ProblemContainer() { mProblems.reserve(ProblemType::NB_ELEMS); }

  template <class T>
  void addProblem(ProblemType type, bool answer, vector<T> &&data, const boost::optional<int>& expectedValue) {
    auto prob = std::make_unique<ConcreteProblem<T>>();
    prob->init(answer, std::move(data), expectedValue);
    mProblems[type].push_back(std::move(prob));
    ++mGlobalSize;
  }

  BaseProblem *getProblem(ProblemType type, int index) {
    return mProblems[type][index].get();
  }

  size_t getProblemSize(ProblemType type) { return mProblems[type].size(); }

  size_t getGlobalSize() const { return mGlobalSize; }

private:
  vector<vector<unique_ptr<BaseProblem>>> mProblems;
  size_t mGlobalSize;
};

atomic<int> score;
ProblemContainer problems;
boost::array<bool, 4> answers;
atomic<int> problemScore;
atomic<bool> expired;

std::random_device rd;
std::default_random_engine e1(rd());
std::uniform_int_distribution<int> uniform_dist(0, ProblemType::NB_ELEMS);
std::uniform_int_distribution<int> uniform_dist2(0, 13);
std::bernoulli_distribution bool_dist1(0.2);
std::bernoulli_distribution bool_dist2(0.01);

class TCPConnection : public boost::enable_shared_from_this<TCPConnection> {
public:
  typedef boost::shared_ptr<TCPConnection> pointer;

  static pointer create(boost::asio::io_service &IOService) {
    return pointer(new TCPConnection(IOService));
  }

  tcp::socket &socket() { return mSocket; }

  void start() {
    sendData();
    readData();
  }

private:
  TCPConnection(boost::asio::io_service &IOService)
      : mSocket(IOService), mTimer(IOService) {}

  void stop() {
    mSocket.close();
    mTimer.cancel();
  }

  void readData() {
    boost::asio::async_read(mSocket, boost::asio::buffer(mReadMessage),
                            boost::bind(&TCPConnection::onDataReceived,
                                        shared_from_this(),
                                        boost::asio::placeholders::error));
  }

  void onDataReceived(const boost::system::error_code &ec) {
    if (ec) {
      cout << "I f****** quit !!!" << endl
           << "Oh btw, network error, client crashed, connection reset, "
              "armagueddon, 9/11 or somethin'...."
           << endl;
      stop();
      // throw exception("bye bye !");
      throw exception();
      return;
    }

    mTimer.expires_from_now(boost::posix_time::seconds(5));
    mTimer.async_wait(boost::bind(&TCPConnection::onDataTimerExpired,
                                  shared_from_this(),
                                  boost::asio::placeholders::error, &mTimer));
    for (int i = 0; i < 4; ++i) {
      if (mReadMessage[i] != answers[i]) {
        score -= problemScore;
        cout << "YOU'RE WRONG !!!! -" << problemScore << endl;
        sendData();
        readData();
        return;
      }
    }
    score += problemScore * 2;
    cout << "well alright... +" << problemScore * 2 << endl;
    sendData();
    readData();
  }

  void sendData() {
    size_t dataSize;
    vector<boost::asio::const_buffer> bufs;

    int next = uniform_dist(e1);
    bufs.push_back(boost::asio::buffer(&next, sizeof(next)));
    problemScore = next < 3 ? 2 : 1;
    std::uniform_int_distribution<int> problemIdxDist(
        0, problems.getProblemSize(static_cast<ProblemType>(next)));

    for (int i = 0; i < 4; ++i) {
      auto problem = problems.getProblem(static_cast<ProblemType>(next),
                                         problemIdxDist(e1));

      boost::optional<size_t> expectedValue = problem->getExpectedValue();
      if (expectedValue) {
        dataSize = expectedValue.get();
        bufs.push_back(boost::asio::buffer(&dataSize, sizeof(dataSize)));
      }

      dataSize = problem->size();
      bufs.push_back(boost::asio::buffer(&dataSize, sizeof(dataSize)));

      if (next < 3) {
        bufs.push_back(boost::asio::buffer(
            reinterpret_cast<const char *>(&problem->getData<int>()),
            dataSize * sizeof(int)));
      } else {
        bufs.push_back(boost::asio::buffer(
            reinterpret_cast<const char *>(&problem->getData<char>()),
            dataSize * sizeof(char)));
      }
      answers[i % 4] = problem->getAnswer();

      boost::asio::async_write(
          mSocket, bufs,
          boost::bind(&TCPConnection::handleWrite, shared_from_this(),
                      boost::asio::placeholders::error,
                      boost::asio::placeholders::bytes_transferred));

      mTimer.expires_from_now(boost::posix_time::seconds(5));
      mTimer.async_wait(boost::bind(&TCPConnection::onDataTimerExpired,
                                    shared_from_this(),
                                    boost::asio::placeholders::error, &mTimer));
    }
  }

  void onDataTimerExpired(const boost::system::error_code &ec,
                          boost::asio::deadline_timer *) {
    if (ec == boost::asio::error::operation_aborted) {
      cout << "Abort" << endl;
      return;
    }

    cout << "Awww.... too slow -" << problemScore << endl;
    score -= problemScore;
    sendData();
  }

  void handleWrite(const boost::system::error_code & /*error*/,
                   size_t /*bytes_transferred*/) {
    cout << "You have " << score << " points, sending new problem" << endl;
  }

private:
  tcp::socket mSocket;
  boost::array<bool, 4> mReadMessage;
  boost::asio::deadline_timer mTimer;
};

class TCPServer {
public:
  TCPServer(boost::asio::io_service &IOService)
      : mAcceptor(IOService, tcp::endpoint(tcp::v4(), 22022)) {
    startAccept();
  }

private:
  void startAccept() {
    TCPConnection::pointer NewConnection =
        TCPConnection::create(mAcceptor.get_io_service());

    mAcceptor.async_accept(NewConnection->socket(),
                           boost::bind(&TCPServer::handleAccept, this,
                                       NewConnection,
                                       boost::asio::placeholders::error));
  }

  void handleAccept(TCPConnection::pointer NewConnection,
                    const boost::system::error_code &error) {
    if (!error) {
      std::cout << "Here we go !" << std::endl;
      NewConnection->start();
    }

    startAccept();
  }

private:
  tcp::acceptor mAcceptor;
};

template <class T>
void readProblem(const string &name, ProblemContainer &problems) {
  ifstream file(name, ios::in | ios::binary);

  file.seekg(0, file.end);
  int length = file.tellg();
  file.seekg(0, file.beg);
  T tempDataType;
  int tempInt;
  int size;
  boost::optional<int> expectedValue = boost::none;

  // Read problem set
  while (length > 0) {
    // Read the flag
    unsigned char flag = file.get();
    --length;
    // Read the 4 problems
    for (int i = 0; i < 4; ++i) {
      // Read single problem size
      file.read((char *)&tempInt, sizeof(tempInt));
      length -= sizeof(tempInt);
      size = tempInt;

      // Read the expected value if the problem uses one
      if (flag > 3) {
        file.read((char *)&tempInt, sizeof(tempInt));
        length -= sizeof(tempInt);
        expectedValue = tempInt;
      }

      // Read the problem data
      vector<T> problemData;
      problemData.reserve(tempInt);
      for (int j = 0; j < size; ++j) {
        file.read((char *)&tempDataType, sizeof(tempDataType));
        length -= sizeof(tempDataType);
        problemData.push_back(tempDataType);
      }
      problems.addProblem(static_cast<ProblemType>(flag), flag & (1 << i), std::move(problemData), expectedValue);
    }
  }
}

int main(int argc, char **argv) {
  cout << argv[1] << endl;
  readProblem<int>(argc > 1 ? argv[1] : "maze_small.bin", problems);
  readProblem<int>(argc > 2 ? argv[2] : "sudoku_small.bin", problems);
  readProblem<int>(argc > 3 ? argv[3] : "array_small.bin", problems);
  readProblem<char>(argc > 4 ? argv[4] : "password_small.bin", problems);
  readProblem<int>(argc > 5 ? argv[5] : "tree_small.bin", problems);
  readProblem<char>(argc > 6 ? argv[6] : "RLE_small.bin", problems);

  cout << problems.getGlobalSize() << " problems loaded" << endl;

  try {
    boost::asio::io_service IOService;
    TCPServer Server(IOService);
    if (bool_dist2(e1)) {
      std::cout << base64_decode(not_welcome) << std::endl << std::endl;
      for (;;) {
        for (int t = 0; t < 5; ++t) {
          std::cout << "0xD ";
          this_thread::sleep_for(chrono::milliseconds(200));
        }
        std::cout << "\t" << go_away[uniform_dist2(e1)] << "\t";
        for (int t = 0; t < 7; ++t) {
          std::cout << " 0xD";
          this_thread::sleep_for(chrono::milliseconds(200));
        }
        std::cout << endl;
        this_thread::sleep_for(chrono::milliseconds(2000));
      }
    } else if (bool_dist1(e1)) {
      std::cout << base64_decode(welcome) << std::endl;
      std::cout << "...Waiting for \"client\"..." << std::endl;
    } else {
      std::cout << "Waiting for client..." << std::endl;
    }
    IOService.run();
  } catch (std::exception &e) {
    std::cerr << e.what() << std::endl;
  }
  for (int i = 0; i < 5; ++i)
    cout << "FINAL SCORE " << score << endl;

  return 0;
}
<commit_msg>Fixed data acquisition<commit_after>#include "base64.h"
#include "strings.h"

#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/optional.hpp>

#include <atomic>
#include <chrono>
#include <fstream>
#include <iostream>
#include <memory>
#include <numeric>
#include <string>
#include <random>
#include <thread>

#include <filesystem>

using boost::asio::ip::tcp;

enum ProblemType {
  MAZE,
  SUDOKU,
  TREE,
  PASSWORD,
  ARRAY,
  RLE,

  NB_ELEMS
};

class BaseProblem {
public:
  virtual ~BaseProblem() {}

public:
  template <class T> void init(bool answer, std::vector<T> &&data, const boost::optional<int>& expectedValue);

  virtual bool getAnswer() const = 0;
  virtual boost::optional<size_t> getExpectedValue() const = 0;
  virtual size_t size() const = 0;

  template <class T> const std::vector<T> &getData() const;
};

template <class T> class ConcreteProblem : public BaseProblem {
public:
  virtual ~ConcreteProblem() {}

public:
    void init(bool answer, std::vector<T> &&data, const boost::optional<int>& expectedValue)
    {
        mAnswer = answer;
        mData = data;
        mExpected = expectedValue;
    }

    bool getAnswer() const override { return mAnswer; }
    boost::optional<size_t> getExpectedValue() const override {
      return mExpected;
    }
    size_t size() const override { return mData.size(); }
    const std::vector<T> &getData() const { return mData; }

private:
  bool mAnswer;
  boost::optional<size_t> mExpected;
  std::vector<T> mData;
};

template <class T> const std::vector<T> &BaseProblem::getData() const {
    return dynamic_cast<const ConcreteProblem<T>&>(*this).getData();
}

template <class T> void BaseProblem::init(bool answer, std::vector<T> &&data,
                                          const boost::optional<int>& expectedValue) {
    return dynamic_cast<const ConcreteProblem<T>&>(*this).init();
}

class ProblemContainer {
public:
  ProblemContainer() : mProblems{ ProblemType::NB_ELEMS } { }

  template <class T>
  void addProblem(ProblemType type, bool answer, std::vector<T> &&data, const boost::optional<int>& expectedValue) {
    auto prob = std::make_unique<ConcreteProblem<T>>();
    prob->init(answer, std::move(data), expectedValue);
    mProblems[type].push_back(std::move(prob));
    ++mGlobalSize;
  }

  BaseProblem *getProblem(ProblemType type, int index) {
    return mProblems[type][index].get();
  }

  size_t getProblemSize(ProblemType type) { return mProblems[type].size(); }

  size_t getGlobalSize() const { return mGlobalSize; }

private:
  std::vector<std::vector<std::unique_ptr<BaseProblem>>> mProblems;
  size_t mGlobalSize;
};

std::atomic<int> score;
ProblemContainer problems;
boost::array<bool, 4> answers;
std::atomic<int> problemScore;
std::atomic<bool> expired;

std::random_device rd;
std::default_random_engine e1(rd());
std::uniform_int_distribution<int> uniform_dist(0, ProblemType::NB_ELEMS);
std::uniform_int_distribution<int> uniform_dist2(0, 13);
std::bernoulli_distribution bool_dist1(0.2);
std::bernoulli_distribution bool_dist2(0.01);

class TCPConnection : public boost::enable_shared_from_this<TCPConnection> {
public:
  typedef boost::shared_ptr<TCPConnection> pointer;

  static pointer create(boost::asio::io_service &IOService) {
    return pointer(new TCPConnection(IOService));
  }

  tcp::socket &socket() { return mSocket; }

  void start() {
    sendData();
    readData();
  }

private:
  TCPConnection(boost::asio::io_service &IOService)
      : mSocket(IOService), mTimer(IOService) {}

  void stop() {
    mSocket.close();
    mTimer.cancel();
  }

  void readData() {
    boost::asio::async_read(mSocket, boost::asio::buffer(mReadMessage),
                            boost::bind(&TCPConnection::onDataReceived,
                                        shared_from_this(),
                                        boost::asio::placeholders::error));
  }

  void onDataReceived(const boost::system::error_code &ec) {
    if (ec) {
        std::cout << "I f****** quit !!!" << std::endl
           << "Oh btw, network error, client crashed, connection reset, "
              "armagueddon, 9/11 or somethin'...."
           << std::endl;
      stop();
      // throw exception("bye bye !");
      throw std::exception();
      return;
    }

    mTimer.expires_from_now(boost::posix_time::seconds(5));
    mTimer.async_wait(boost::bind(&TCPConnection::onDataTimerExpired,
                                  shared_from_this(),
                                  boost::asio::placeholders::error, &mTimer));
    for (int i = 0; i < 4; ++i) {
      if (mReadMessage[i] != answers[i]) {
        score -= problemScore;
        std::cout << "YOU'RE WRONG !!!! -" << problemScore << std::endl;
        sendData();
        readData();
        return;
      }
    }
    score += problemScore * 2;
    std::cout << "well alright... +" << problemScore * 2 << std::endl;
    sendData();
    readData();
  }

  void sendData() {
    size_t dataSize;
    std::vector<boost::asio::const_buffer> bufs;

    int next = uniform_dist(e1);
    bufs.push_back(boost::asio::buffer(&next, sizeof(next)));
    problemScore = next < 3 ? 2 : 1;
    std::uniform_int_distribution<int> problemIdxDist(
        0, problems.getProblemSize(static_cast<ProblemType>(next)));

    for (int i = 0; i < 4; ++i) {
      auto problem = problems.getProblem(static_cast<ProblemType>(next),
                                         problemIdxDist(e1));

      boost::optional<size_t> expectedValue = problem->getExpectedValue();
      if (expectedValue) {
        dataSize = expectedValue.get();
        bufs.push_back(boost::asio::buffer(&dataSize, sizeof(dataSize)));
      }

      dataSize = problem->size();
      bufs.push_back(boost::asio::buffer(&dataSize, sizeof(dataSize)));

      if (next < 3) {
        bufs.push_back(boost::asio::buffer(
            reinterpret_cast<const char *>(&problem->getData<int>()),
            dataSize * sizeof(int)));
      } else {
        bufs.push_back(boost::asio::buffer(
            reinterpret_cast<const char *>(&problem->getData<char>()),
            dataSize * sizeof(char)));
      }
      answers[i % 4] = problem->getAnswer();

      boost::asio::async_write(
          mSocket, bufs,
          boost::bind(&TCPConnection::handleWrite, shared_from_this(),
                      boost::asio::placeholders::error,
                      boost::asio::placeholders::bytes_transferred));

      mTimer.expires_from_now(boost::posix_time::seconds(5));
      mTimer.async_wait(boost::bind(&TCPConnection::onDataTimerExpired,
                                    shared_from_this(),
                                    boost::asio::placeholders::error, &mTimer));
    }
  }

  void onDataTimerExpired(const boost::system::error_code &ec,
                          boost::asio::deadline_timer *) {
    if (ec == boost::asio::error::operation_aborted) {
        std::cout << "Abort" << std::endl;
      return;
    }

    std::cout << "Awww.... too slow -" << problemScore << std::endl;
    score -= problemScore;
    sendData();
  }

  void handleWrite(const boost::system::error_code & /*error*/,
                   size_t /*bytes_transferred*/) {
      std::cout << "You have " << score << " points, sending new problem" << std::endl;
  }

private:
  tcp::socket mSocket;
  boost::array<bool, 4> mReadMessage;
  boost::asio::deadline_timer mTimer;
};

class TCPServer {
public:
  TCPServer(boost::asio::io_service &IOService)
      : mAcceptor(IOService, tcp::endpoint(tcp::v4(), 22022)) {
    startAccept();
  }

private:
  void startAccept() {
    TCPConnection::pointer NewConnection =
        TCPConnection::create(mAcceptor.get_io_service());

    mAcceptor.async_accept(NewConnection->socket(),
                           boost::bind(&TCPServer::handleAccept, this,
                                       NewConnection,
                                       boost::asio::placeholders::error));
  }

  void handleAccept(TCPConnection::pointer NewConnection,
                    const boost::system::error_code &error) {
    if (!error) {
      std::cout << "Here we go !" << std::endl;
      NewConnection->start();
    }

    startAccept();
  }

private:
  tcp::acceptor mAcceptor;
};

template <class T>
void readProblem(const std::string &name, ProblemType type, ProblemContainer &problems) {
  std::ifstream file(name, std::ios::in | std::ios::binary);

  file.seekg(0, file.end);
  int length = file.tellg();
  file.seekg(0, file.beg);
  T tempDataType;
  int tempInt;
  int size;
  boost::optional<int> expectedValue = boost::none;

  // Read problem set
  while (length > 0) {
    // Read the flag
    unsigned char flag = file.get();
    --length;

    // Read the expected value if this is a RLE problem
    if (type == RLE) {
        file.read((char *)&tempInt, sizeof(tempInt));
        length -= sizeof(tempInt);
        expectedValue = tempInt;
    }

    // Read the 4 problems
    for (int i = 0; i < 4; ++i) {
      // Read single problem size
      file.read((char *)&tempInt, sizeof(tempInt));
      length -= sizeof(tempInt);
      size = tempInt;

      // Read the expected value if this is an array problem
      if (type == ARRAY) {
        file.read((char *)&tempInt, sizeof(tempInt));
        length -= sizeof(tempInt);
        expectedValue = tempInt;
        --size;
      }

      // Read the problem data
      std::vector<T> problemData;
      problemData.reserve(tempInt);
      for (int j = 0; j < size; ++j) {
        file.read((char *)&tempDataType, sizeof(tempDataType));
        length -= sizeof(tempDataType);
        problemData.push_back(tempDataType);
      }
      problems.addProblem(type, flag & (1 << i), std::move(problemData), expectedValue);
    }
  }
}

int main(int argc, char **argv) {
  readProblem<int>(argc > 1 ? argv[1] : "maze_small.bin", MAZE, problems);
  readProblem<int>(argc > 2 ? argv[2] : "sudoku_small.bin", SUDOKU, problems);
  readProblem<int>(argc > 3 ? argv[3] : "array_small.bin", ARRAY, problems);
  readProblem<char>(argc > 4 ? argv[4] : "password_small.bin", PASSWORD, problems);
  readProblem<int>(argc > 5 ? argv[5] : "tree_small.bin", TREE, problems);
  readProblem<char>(argc > 6 ? argv[6] : "RLE_small.bin", RLE, problems);

  std::cout << problems.getGlobalSize() << " problems loaded" << std::endl;

  try {
    boost::asio::io_service IOService;
    TCPServer Server(IOService);
    if (bool_dist2(e1)) {
      std::cout << base64_decode(not_welcome) << std::endl << std::endl;
      for (;;) {
        for (int t = 0; t < 5; ++t) {
          std::cout << "0xD ";
          std::this_thread::sleep_for(std::chrono::milliseconds(200));
        }
        std::cout << "\t" << go_away[uniform_dist2(e1)] << "\t";
        for (int t = 0; t < 7; ++t) {
          std::cout << " 0xD";
          std::this_thread::sleep_for(std::chrono::milliseconds(200));
        }
        std::cout << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(2000));
      }
    } else if (bool_dist1(e1)) {
      std::cout << base64_decode(welcome) << std::endl;
      std::cout << "...Waiting for \"client\"..." << std::endl;
    } else {
      std::cout << "Waiting for client..." << std::endl;
    }
    IOService.run();
  } catch (std::exception &e) {
    std::cerr << e.what() << std::endl;
  }
  for (int i = 0; i < 5; ++i)
      std::cout << "FINAL SCORE " << score << std::endl;

  return 0;
}
<|endoftext|>


<commit_before>#include "base64.h"
#include "strings.h"

#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/optional.hpp>

#include <atomic>
#include <chrono>
#include <fstream>
#include <iostream>
#include <memory>
#include <numeric>
#include <string>
#include <random>
#include <thread>

using boost::asio::ip::tcp;

enum ProblemType {
  MAZE,
  SUDOKU,
  TREE,
  PASSWORD,
  ARRAY,
  RLE,

  NB_ELEMS
};

class BaseProblem {
public:
  virtual ~BaseProblem() {}

public:
  template <class T> void init(bool answer, std::vector<T> &&data, const boost::optional<int>& expectedValue);

  virtual bool getAnswer() const = 0;
  virtual boost::optional<size_t> getExpectedValue() const = 0;
  virtual size_t size() const = 0;

  template <class T> const std::vector<T> &getData() const;
};

template <class T> class ConcreteProblem : public BaseProblem {
public:
  virtual ~ConcreteProblem() {}

public:
    void init(bool answer, std::vector<T> &&data, const boost::optional<int>& expectedValue)
    {
        mAnswer = answer;
        mData = data;
        mExpected = expectedValue;
    }

    bool getAnswer() const override { return mAnswer; }
    boost::optional<size_t> getExpectedValue() const override {
      return mExpected;
    }
    size_t size() const override { return mData.size(); }
    const std::vector<T> &getData() const { return mData; }

private:
  bool mAnswer;
  boost::optional<size_t> mExpected;
  std::vector<T> mData;
};

template <class T> const std::vector<T> &BaseProblem::getData() const {
    return dynamic_cast<const ConcreteProblem<T>&>(*this).getData();
}

template <class T> void BaseProblem::init(bool answer, std::vector<T> &&data,
                                          const boost::optional<int>& expectedValue) {
    return dynamic_cast<const ConcreteProblem<T>&>(*this).init();
}

class ProblemContainer {
public:
  ProblemContainer() : mProblems{ ProblemType::NB_ELEMS } { }

  template <class T>
  void addProblem(ProblemType type, bool answer, std::vector<T> &&data, const boost::optional<int>& expectedValue) {
    auto prob = std::make_unique<ConcreteProblem<T>>();
    prob->init(answer, std::move(data), expectedValue);
    mProblems[type].push_back(std::move(prob));
    ++mGlobalSize;
  }

  BaseProblem *getProblem(ProblemType type, int index) {
    return mProblems[type][index].get();
  }

  size_t getProblemSize(ProblemType type) { return mProblems[type].size(); }

  size_t getGlobalSize() const { return mGlobalSize; }

private:
  std::vector<std::vector<std::unique_ptr<BaseProblem>>> mProblems;
  size_t mGlobalSize;
};

std::atomic<int> score;
ProblemContainer problems;
boost::array<bool, 4> answers;
std::atomic<int> problemScore;
std::atomic<bool> expired;

std::random_device rd;
std::default_random_engine e1(rd());
std::uniform_int_distribution<int> uniform_dist(0, ProblemType::NB_ELEMS);
std::uniform_int_distribution<int> uniform_dist2(0, 13);
std::bernoulli_distribution bool_dist1(0.2);
std::bernoulli_distribution bool_dist2(0.01);

class TCPConnection : public boost::enable_shared_from_this<TCPConnection> {
public:
  typedef boost::shared_ptr<TCPConnection> pointer;

  static pointer create(boost::asio::io_service &IOService) {
    return pointer(new TCPConnection(IOService));
  }

  tcp::socket &socket() { return mSocket; }

  void start() {
    sendData();
    readData();
  }

private:
  TCPConnection(boost::asio::io_service &IOService)
      : mSocket(IOService), mTimer(IOService) {}

  void stop() {
    mSocket.close();
    mTimer.cancel();
  }

  void readData() {
    boost::asio::async_read(mSocket, boost::asio::buffer(mReadMessage),
                            boost::bind(&TCPConnection::onDataReceived,
                                        shared_from_this(),
                                        boost::asio::placeholders::error));
  }

  void onDataReceived(const boost::system::error_code &ec) {
    if (ec) {
        std::cout << "I f****** quit !!!" << std::endl
           << "Oh btw, network error, client crashed, connection reset, "
              "armagueddon, 9/11 or somethin'...."
           << std::endl;
      stop();
      // throw exception("bye bye !");
      throw std::exception();
      return;
    }

    mTimer.expires_from_now(boost::posix_time::seconds(5));
    mTimer.async_wait(boost::bind(&TCPConnection::onDataTimerExpired,
                                  shared_from_this(),
                                  boost::asio::placeholders::error, &mTimer));
    for (int i = 0; i < 4; ++i) {
      if (mReadMessage[i] != answers[i]) {
        score -= problemScore;
        std::cout << "YOU'RE WRONG !!!! -" << problemScore << std::endl;
        sendData();
        readData();
        return;
      }
    }
    score += problemScore * 2;
    std::cout << "well alright... +" << problemScore * 2 << std::endl;
    sendData();
    readData();
  }

  void sendData() {
    size_t dataSize;
    std::vector<boost::asio::const_buffer> bufs;

    int next = uniform_dist(e1);
    bufs.push_back(boost::asio::buffer(&next, sizeof(next)));
    problemScore = next < 3 ? 2 : 1;
    std::uniform_int_distribution<int> problemIdxDist(
        0, problems.getProblemSize(static_cast<ProblemType>(next)));

    for (int i = 0; i < 4; ++i) {
      auto problem = problems.getProblem(static_cast<ProblemType>(next),
                                         problemIdxDist(e1));

      boost::optional<size_t> expectedValue = problem->getExpectedValue();
      if (expectedValue) {
        dataSize = expectedValue.get();
        bufs.push_back(boost::asio::buffer(&dataSize, sizeof(dataSize)));
      }

      dataSize = problem->size();
      bufs.push_back(boost::asio::buffer(&dataSize, sizeof(dataSize)));

      if (next < 3) {
        bufs.push_back(boost::asio::buffer(
            reinterpret_cast<const char *>(&problem->getData<int>()),
            dataSize * sizeof(int)));
      } else {
        bufs.push_back(boost::asio::buffer(
            reinterpret_cast<const char *>(&problem->getData<char>()),
            dataSize * sizeof(char)));
      }
      answers[i % 4] = problem->getAnswer();

      boost::asio::async_write(
          mSocket, bufs,
          boost::bind(&TCPConnection::handleWrite, shared_from_this(),
                      boost::asio::placeholders::error,
                      boost::asio::placeholders::bytes_transferred));

      mTimer.expires_from_now(boost::posix_time::seconds(5));
      mTimer.async_wait(boost::bind(&TCPConnection::onDataTimerExpired,
                                    shared_from_this(),
                                    boost::asio::placeholders::error, &mTimer));
    }
  }

  void onDataTimerExpired(const boost::system::error_code &ec,
                          boost::asio::deadline_timer *) {
    if (ec == boost::asio::error::operation_aborted) {
        std::cout << "Abort" << std::endl;
      return;
    }

    std::cout << "Awww.... too slow -" << problemScore << std::endl;
    score -= problemScore;
    sendData();
  }

  void handleWrite(const boost::system::error_code & /*error*/,
                   size_t /*bytes_transferred*/) {
      std::cout << "You have " << score << " points, sending new problem" << std::endl;
  }

private:
  tcp::socket mSocket;
  boost::array<bool, 4> mReadMessage;
  boost::asio::deadline_timer mTimer;
};

class TCPServer {
public:
  TCPServer(boost::asio::io_service &IOService)
      : mAcceptor(IOService, tcp::endpoint(tcp::v4(), 22022)) {
    startAccept();
  }

private:
  void startAccept() {
    TCPConnection::pointer NewConnection =
        TCPConnection::create(mAcceptor.get_io_service());

    mAcceptor.async_accept(NewConnection->socket(),
                           boost::bind(&TCPServer::handleAccept, this,
                                       NewConnection,
                                       boost::asio::placeholders::error));
  }

  void handleAccept(TCPConnection::pointer NewConnection,
                    const boost::system::error_code &error) {
    if (!error) {
      std::cout << "Here we go !" << std::endl;
      NewConnection->start();
    }

    startAccept();
  }

private:
  tcp::acceptor mAcceptor;
};

template <class T>
void readProblem(const std::string &name, ProblemType type, ProblemContainer &problems) {
  std::ifstream file(name, std::ios::in | std::ios::binary);

  file.seekg(0, file.end);
  int length = file.tellg();
  file.seekg(0, file.beg);
  T tempDataType;
  int tempInt;
  int size;
  boost::optional<int> expectedValue = boost::none;

  // Read problem set
  while (length > 0) {
    // Read the flag
    unsigned char flag = file.get();
    --length;

    // Read the expected value if this is a RLE problem
    if (type == RLE) {
        file.read((char *)&tempInt, sizeof(tempInt));
        length -= sizeof(tempInt);
        expectedValue = tempInt;
    }

    // Read the 4 problems
    for (int i = 0; i < 4; ++i) {
      // Read single problem size
      file.read((char *)&tempInt, sizeof(tempInt));
      length -= sizeof(tempInt);
      size = tempInt;

      // Read the expected value if this is an array problem
      if (type == ARRAY) {
        file.read((char *)&tempInt, sizeof(tempInt));
        length -= sizeof(tempInt);
        expectedValue = tempInt;
        --size;
      }

      // Read the problem data
      std::vector<T> problemData;
      problemData.reserve(tempInt);
      for (int j = 0; j < size; ++j) {
        file.read((char *)&tempDataType, sizeof(tempDataType));
        length -= sizeof(tempDataType);
        problemData.push_back(tempDataType);
      }
      problems.addProblem(type, flag & (1 << i), std::move(problemData), expectedValue);
    }
  }
}

int main(int argc, char **argv) {
  readProblem<int>(argc > 1 ? argv[1] : "maze_small.bin", MAZE, problems);
  readProblem<int>(argc > 2 ? argv[2] : "sudoku_small.bin", SUDOKU, problems);
  readProblem<int>(argc > 3 ? argv[3] : "array_small.bin", ARRAY, problems);
  readProblem<char>(argc > 4 ? argv[4] : "password_small.bin", PASSWORD, problems);
  readProblem<int>(argc > 5 ? argv[5] : "tree_small.bin", TREE, problems);
  readProblem<char>(argc > 6 ? argv[6] : "RLE_small.bin", RLE, problems);

  std::cout << problems.getGlobalSize() << " problems loaded" << std::endl;

  try {
    boost::asio::io_service IOService;
    TCPServer Server(IOService);
    if (bool_dist2(e1)) {
      std::cout << base64_decode(not_welcome) << std::endl << std::endl;
      for (;;) {
        for (int t = 0; t < 5; ++t) {
          std::cout << "0xD ";
          std::this_thread::sleep_for(std::chrono::milliseconds(200));
        }
        std::cout << "\t" << go_away[uniform_dist2(e1)] << "\t";
        for (int t = 0; t < 7; ++t) {
          std::cout << " 0xD";
          std::this_thread::sleep_for(std::chrono::milliseconds(200));
        }
        std::cout << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(2000));
      }
    } else if (bool_dist1(e1)) {
      std::cout << base64_decode(welcome) << std::endl;
      std::cout << "...Waiting for \"client\"..." << std::endl;
    } else {
      std::cout << "Waiting for client..." << std::endl;
    }
    IOService.run();
  } catch (std::exception &e) {
    std::cerr << e.what() << std::endl;
  }
  for (int i = 0; i < 5; ++i)
      std::cout << "FINAL SCORE " << score << std::endl;

  return 0;
}
<commit_msg>Fixed problem initialization<commit_after>#include "base64.h"
#include "strings.h"

#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/optional.hpp>

#include <atomic>
#include <chrono>
#include <fstream>
#include <iostream>
#include <memory>
#include <numeric>
#include <string>
#include <random>
#include <thread>

using boost::asio::ip::tcp;

enum ProblemType {
  MAZE,
  SUDOKU,
  TREE,
  PASSWORD,
  ARRAY,
  RLE,

  NB_ELEMS
};

class BaseProblem {
public:
  virtual ~BaseProblem() {}

public:
  template <class T> void init(bool answer, std::vector<T> &&data, const boost::optional<int>& expectedValue);

  virtual bool getAnswer() const = 0;
  virtual boost::optional<size_t> getExpectedValue() const = 0;
  virtual size_t size() const = 0;

  template <class T> const std::vector<T> &getData() const;
};

template <class T> class ConcreteProblem : public BaseProblem {
public:
  virtual ~ConcreteProblem() {}

public:
    void init(bool answer, std::vector<T> &&data, const boost::optional<int>& expectedValue)
    {
        mAnswer = answer;
        mData = data;
        mExpected = expectedValue;
    }

    bool getAnswer() const override { return mAnswer; }
    boost::optional<size_t> getExpectedValue() const override {
      return mExpected;
    }
    size_t size() const override { return mData.size(); }
    const std::vector<T> &getData() const { return mData; }

private:
  bool mAnswer;
  boost::optional<size_t> mExpected;
  std::vector<T> mData;
};

template <class T> const std::vector<T> &BaseProblem::getData() const {
    return dynamic_cast<const ConcreteProblem<T>&>(*this).getData();
}

template <class T> void BaseProblem::init(bool answer, std::vector<T> &&data,
                                          const boost::optional<int>& expectedValue) {
    return dynamic_cast<const ConcreteProblem<T>&>(*this).init(answer, data, expectedValue);
}

class ProblemContainer {
public:
  ProblemContainer() : mProblems{ ProblemType::NB_ELEMS } { }

  template <class T>
  void addProblem(ProblemType type, bool answer, std::vector<T> &&data, const boost::optional<int>& expectedValue) {
    auto prob = std::make_unique<ConcreteProblem<T>>();
    prob->init(answer, std::move(data), expectedValue);
    mProblems[type].push_back(std::move(prob));
    ++mGlobalSize;
  }

  BaseProblem *getProblem(ProblemType type, int index) {
    return mProblems[type][index].get();
  }

  size_t getProblemSize(ProblemType type) { return mProblems[type].size(); }

  size_t getGlobalSize() const { return mGlobalSize; }

private:
  std::vector<std::vector<std::unique_ptr<BaseProblem>>> mProblems;
  size_t mGlobalSize;
};

std::atomic<int> score;
ProblemContainer problems;
boost::array<bool, 4> answers;
std::atomic<int> problemScore;
std::atomic<bool> expired;

std::random_device rd;
std::default_random_engine e1(rd());
std::uniform_int_distribution<int> uniform_dist(0, ProblemType::NB_ELEMS);
std::uniform_int_distribution<int> uniform_dist2(0, 13);
std::bernoulli_distribution bool_dist1(0.2);
std::bernoulli_distribution bool_dist2(0.01);

class TCPConnection : public boost::enable_shared_from_this<TCPConnection> {
public:
  typedef boost::shared_ptr<TCPConnection> pointer;

  static pointer create(boost::asio::io_service &IOService) {
    return pointer(new TCPConnection(IOService));
  }

  tcp::socket &socket() { return mSocket; }

  void start() {
    sendData();
    readData();
  }

private:
  TCPConnection(boost::asio::io_service &IOService)
      : mSocket(IOService), mTimer(IOService) {}

  void stop() {
    mSocket.close();
    mTimer.cancel();
  }

  void readData() {
    boost::asio::async_read(mSocket, boost::asio::buffer(mReadMessage),
                            boost::bind(&TCPConnection::onDataReceived,
                                        shared_from_this(),
                                        boost::asio::placeholders::error));
  }

  void onDataReceived(const boost::system::error_code &ec) {
    if (ec) {
        std::cout << "I f****** quit !!!" << std::endl
           << "Oh btw, network error, client crashed, connection reset, "
              "armagueddon, 9/11 or somethin'...."
           << std::endl;
      stop();
      // throw exception("bye bye !");
      throw std::exception();
      return;
    }

    mTimer.expires_from_now(boost::posix_time::seconds(5));
    mTimer.async_wait(boost::bind(&TCPConnection::onDataTimerExpired,
                                  shared_from_this(),
                                  boost::asio::placeholders::error, &mTimer));
    for (int i = 0; i < 4; ++i) {
      if (mReadMessage[i] != answers[i]) {
        score -= problemScore;
        std::cout << "YOU'RE WRONG !!!! -" << problemScore << std::endl;
        sendData();
        readData();
        return;
      }
    }
    score += problemScore * 2;
    std::cout << "well alright... +" << problemScore * 2 << std::endl;
    sendData();
    readData();
  }

  void sendData() {
    size_t dataSize;
    std::vector<boost::asio::const_buffer> bufs;

    int next = uniform_dist(e1);
    bufs.push_back(boost::asio::buffer(&next, sizeof(next)));
    problemScore = next < 3 ? 2 : 1;
    std::uniform_int_distribution<int> problemIdxDist(
        0, problems.getProblemSize(static_cast<ProblemType>(next)));

    for (int i = 0; i < 4; ++i) {
      auto problem = problems.getProblem(static_cast<ProblemType>(next),
                                         problemIdxDist(e1));

      boost::optional<size_t> expectedValue = problem->getExpectedValue();
      if (expectedValue) {
        dataSize = expectedValue.get();
        bufs.push_back(boost::asio::buffer(&dataSize, sizeof(dataSize)));
      }

      dataSize = problem->size();
      bufs.push_back(boost::asio::buffer(&dataSize, sizeof(dataSize)));

      if (next < 3) {
        bufs.push_back(boost::asio::buffer(
            reinterpret_cast<const char *>(&problem->getData<int>()),
            dataSize * sizeof(int)));
      } else {
        bufs.push_back(boost::asio::buffer(
            reinterpret_cast<const char *>(&problem->getData<char>()),
            dataSize * sizeof(char)));
      }
      answers[i % 4] = problem->getAnswer();

      boost::asio::async_write(
          mSocket, bufs,
          boost::bind(&TCPConnection::handleWrite, shared_from_this(),
                      boost::asio::placeholders::error,
                      boost::asio::placeholders::bytes_transferred));

      mTimer.expires_from_now(boost::posix_time::seconds(5));
      mTimer.async_wait(boost::bind(&TCPConnection::onDataTimerExpired,
                                    shared_from_this(),
                                    boost::asio::placeholders::error, &mTimer));
    }
  }

  void onDataTimerExpired(const boost::system::error_code &ec,
                          boost::asio::deadline_timer *) {
    if (ec == boost::asio::error::operation_aborted) {
        std::cout << "Abort" << std::endl;
      return;
    }

    std::cout << "Awww.... too slow -" << problemScore << std::endl;
    score -= problemScore;
    sendData();
  }

  void handleWrite(const boost::system::error_code & /*error*/,
                   size_t /*bytes_transferred*/) {
      std::cout << "You have " << score << " points, sending new problem" << std::endl;
  }

private:
  tcp::socket mSocket;
  boost::array<bool, 4> mReadMessage;
  boost::asio::deadline_timer mTimer;
};

class TCPServer {
public:
  TCPServer(boost::asio::io_service &IOService)
      : mAcceptor(IOService, tcp::endpoint(tcp::v4(), 22022)) {
    startAccept();
  }

private:
  void startAccept() {
    TCPConnection::pointer NewConnection =
        TCPConnection::create(mAcceptor.get_io_service());

    mAcceptor.async_accept(NewConnection->socket(),
                           boost::bind(&TCPServer::handleAccept, this,
                                       NewConnection,
                                       boost::asio::placeholders::error));
  }

  void handleAccept(TCPConnection::pointer NewConnection,
                    const boost::system::error_code &error) {
    if (!error) {
      std::cout << "Here we go !" << std::endl;
      NewConnection->start();
    }

    startAccept();
  }

private:
  tcp::acceptor mAcceptor;
};

template <class T>
void readProblem(const std::string &name, ProblemType type, ProblemContainer &problems) {
  std::ifstream file(name, std::ios::in | std::ios::binary);

  file.seekg(0, file.end);
  int length = file.tellg();
  file.seekg(0, file.beg);
  T tempDataType;
  int tempInt;
  int size;
  boost::optional<int> expectedValue = boost::none;

  // Read problem set
  while (length > 0) {
    // Read the flag
    unsigned char flag = file.get();
    --length;

    // Read the expected value if this is a RLE problem
    if (type == RLE) {
        file.read((char *)&tempInt, sizeof(tempInt));
        length -= sizeof(tempInt);
        expectedValue = tempInt;
    }

    // Read the 4 problems
    for (int i = 0; i < 4; ++i) {
      // Read single problem size
      file.read((char *)&tempInt, sizeof(tempInt));
      length -= sizeof(tempInt);
      size = tempInt;

      // Read the expected value if this is an array problem
      if (type == ARRAY) {
        file.read((char *)&tempInt, sizeof(tempInt));
        length -= sizeof(tempInt);
        expectedValue = tempInt;
        --size;
      }

      // Read the problem data
      std::vector<T> problemData;
      problemData.reserve(tempInt);
      for (int j = 0; j < size; ++j) {
        file.read((char *)&tempDataType, sizeof(tempDataType));
        length -= sizeof(tempDataType);
        problemData.push_back(tempDataType);
      }
      problems.addProblem(type, flag & (1 << i), std::move(problemData), expectedValue);
    }
  }
}

int main(int argc, char **argv) {
  readProblem<int>(argc > 1 ? argv[1] : "maze_small.bin", MAZE, problems);
  readProblem<int>(argc > 2 ? argv[2] : "sudoku_small.bin", SUDOKU, problems);
  readProblem<int>(argc > 3 ? argv[3] : "array_small.bin", ARRAY, problems);
  readProblem<char>(argc > 4 ? argv[4] : "password_small.bin", PASSWORD, problems);
  readProblem<int>(argc > 5 ? argv[5] : "tree_small.bin", TREE, problems);
  readProblem<char>(argc > 6 ? argv[6] : "RLE_small.bin", RLE, problems);

  std::cout << problems.getGlobalSize() << " problems loaded" << std::endl;

  try {
    boost::asio::io_service IOService;
    TCPServer Server(IOService);
    if (bool_dist2(e1)) {
      std::cout << base64_decode(not_welcome) << std::endl << std::endl;
      for (;;) {
        for (int t = 0; t < 5; ++t) {
          std::cout << "0xD ";
          std::this_thread::sleep_for(std::chrono::milliseconds(200));
        }
        std::cout << "\t" << go_away[uniform_dist2(e1)] << "\t";
        for (int t = 0; t < 7; ++t) {
          std::cout << " 0xD";
          std::this_thread::sleep_for(std::chrono::milliseconds(200));
        }
        std::cout << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(2000));
      }
    } else if (bool_dist1(e1)) {
      std::cout << base64_decode(welcome) << std::endl;
      std::cout << "...Waiting for \"client\"..." << std::endl;
    } else {
      std::cout << "Waiting for client..." << std::endl;
    }
    IOService.run();
  } catch (std::exception &e) {
    std::cerr << e.what() << std::endl;
  }
  for (int i = 0; i < 5; ++i)
      std::cout << "FINAL SCORE " << score << std::endl;

  return 0;
}
<|endoftext|>


<commit_before>#include "MenuState.h"
#include "GameStateHandler.h"

MenuState::MenuState()
{
}

MenuState::~MenuState()
{
}

int MenuState::ShutDown()
{
	int result = 1;
	return result;
}

int MenuState::Initialize(GameStateHandler * gsh, ComponentHandler* cHandler, Camera* cameraRef)
{
	int result = 0;

	result = GameState::InitializeBase(gsh, cHandler, cameraRef);

	this->m_cHandlerPtr = cHandler;
	this->m_cameraRef = cameraRef;

	//Workaround for camera trying to access nullptr
	this->m_lockTarget = DirectX::XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);
	DirectX::XMVECTOR targetOffset = DirectX::XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);
	float distance = 4.0f;
	this->m_cameraRef->SetCameraPivot(&this->m_lockTarget, targetOffset, distance);

	for (size_t i = 0; i < m_NR_OF_MAIN_MENU_ITEMS; i++)
	{
		this->m_menuButtons[i].m_uiComp = cHandler->GetUIComponent();
		this->m_menuButtons[i].m_uiComp->active = 1;
		this->m_menuButtons[i].m_uiComp->position = DirectX::XMFLOAT2(100.f, 200.f + (i * 150.f));
		this->m_menuButtons[i].m_uiComp->size = DirectX::XMFLOAT2(400.f, 100.f);
		this->m_menuButtons[i].m_textComp = cHandler->GetTextComponent();
		this->m_menuButtons[i].m_textComp->active = 1;
		this->m_menuButtons[i].m_textComp->position = DirectX::XMFLOAT2(125.f, 220.f + (i * 150.f));
	}
	for (size_t i = m_NR_OF_MAIN_MENU_ITEMS; i < m_NR_OF_MENU_ITEMS; i++)
	{
		int newI = i - m_NR_OF_MAIN_MENU_ITEMS;
		this->m_menuButtons[i].m_uiComp = cHandler->GetUIComponent();
		this->m_menuButtons[i].m_uiComp->active = 0;
		this->m_menuButtons[i].m_uiComp->position = DirectX::XMFLOAT2(100.f, 200.f + (newI * 150.f));
		this->m_menuButtons[i].m_uiComp->size = DirectX::XMFLOAT2(400.f, 100.f);
		this->m_menuButtons[i].m_textComp = cHandler->GetTextComponent();
		this->m_menuButtons[i].m_textComp->active = 0;
		this->m_menuButtons[i].m_textComp->position = DirectX::XMFLOAT2(125.f, 220.f + (newI * 150.f));
	}

	this->m_ipTextBox.m_uiComp = cHandler->GetUIComponent();
	this->m_ipTextBox.m_uiComp->active = 0;
	this->m_ipTextBox.m_uiComp->position = DirectX::XMFLOAT2(550.f, 200.f + (150.f));
	this->m_ipTextBox.m_uiComp->size = DirectX::XMFLOAT2(400.f, 100.f);
	this->m_ipTextBox.m_textComp = cHandler->GetTextComponent();
	this->m_ipTextBox.m_textComp->active = 0;
	this->m_ipTextBox.m_textComp->position = DirectX::XMFLOAT2(575.f, 220.f + (150.f));
	this->m_ipTextBox.m_textComp->text = L"Enter IP...";

	this->m_menuButtons[0].m_textComp->text = L"Start Game";
	this->m_menuButtons[2].m_textComp->text = L"Quit Game";
	this->m_menuButtons[1].m_textComp->text = L"Options";

	this->m_menuButtons[3].m_textComp->text = L"Toggle Fullscreen";
	this->m_menuButtons[4].m_textComp->text = L"Go Back";

	this->m_markedItem = 0;
	this->m_menuButtons[0].SetHovered(true);

	return result;
}

int MenuState::Update(float dt, InputHandler * inputHandler)
{
	int result = 1;

	DirectX::XMFLOAT2 mousePos = inputHandler->GetMousePos();

	switch (this->m_menuState)
	{
	case 0: //Main menu
		for (size_t i = 0; i < m_NR_OF_MAIN_MENU_ITEMS; i++)
		{
			this->m_menuButtons[i].m_uiComp->UpdateHover(mousePos);
			if (this->m_menuButtons[i].m_uiComp->isHovered)
			{
				this->m_menuButtons[i].SetHovered(true);
				this->m_markedItem = i;
			}
			else if (i != this->m_markedItem)
			{
				this->m_menuButtons[i].SetHovered(false);
			}
		}

		if (inputHandler->IsMouseKeyReleased(SDL_BUTTON_LEFT))
		{
			for (size_t i = 0; i < m_NR_OF_MAIN_MENU_ITEMS; i++)
			{
				this->m_menuButtons[i].m_uiComp->UpdateClicked(mousePos);
			}
		}
		if (inputHandler->IsKeyPressed(SDL_SCANCODE_RETURN))
		{
			this->m_menuButtons[m_markedItem].m_uiComp->wasClicked = true;
		}
		if (inputHandler->IsKeyPressed(SDL_SCANCODE_DOWN))
		{
			if (this->m_markedItem < this->m_NR_OF_MAIN_MENU_ITEMS - 1)
			{
				this->m_menuButtons[this->m_markedItem].SetHovered(false);
				this->m_markedItem++;
				this->m_menuButtons[this->m_markedItem].SetHovered(true);
			}
		}
		if (inputHandler->IsKeyPressed(SDL_SCANCODE_UP))
		{
			if (this->m_markedItem > 0)
			{
				this->m_menuButtons[this->m_markedItem].SetHovered(false);
				this->m_markedItem--;
				this->m_menuButtons[this->m_markedItem].SetHovered(true);
			}
		}

		if (this->m_menuButtons[0].m_uiComp->CheckClicked())
		{
			//Start game was clicked

			//Create, Initialize and push a LevelSelectState
			LevelSelectState* levelSelect = new LevelSelectState();
			result = levelSelect->Initialize(this->m_gsh, this->m_cHandlerPtr, this->m_cameraRef);

			//If the initialization was successful
			if (result > 0)
			{
				//Push it to the gamestate stack/vector
				this->m_gsh->PushStateToStack(levelSelect);

				levelSelect->LoadLevel(std::string("../ResourceLib/AssetFiles/TestingLevel.level"));
			}
			else
			{
				//Delete it
				delete levelSelect;
				levelSelect = nullptr;
			}

			for (size_t i = 0; i < m_NR_OF_MAIN_MENU_ITEMS; i++)
			{
				this->m_menuButtons[i].SetActive(false);
			}
		}
		else if (this->m_menuButtons[1].m_uiComp->CheckClicked())
		{
			//Options was clicked
			this->m_menuButtons[this->m_markedItem].SetHovered(false);
			this->m_markedItem = this->m_NR_OF_MAIN_MENU_ITEMS;
			this->m_menuButtons[this->m_NR_OF_MAIN_MENU_ITEMS].SetHovered(true);
			this->m_menuState = 1;
			for (size_t i = 0; i < m_NR_OF_MAIN_MENU_ITEMS; i++)
			{
				this->m_menuButtons[i].SetActive(false);
			}
			for (size_t i = m_NR_OF_MAIN_MENU_ITEMS; i < m_NR_OF_MENU_ITEMS; i++)
			{
				this->m_menuButtons[i].SetActive(true);
			}
			this->m_ipTextBox.SetActive(true);
		}
		else if (this->m_menuButtons[2].m_uiComp->CheckClicked())
		{
			//Quit game was clicked
			result = 0;
		}
		break;

	case 1: //Options Menu
		for (size_t i = m_NR_OF_MAIN_MENU_ITEMS; i < m_NR_OF_OPTIONS_MENU_ITEMS; i++)
		{
			this->m_menuButtons[i].m_uiComp->UpdateHover(mousePos);
			if (this->m_menuButtons[i].m_uiComp->isHovered)
			{
				this->m_menuButtons[i].SetHovered(true);
				this->m_markedItem = i;
			}
			else if (i != this->m_markedItem)
			{
				this->m_menuButtons[i].SetHovered(false);
			}
		}

		if (inputHandler->IsMouseKeyReleased(SDL_BUTTON_LEFT))
		{
			for (size_t i = m_NR_OF_MAIN_MENU_ITEMS; i < m_NR_OF_OPTIONS_MENU_ITEMS; i++)
			{
				this->m_menuButtons[i].m_uiComp->UpdateClicked(mousePos);
			}
			this->m_ipTextBox.m_uiComp->UpdateClicked(mousePos);
		}
		if (inputHandler->IsKeyPressed(SDL_SCANCODE_RETURN) || inputHandler->IsKeyPressed(SDL_SCANCODE_KP_ENTER))
		{
			if (!this->m_ipTextBox.m_focused)
			{
				this->m_menuButtons[m_markedItem].m_uiComp->wasClicked = true;
			}
			else
			{
				this->m_ipTextBox.SetFocused(false);
				this->m_markedItem--;
				this->m_menuButtons[this->m_markedItem].SetHovered(true);
			}
		}
		if (inputHandler->IsKeyPressed(SDL_SCANCODE_DOWN))
		{
			if (this->m_markedItem < this->m_NR_OF_MENU_ITEMS)
			{
				this->m_menuButtons[this->m_markedItem].SetHovered(false);
				this->m_markedItem++;
				if (this->m_markedItem != this->m_NR_OF_MENU_ITEMS)
				{
					this->m_menuButtons[this->m_markedItem].SetHovered(true);
				}
				else
				{
					this->m_ipTextBox.SetFocused(true);
				}
			}
		}
		if (inputHandler->IsKeyPressed(SDL_SCANCODE_UP))
		{
			if (this->m_markedItem > this->m_NR_OF_MAIN_MENU_ITEMS)
			{
				if (this->m_markedItem < this->m_NR_OF_MENU_ITEMS)
				{
					this->m_menuButtons[this->m_markedItem].SetHovered(false);
				}
				else
				{
					this->m_ipTextBox.SetFocused(false);
				}
				this->m_markedItem--;
				this->m_menuButtons[this->m_markedItem].SetHovered(true);
			}
		}

		if (this->m_menuButtons[3].m_uiComp->CheckClicked())
		{
			//Toggle fullscreen was clicked
			//Cheating by telling the system the user pressed F
			inputHandler->SetKeyState(SDL_SCANCODE_F, true); //Seems this does not reset, ever
		}
		else if (this->m_menuButtons[4].m_uiComp->CheckClicked())
		{
			//Return to main menu was clicked
			inputHandler->SetKeyState(SDL_SCANCODE_F, false); //Temporary cheat
			this->m_menuButtons[this->m_markedItem].SetHovered(false);
			this->m_markedItem = 0;
			this->m_menuButtons[0].SetHovered(true);
			this->m_menuState = 0;
			for (size_t i = 0; i < m_NR_OF_MAIN_MENU_ITEMS; i++)
			{
				this->m_menuButtons[i].SetActive(true);
			}
			for (size_t i = m_NR_OF_MAIN_MENU_ITEMS; i < m_NR_OF_MENU_ITEMS; i++)
			{
				this->m_menuButtons[i].SetActive(false);
			}
			this->m_ipTextBox.SetActive(false);
		}

		if (this->m_ipTextBox.m_focused)
		{
			if (inputHandler->IsKeyPressed(SDL_SCANCODE_BACKSPACE))
			{
				this->m_ipTextBox.RemoveChar();
			}
			if (inputHandler->IsKeyPressed(SDL_SCANCODE_1) || inputHandler->IsKeyPressed(SDL_SCANCODE_KP_1))
			{
				this->m_ipTextBox.AddChar(L"1");
			}
			if (inputHandler->IsKeyPressed(SDL_SCANCODE_2) || inputHandler->IsKeyPressed(SDL_SCANCODE_KP_2))
			{
				this->m_ipTextBox.AddChar(L"2");
			}
			if (inputHandler->IsKeyPressed(SDL_SCANCODE_3) || inputHandler->IsKeyPressed(SDL_SCANCODE_KP_3))
			{
				this->m_ipTextBox.AddChar(L"3");
			}
			if (inputHandler->IsKeyPressed(SDL_SCANCODE_4) || inputHandler->IsKeyPressed(SDL_SCANCODE_KP_4))
			{
				this->m_ipTextBox.AddChar(L"4");
			}
			if (inputHandler->IsKeyPressed(SDL_SCANCODE_5) || inputHandler->IsKeyPressed(SDL_SCANCODE_KP_5))
			{
				this->m_ipTextBox.AddChar(L"5");
			}
			if (inputHandler->IsKeyPressed(SDL_SCANCODE_6) || inputHandler->IsKeyPressed(SDL_SCANCODE_KP_6))
			{
				this->m_ipTextBox.AddChar(L"6");
			}
			if (inputHandler->IsKeyPressed(SDL_SCANCODE_7) || inputHandler->IsKeyPressed(SDL_SCANCODE_KP_7))
			{
				this->m_ipTextBox.AddChar(L"7");
			}
			if (inputHandler->IsKeyPressed(SDL_SCANCODE_8) || inputHandler->IsKeyPressed(SDL_SCANCODE_KP_8))
			{
				this->m_ipTextBox.AddChar(L"8");
			}
			if (inputHandler->IsKeyPressed(SDL_SCANCODE_9) || inputHandler->IsKeyPressed(SDL_SCANCODE_KP_9))
			{
				this->m_ipTextBox.AddChar(L"9");
			}
			if (inputHandler->IsKeyPressed(SDL_SCANCODE_0) || inputHandler->IsKeyPressed(SDL_SCANCODE_KP_0))
			{
				this->m_ipTextBox.AddChar(L"0");
			}
			if (inputHandler->IsKeyPressed(SDL_SCANCODE_PERIOD))
			{
				this->m_ipTextBox.AddChar(L".");
			}
		}

		break;
	default:
		break;
	}

	return result;
}
<commit_msg>ADD mouse activation for ip text box<commit_after>#include "MenuState.h"
#include "GameStateHandler.h"

MenuState::MenuState()
{
}

MenuState::~MenuState()
{
}

int MenuState::ShutDown()
{
	int result = 1;
	return result;
}

int MenuState::Initialize(GameStateHandler * gsh, ComponentHandler* cHandler, Camera* cameraRef)
{
	int result = 0;

	result = GameState::InitializeBase(gsh, cHandler, cameraRef);

	this->m_cHandlerPtr = cHandler;
	this->m_cameraRef = cameraRef;

	//Workaround for camera trying to access nullptr
	this->m_lockTarget = DirectX::XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);
	DirectX::XMVECTOR targetOffset = DirectX::XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);
	float distance = 4.0f;
	this->m_cameraRef->SetCameraPivot(&this->m_lockTarget, targetOffset, distance);

	for (size_t i = 0; i < m_NR_OF_MAIN_MENU_ITEMS; i++)
	{
		this->m_menuButtons[i].m_uiComp = cHandler->GetUIComponent();
		this->m_menuButtons[i].m_uiComp->active = 1;
		this->m_menuButtons[i].m_uiComp->position = DirectX::XMFLOAT2(100.f, 200.f + (i * 150.f));
		this->m_menuButtons[i].m_uiComp->size = DirectX::XMFLOAT2(400.f, 100.f);
		this->m_menuButtons[i].m_textComp = cHandler->GetTextComponent();
		this->m_menuButtons[i].m_textComp->active = 1;
		this->m_menuButtons[i].m_textComp->position = DirectX::XMFLOAT2(125.f, 220.f + (i * 150.f));
	}
	for (size_t i = m_NR_OF_MAIN_MENU_ITEMS; i < m_NR_OF_MENU_ITEMS; i++)
	{
		int newI = i - m_NR_OF_MAIN_MENU_ITEMS;
		this->m_menuButtons[i].m_uiComp = cHandler->GetUIComponent();
		this->m_menuButtons[i].m_uiComp->active = 0;
		this->m_menuButtons[i].m_uiComp->position = DirectX::XMFLOAT2(100.f, 200.f + (newI * 150.f));
		this->m_menuButtons[i].m_uiComp->size = DirectX::XMFLOAT2(400.f, 100.f);
		this->m_menuButtons[i].m_textComp = cHandler->GetTextComponent();
		this->m_menuButtons[i].m_textComp->active = 0;
		this->m_menuButtons[i].m_textComp->position = DirectX::XMFLOAT2(125.f, 220.f + (newI * 150.f));
	}

	this->m_ipTextBox.m_uiComp = cHandler->GetUIComponent();
	this->m_ipTextBox.m_uiComp->active = 0;
	this->m_ipTextBox.m_uiComp->position = DirectX::XMFLOAT2(550.f, 200.f + (150.f));
	this->m_ipTextBox.m_uiComp->size = DirectX::XMFLOAT2(400.f, 100.f);
	this->m_ipTextBox.m_textComp = cHandler->GetTextComponent();
	this->m_ipTextBox.m_textComp->active = 0;
	this->m_ipTextBox.m_textComp->position = DirectX::XMFLOAT2(575.f, 220.f + (150.f));
	this->m_ipTextBox.m_textComp->text = L"Enter IP...";

	this->m_menuButtons[0].m_textComp->text = L"Start Game";
	this->m_menuButtons[2].m_textComp->text = L"Quit Game";
	this->m_menuButtons[1].m_textComp->text = L"Options";

	this->m_menuButtons[3].m_textComp->text = L"Toggle Fullscreen";
	this->m_menuButtons[4].m_textComp->text = L"Go Back";

	this->m_markedItem = 0;
	this->m_menuButtons[0].SetHovered(true);

	return result;
}

int MenuState::Update(float dt, InputHandler * inputHandler)
{
	int result = 1;

	DirectX::XMFLOAT2 mousePos = inputHandler->GetMousePos();

	switch (this->m_menuState)
	{
	case 0: //Main menu
		for (size_t i = 0; i < m_NR_OF_MAIN_MENU_ITEMS; i++)
		{
			this->m_menuButtons[i].m_uiComp->UpdateHover(mousePos);
			if (this->m_menuButtons[i].m_uiComp->isHovered)
			{
				this->m_menuButtons[i].SetHovered(true);
				this->m_markedItem = i;
			}
			else if (i != this->m_markedItem)
			{
				this->m_menuButtons[i].SetHovered(false);
			}
		}

		if (inputHandler->IsMouseKeyReleased(SDL_BUTTON_LEFT))
		{
			for (size_t i = 0; i < m_NR_OF_MAIN_MENU_ITEMS; i++)
			{
				this->m_menuButtons[i].m_uiComp->UpdateClicked(mousePos);
			}
		}
		if (inputHandler->IsKeyPressed(SDL_SCANCODE_RETURN))
		{
			this->m_menuButtons[m_markedItem].m_uiComp->wasClicked = true;
		}
		if (inputHandler->IsKeyPressed(SDL_SCANCODE_DOWN))
		{
			if (this->m_markedItem < this->m_NR_OF_MAIN_MENU_ITEMS - 1)
			{
				this->m_menuButtons[this->m_markedItem].SetHovered(false);
				this->m_markedItem++;
				this->m_menuButtons[this->m_markedItem].SetHovered(true);
			}
		}
		if (inputHandler->IsKeyPressed(SDL_SCANCODE_UP))
		{
			if (this->m_markedItem > 0)
			{
				this->m_menuButtons[this->m_markedItem].SetHovered(false);
				this->m_markedItem--;
				this->m_menuButtons[this->m_markedItem].SetHovered(true);
			}
		}

		if (this->m_menuButtons[0].m_uiComp->CheckClicked())
		{
			//Start game was clicked

			//Create, Initialize and push a LevelSelectState
			LevelSelectState* levelSelect = new LevelSelectState();
			result = levelSelect->Initialize(this->m_gsh, this->m_cHandlerPtr, this->m_cameraRef);

			//If the initialization was successful
			if (result > 0)
			{
				//Push it to the gamestate stack/vector
				this->m_gsh->PushStateToStack(levelSelect);

				levelSelect->LoadLevel(std::string("../ResourceLib/AssetFiles/TestingLevel.level"));
			}
			else
			{
				//Delete it
				delete levelSelect;
				levelSelect = nullptr;
			}

			for (size_t i = 0; i < m_NR_OF_MAIN_MENU_ITEMS; i++)
			{
				this->m_menuButtons[i].SetActive(false);
			}
		}
		else if (this->m_menuButtons[1].m_uiComp->CheckClicked())
		{
			//Options was clicked
			this->m_menuButtons[this->m_markedItem].SetHovered(false);
			this->m_markedItem = this->m_NR_OF_MAIN_MENU_ITEMS;
			this->m_menuButtons[this->m_NR_OF_MAIN_MENU_ITEMS].SetHovered(true);
			this->m_menuState = 1;
			for (size_t i = 0; i < m_NR_OF_MAIN_MENU_ITEMS; i++)
			{
				this->m_menuButtons[i].SetActive(false);
			}
			for (size_t i = m_NR_OF_MAIN_MENU_ITEMS; i < m_NR_OF_MENU_ITEMS; i++)
			{
				this->m_menuButtons[i].SetActive(true);
			}
			this->m_ipTextBox.SetActive(true);
		}
		else if (this->m_menuButtons[2].m_uiComp->CheckClicked())
		{
			//Quit game was clicked
			result = 0;
		}
		break;

	case 1: //Options Menu
		for (size_t i = m_NR_OF_MAIN_MENU_ITEMS; i < m_NR_OF_OPTIONS_MENU_ITEMS; i++)
		{
			this->m_menuButtons[i].m_uiComp->UpdateHover(mousePos);
			if (this->m_menuButtons[i].m_uiComp->isHovered)
			{
				this->m_menuButtons[i].SetHovered(true);
				this->m_markedItem = i;
				this->m_ipTextBox.SetFocused(false);
			}
			else if (i != this->m_markedItem)
			{
				this->m_menuButtons[i].SetHovered(false);
			}
		}

		if (inputHandler->IsMouseKeyReleased(SDL_BUTTON_LEFT))
		{
			for (size_t i = m_NR_OF_MAIN_MENU_ITEMS; i < m_NR_OF_OPTIONS_MENU_ITEMS; i++)
			{
				this->m_menuButtons[i].m_uiComp->UpdateClicked(mousePos);
			}
			this->m_ipTextBox.m_uiComp->UpdateClicked(mousePos);
		}
		if (inputHandler->IsKeyPressed(SDL_SCANCODE_RETURN) || inputHandler->IsKeyPressed(SDL_SCANCODE_KP_ENTER))
		{
			if (!this->m_ipTextBox.m_focused)
			{
				this->m_menuButtons[m_markedItem].m_uiComp->wasClicked = true;
			}
			else
			{
				this->m_ipTextBox.SetFocused(false);
				this->m_markedItem--;
				this->m_menuButtons[this->m_markedItem].SetHovered(true);
			}
		}
		if (inputHandler->IsKeyPressed(SDL_SCANCODE_DOWN))
		{
			if (this->m_markedItem < this->m_NR_OF_MENU_ITEMS)
			{
				this->m_menuButtons[this->m_markedItem].SetHovered(false);
				this->m_markedItem++;
				if (this->m_markedItem != this->m_NR_OF_MENU_ITEMS)
				{
					this->m_menuButtons[this->m_markedItem].SetHovered(true);
				}
				else
				{
					this->m_ipTextBox.SetFocused(true);
				}
			}
		}
		if (inputHandler->IsKeyPressed(SDL_SCANCODE_UP))
		{
			if (this->m_markedItem > this->m_NR_OF_MAIN_MENU_ITEMS)
			{
				if (this->m_markedItem < this->m_NR_OF_MENU_ITEMS)
				{
					this->m_menuButtons[this->m_markedItem].SetHovered(false);
				}
				else
				{
					this->m_ipTextBox.SetFocused(false);
				}
				this->m_markedItem--;
				this->m_menuButtons[this->m_markedItem].SetHovered(true);
			}
		}

		if (this->m_menuButtons[3].m_uiComp->CheckClicked())
		{
			//Toggle fullscreen was clicked
			//Cheating by telling the system the user pressed F
			inputHandler->SetKeyState(SDL_SCANCODE_F, true); //Seems this does not reset, ever
		}
		else if (this->m_menuButtons[4].m_uiComp->CheckClicked())
		{
			//Return to main menu was clicked
			inputHandler->SetKeyState(SDL_SCANCODE_F, false); //Temporary cheat
			this->m_menuButtons[this->m_markedItem].SetHovered(false);
			this->m_markedItem = 0;
			this->m_menuButtons[0].SetHovered(true);
			this->m_menuState = 0;
			for (size_t i = 0; i < m_NR_OF_MAIN_MENU_ITEMS; i++)
			{
				this->m_menuButtons[i].SetActive(true);
			}
			for (size_t i = m_NR_OF_MAIN_MENU_ITEMS; i < m_NR_OF_MENU_ITEMS; i++)
			{
				this->m_menuButtons[i].SetActive(false);
			}
			this->m_ipTextBox.SetActive(false);
		}
		else if (this->m_ipTextBox.m_uiComp->CheckClicked())
		{
			//IP text box was clicked
			this->m_ipTextBox.SetFocused(true);
		}

		if (this->m_ipTextBox.m_focused)
		{
			if (inputHandler->IsKeyPressed(SDL_SCANCODE_BACKSPACE))
			{
				this->m_ipTextBox.RemoveChar();
			}
			if (inputHandler->IsKeyPressed(SDL_SCANCODE_1) || inputHandler->IsKeyPressed(SDL_SCANCODE_KP_1))
			{
				this->m_ipTextBox.AddChar(L"1");
			}
			if (inputHandler->IsKeyPressed(SDL_SCANCODE_2) || inputHandler->IsKeyPressed(SDL_SCANCODE_KP_2))
			{
				this->m_ipTextBox.AddChar(L"2");
			}
			if (inputHandler->IsKeyPressed(SDL_SCANCODE_3) || inputHandler->IsKeyPressed(SDL_SCANCODE_KP_3))
			{
				this->m_ipTextBox.AddChar(L"3");
			}
			if (inputHandler->IsKeyPressed(SDL_SCANCODE_4) || inputHandler->IsKeyPressed(SDL_SCANCODE_KP_4))
			{
				this->m_ipTextBox.AddChar(L"4");
			}
			if (inputHandler->IsKeyPressed(SDL_SCANCODE_5) || inputHandler->IsKeyPressed(SDL_SCANCODE_KP_5))
			{
				this->m_ipTextBox.AddChar(L"5");
			}
			if (inputHandler->IsKeyPressed(SDL_SCANCODE_6) || inputHandler->IsKeyPressed(SDL_SCANCODE_KP_6))
			{
				this->m_ipTextBox.AddChar(L"6");
			}
			if (inputHandler->IsKeyPressed(SDL_SCANCODE_7) || inputHandler->IsKeyPressed(SDL_SCANCODE_KP_7))
			{
				this->m_ipTextBox.AddChar(L"7");
			}
			if (inputHandler->IsKeyPressed(SDL_SCANCODE_8) || inputHandler->IsKeyPressed(SDL_SCANCODE_KP_8))
			{
				this->m_ipTextBox.AddChar(L"8");
			}
			if (inputHandler->IsKeyPressed(SDL_SCANCODE_9) || inputHandler->IsKeyPressed(SDL_SCANCODE_KP_9))
			{
				this->m_ipTextBox.AddChar(L"9");
			}
			if (inputHandler->IsKeyPressed(SDL_SCANCODE_0) || inputHandler->IsKeyPressed(SDL_SCANCODE_KP_0))
			{
				this->m_ipTextBox.AddChar(L"0");
			}
			if (inputHandler->IsKeyPressed(SDL_SCANCODE_PERIOD))
			{
				this->m_ipTextBox.AddChar(L".");
			}
		}

		break;
	default:
		break;
	}

	return result;
}
<|endoftext|>


<commit_before>#include "editor.hh"

#include "exception.hh"
#include "utils.hh"
#include "register.hh"
#include "register_manager.hh"

#include "utf8_iterator.hh"

#include <array>

namespace Kakoune
{

Editor::Editor(Buffer& buffer)
    : m_buffer(&buffer),
      m_edition_level(0),
      m_selections(buffer)
{
    m_selections.push_back(Selection(buffer.begin(), buffer.begin()));
}

void Editor::erase()
{
    scoped_edition edition(*this);
    for (auto& sel : m_selections)
    {
        m_buffer->erase(sel.begin(), sel.end());
        sel.avoid_eol();
    }
}

static BufferIterator prepare_insert(Buffer& buffer, const Selection& sel,
                                     InsertMode mode)
{
    switch (mode)
    {
    case InsertMode::Insert:
    case InsertMode::Replace:
        return sel.begin();
    case InsertMode::Append:
    {
        // special case for end of lines, append to current line instead
        auto pos = std::max(sel.first(), sel.last());
        if (pos.column() == buffer.line_length(pos.line()) - 1)
            return pos;
        else
            return pos+1;
    }
    case InsertMode::InsertAtLineBegin:
        return buffer.iterator_at_line_begin(sel.begin());
    case InsertMode::AppendAtLineEnd:
        return buffer.iterator_at_line_end(sel.end()-1)-1;
    case InsertMode::InsertAtNextLineBegin:
        return buffer.iterator_at_line_end(sel.end()-1);
    case InsertMode::OpenLineBelow:
    {
        LineCount line = (sel.end() - 1).line();
        buffer.insert(buffer.iterator_at_line_end(line), "\n");
        return buffer.iterator_at_line_begin(line + 1);
    }
    case InsertMode::OpenLineAbove:
    {
        auto pos = buffer.iterator_at_line_begin(sel.begin());
        buffer.insert(pos, "\n");
        return pos;
    }
    }
    assert(false);
    return BufferIterator{};
}

void Editor::insert(const String& string, InsertMode mode)
{
    scoped_edition edition(*this);

    if (mode == InsertMode::Replace)
    {
        // do not call Editor::erase as we do not want to avoid end of lines
        for (auto& sel : m_selections)
            m_buffer->erase(sel.begin(), sel.end());
    }

    for (auto& sel : m_selections)
    {
        BufferIterator pos = prepare_insert(*m_buffer, sel, mode);
        m_buffer->insert(pos, string);
        if (mode == InsertMode::Replace)
        {
            sel.first() = pos;
            sel.last()  = pos + (string.empty() ? 0 : string.length() - 1 );
        }
        sel.avoid_eol();
    }
}

void Editor::insert(const memoryview<String>& strings, InsertMode mode)
{
    scoped_edition edition(*this);
    if (mode == InsertMode::Replace)
    {
        // do not call Editor::erase as we do not want to avoid end of lines
        for (auto& sel : m_selections)
            m_buffer->erase(sel.begin(), sel.end());
    }
    if (strings.empty())
        return;

    for (size_t i = 0; i < selections().size(); ++i)
    {
        auto& sel = m_selections[i];
        BufferIterator pos = prepare_insert(*m_buffer, sel, mode);
        const String& str = strings[std::min(i, strings.size()-1)];
        m_buffer->insert(pos, str);
        if (mode == InsertMode::Replace)
        {
            sel.first() = pos;
            sel.last()  = pos + (str.empty() ? 0 : str.length() - 1);
        }
        sel.avoid_eol();
    }
}

std::vector<String> Editor::selections_content() const
{
    std::vector<String> contents;
    for (auto& sel : m_selections)
        contents.push_back(m_buffer->string(sel.begin(),
                                           sel.end()));
    return contents;
}

static void sort_and_merge_overlapping(SelectionList& selections)
{
    std::sort(selections.begin(), selections.end(),
              [](const Selection& lhs, const Selection& rhs)
              { return lhs.begin() < rhs.begin(); });
    for (size_t i = 0; i < selections.size()-1;)
    {
        if (overlaps(selections[i], selections[i+1]))
        {
            selections[i].merge_with(selections[i+1]);
            selections.erase(selections.begin() + i + 1);
        }
        else
           ++i;
    }
}

void Editor::move_selections(CharCount offset, SelectMode mode)
{
    assert(mode == SelectMode::Replace or mode == SelectMode::Extend);
    for (auto& sel : m_selections)
    {
        auto last = sel.last();
        auto limit = offset < 0 ? buffer().iterator_at_line_begin(last)
                                : utf8::previous(buffer().iterator_at_line_end(last));
        last = utf8::advance(last, limit, offset);
        sel = Selection(mode == SelectMode::Extend ? sel.first() : last, last);
        sel.avoid_eol();
    }
    sort_and_merge_overlapping(m_selections);
}

void Editor::move_selections(LineCount offset, SelectMode mode)
{
    assert(mode == SelectMode::Replace or mode == SelectMode::Extend);
    for (auto& sel : m_selections)
    {
        BufferCoord pos = sel.last().coord();
        pos.line += offset;
        BufferIterator last = utf8::finish(m_buffer->iterator_at(pos, true));
        sel = Selection(mode == SelectMode::Extend ? sel.first() : last, last);
        sel.avoid_eol();
    }
    sort_and_merge_overlapping(m_selections);
}

void Editor::clear_selections()
{
    BufferIterator pos = m_selections.back().last();

    if (*pos == '\n' and not pos.is_begin() and *utf8::previous(pos) != '\n')
        pos = utf8::previous(pos);

    Selection sel = Selection(pos, pos);
    m_selections = SelectionList{ std::move(sel) };
    check_invariant();
}

void Editor::flip_selections()
{
    for (auto& sel : m_selections)
        std::swap(sel.first(), sel.last());
    check_invariant();
}

void Editor::keep_selection(int index)
{
    if (index < m_selections.size())
        m_selections = SelectionList{ std::move(m_selections[index]) };
    check_invariant();
}

void Editor::remove_selection(int index)
{
    if (m_selections.size() > 1 and index < m_selections.size())
        m_selections.erase(m_selections.begin() + index);
    check_invariant();
}

void Editor::select(const Selection& selection, SelectMode mode)
{
    if (mode == SelectMode::Replace)
        m_selections = SelectionList{ selection };
    else if (mode == SelectMode::Extend)
    {
        for (auto& sel : m_selections)
            sel.merge_with(selection);
        sort_and_merge_overlapping(m_selections);
    }
    else if (mode == SelectMode::Append)
    {
        m_selections.push_back(selection);
        sort_and_merge_overlapping(m_selections);
    }
    check_invariant();
}

void Editor::select(SelectionList selections)
{
    if (selections.empty())
        throw runtime_error("no selections");
    m_selections = std::move(selections);
    check_invariant();
}

void Editor::select(const Selector& selector, SelectMode mode)
{
    if (mode == SelectMode::Append)
    {
        auto& sel = m_selections.back();
        Selection res = selector(sel);
        if (res.captures().empty())
            res.captures() = sel.captures();
        m_selections.push_back(res);
    }
    else
    {
        for (auto& sel : m_selections)
        {
            Selection res = selector(sel);
            if (mode == SelectMode::Extend)
                sel.merge_with(res);
            else
                sel = std::move(res);

            if (not res.captures().empty())
                sel.captures() = std::move(res.captures());
        }
    }
    sort_and_merge_overlapping(m_selections);
    check_invariant();
}

struct nothing_selected : public runtime_error
{
    nothing_selected() : runtime_error("nothing was selected") {}
};

void Editor::multi_select(const MultiSelector& selector)
{
    SelectionList new_selections;
    for (auto& sel : m_selections)
    {
        SelectionList res = selector(sel);
        for (auto& new_sel : res)
        {
            // preserve captures when selectors captures nothing.
            if (new_sel.captures().empty())
                new_selections.emplace_back(new_sel.first(), new_sel.last(),
                                            sel.captures());
            else
                new_selections.push_back(std::move(new_sel));
        }
    }
    if (new_selections.empty())
        throw nothing_selected();
    sort_and_merge_overlapping(new_selections);
    m_selections = std::move(new_selections);
    check_invariant();
}

class LastModifiedRangeListener : public BufferChangeListener
{
public:
    LastModifiedRangeListener(Buffer& buffer)
       : m_buffer(buffer)
    { m_buffer.add_change_listener(*this); }

    ~LastModifiedRangeListener()
    { m_buffer.remove_change_listener(*this); }

    void on_insert(const BufferIterator& begin, const BufferIterator& end)
    {
        m_first = begin;
        m_last = utf8::previous(end);
    }

    void on_erase(const BufferIterator& begin, const BufferIterator& end)
    {
        m_first = begin;
        if (m_first >= m_buffer.end())
            m_first = utf8::previous(m_buffer.end());
        m_last = m_first;
    }

    const BufferIterator& first() const { return m_first; }
    const BufferIterator& last() const { return m_last; }

private:
    BufferIterator m_first;
    BufferIterator m_last;
    Buffer& m_buffer;
};

bool Editor::undo()
{
    LastModifiedRangeListener listener(buffer());
    bool res = m_buffer->undo();
    if (res)
        m_selections = SelectionList{ {listener.first(), listener.last()} };
    check_invariant();
    return res;
}

bool Editor::redo()
{
    LastModifiedRangeListener listener(buffer());
    bool res = m_buffer->redo();
    if (res)
        m_selections = SelectionList{ {listener.first(), listener.last()} };
    check_invariant();
    return res;
}

void Editor::check_invariant() const
{
    assert(not m_selections.empty());
    m_selections.check_invariant();
}

void Editor::begin_edition()
{
    ++m_edition_level;

    if (m_edition_level == 1)
        m_buffer->begin_undo_group();
}

void Editor::end_edition()
{
    assert(m_edition_level > 0);
    if (m_edition_level == 1)
        m_buffer->end_undo_group();

    --m_edition_level;
}

using utf8_it = utf8::utf8_iterator<BufferIterator, utf8::InvalidBytePolicy::Pass>;

IncrementalInserter::IncrementalInserter(Editor& editor, InsertMode mode)
    : m_editor(editor), m_edition(editor), m_mode(mode)
{
    m_editor.on_incremental_insertion_begin();
    Buffer& buffer = *editor.m_buffer;

    if (mode == InsertMode::Replace)
    {
        for (auto& sel : editor.m_selections)
            buffer.erase(sel.begin(), sel.end());
    }

    for (auto& sel : m_editor.m_selections)
    {
        utf8_it first, last;
        switch (mode)
        {
        case InsertMode::Insert:  first = utf8_it(sel.end()) - 1; last = sel.begin(); break;
        case InsertMode::Replace: first = utf8_it(sel.end()) - 1; last = sel.begin(); break;
        case InsertMode::Append:
        {
            first = sel.begin();
            last  = std::max(sel.first(), sel.last());
            // special case for end of lines, append to current line instead
            auto coord = last.underlying_iterator().coord();
            if (coord.column != buffer.line_length(coord.line) - 1)
                ++last;
            break;
        }

        case InsertMode::OpenLineBelow:
        case InsertMode::AppendAtLineEnd:
            first = utf8_it(buffer.iterator_at_line_end(utf8::previous(sel.end()))) - 1;
            last  = first;
            break;

        case InsertMode::OpenLineAbove:
        case InsertMode::InsertAtLineBegin:
            first = buffer.iterator_at_line_begin(sel.begin());
            if (mode == InsertMode::OpenLineAbove)
                --first;
            else
            {
                auto first_non_blank = first;
                while (*first_non_blank == ' ' or *first_non_blank == '\t')
                    ++first_non_blank;
                if (*first_non_blank != '\n')
                    first = first_non_blank;
            }
            last = first;
            break;
        }
        if (first.underlying_iterator().is_end())
           --first;
        if (last.underlying_iterator().is_end())
           --last;
        sel.first() = first.underlying_iterator();
        sel.last()  = last.underlying_iterator();
    }
    if (mode == InsertMode::OpenLineBelow or mode == InsertMode::OpenLineAbove)
    {
        insert("\n");
        if (mode == InsertMode::OpenLineAbove)
        {
            for (auto& sel : m_editor.m_selections)
            {
                // special case, the --first line above did nothing, so we need to compensate now
                if (sel.first() == utf8::next(buffer.begin()))
                    sel.first() = sel.last() = buffer.begin();
            }
        }
    }
    editor.check_invariant();
}

IncrementalInserter::~IncrementalInserter()
{
    for (auto& sel : m_editor.m_selections)
    {
        if (m_mode == InsertMode::Append and sel.last().column() > 0)
            sel.last() = utf8::previous(sel.last());
         sel.avoid_eol();
    }

    m_editor.on_incremental_insertion_end();
}

void IncrementalInserter::insert(String content)
{
    Buffer& buffer = m_editor.buffer();
    for (auto& sel : m_editor.m_selections)
    {
        m_editor.filters()(buffer, sel, content);
        buffer.insert(sel.last(), content);
    }
}

void IncrementalInserter::insert(const memoryview<String>& strings)
{
    for (size_t i = 0; i < m_editor.m_selections.size(); ++i)
    {
        size_t index = std::min(i, strings.size()-1);
        m_editor.buffer().insert(m_editor.m_selections[i].last(),
                                 strings[index]);
    }
}

void IncrementalInserter::erase()
{
    for (auto& sel : m_editor.m_selections)
    {
        BufferIterator pos = sel.last();
        m_editor.buffer().erase(utf8::previous(pos), pos);
    }
}

void IncrementalInserter::move_cursors(CharCount move)
{
    m_editor.move_selections(move, SelectMode::Replace);
}

void IncrementalInserter::move_cursors(LineCount move)
{
    m_editor.move_selections(move, SelectMode::Replace);
}

}
<commit_msg>Editor: preserve captures accross movements<commit_after>#include "editor.hh"

#include "exception.hh"
#include "utils.hh"
#include "register.hh"
#include "register_manager.hh"

#include "utf8_iterator.hh"

#include <array>

namespace Kakoune
{

Editor::Editor(Buffer& buffer)
    : m_buffer(&buffer),
      m_edition_level(0),
      m_selections(buffer)
{
    m_selections.push_back(Selection(buffer.begin(), buffer.begin()));
}

void Editor::erase()
{
    scoped_edition edition(*this);
    for (auto& sel : m_selections)
    {
        m_buffer->erase(sel.begin(), sel.end());
        sel.avoid_eol();
    }
}

static BufferIterator prepare_insert(Buffer& buffer, const Selection& sel,
                                     InsertMode mode)
{
    switch (mode)
    {
    case InsertMode::Insert:
    case InsertMode::Replace:
        return sel.begin();
    case InsertMode::Append:
    {
        // special case for end of lines, append to current line instead
        auto pos = std::max(sel.first(), sel.last());
        if (pos.column() == buffer.line_length(pos.line()) - 1)
            return pos;
        else
            return pos+1;
    }
    case InsertMode::InsertAtLineBegin:
        return buffer.iterator_at_line_begin(sel.begin());
    case InsertMode::AppendAtLineEnd:
        return buffer.iterator_at_line_end(sel.end()-1)-1;
    case InsertMode::InsertAtNextLineBegin:
        return buffer.iterator_at_line_end(sel.end()-1);
    case InsertMode::OpenLineBelow:
    {
        LineCount line = (sel.end() - 1).line();
        buffer.insert(buffer.iterator_at_line_end(line), "\n");
        return buffer.iterator_at_line_begin(line + 1);
    }
    case InsertMode::OpenLineAbove:
    {
        auto pos = buffer.iterator_at_line_begin(sel.begin());
        buffer.insert(pos, "\n");
        return pos;
    }
    }
    assert(false);
    return BufferIterator{};
}

void Editor::insert(const String& string, InsertMode mode)
{
    scoped_edition edition(*this);

    if (mode == InsertMode::Replace)
    {
        // do not call Editor::erase as we do not want to avoid end of lines
        for (auto& sel : m_selections)
            m_buffer->erase(sel.begin(), sel.end());
    }

    for (auto& sel : m_selections)
    {
        BufferIterator pos = prepare_insert(*m_buffer, sel, mode);
        m_buffer->insert(pos, string);
        if (mode == InsertMode::Replace)
        {
            sel.first() = pos;
            sel.last()  = pos + (string.empty() ? 0 : string.length() - 1 );
        }
        sel.avoid_eol();
    }
}

void Editor::insert(const memoryview<String>& strings, InsertMode mode)
{
    scoped_edition edition(*this);
    if (mode == InsertMode::Replace)
    {
        // do not call Editor::erase as we do not want to avoid end of lines
        for (auto& sel : m_selections)
            m_buffer->erase(sel.begin(), sel.end());
    }
    if (strings.empty())
        return;

    for (size_t i = 0; i < selections().size(); ++i)
    {
        auto& sel = m_selections[i];
        BufferIterator pos = prepare_insert(*m_buffer, sel, mode);
        const String& str = strings[std::min(i, strings.size()-1)];
        m_buffer->insert(pos, str);
        if (mode == InsertMode::Replace)
        {
            sel.first() = pos;
            sel.last()  = pos + (str.empty() ? 0 : str.length() - 1);
        }
        sel.avoid_eol();
    }
}

std::vector<String> Editor::selections_content() const
{
    std::vector<String> contents;
    for (auto& sel : m_selections)
        contents.push_back(m_buffer->string(sel.begin(),
                                           sel.end()));
    return contents;
}

static void sort_and_merge_overlapping(SelectionList& selections)
{
    std::sort(selections.begin(), selections.end(),
              [](const Selection& lhs, const Selection& rhs)
              { return lhs.begin() < rhs.begin(); });
    for (size_t i = 0; i < selections.size()-1;)
    {
        if (overlaps(selections[i], selections[i+1]))
        {
            selections[i].merge_with(selections[i+1]);
            selections.erase(selections.begin() + i + 1);
        }
        else
           ++i;
    }
}

void Editor::move_selections(CharCount offset, SelectMode mode)
{
    assert(mode == SelectMode::Replace or mode == SelectMode::Extend);
    for (auto& sel : m_selections)
    {
        auto last = sel.last();
        auto limit = offset < 0 ? buffer().iterator_at_line_begin(last)
                                : utf8::previous(buffer().iterator_at_line_end(last));
        last = utf8::advance(last, limit, offset);
        sel.first() = mode == SelectMode::Extend ? sel.first() : last;
        sel.last()  = last;
        sel.avoid_eol();
    }
    sort_and_merge_overlapping(m_selections);
}

void Editor::move_selections(LineCount offset, SelectMode mode)
{
    assert(mode == SelectMode::Replace or mode == SelectMode::Extend);
    for (auto& sel : m_selections)
    {
        BufferCoord pos = sel.last().coord();
        pos.line += offset;
        BufferIterator last = utf8::finish(m_buffer->iterator_at(pos, true));
        sel.first() = mode == SelectMode::Extend ? sel.first() : last;
        sel.last()  = last;
        sel.avoid_eol();
    }
    sort_and_merge_overlapping(m_selections);
}

void Editor::clear_selections()
{
    BufferIterator pos = m_selections.back().last();

    if (*pos == '\n' and not pos.is_begin() and *utf8::previous(pos) != '\n')
        pos = utf8::previous(pos);

    Selection sel = Selection(pos, pos);
    m_selections = SelectionList{ std::move(sel) };
    check_invariant();
}

void Editor::flip_selections()
{
    for (auto& sel : m_selections)
        std::swap(sel.first(), sel.last());
    check_invariant();
}

void Editor::keep_selection(int index)
{
    if (index < m_selections.size())
        m_selections = SelectionList{ std::move(m_selections[index]) };
    check_invariant();
}

void Editor::remove_selection(int index)
{
    if (m_selections.size() > 1 and index < m_selections.size())
        m_selections.erase(m_selections.begin() + index);
    check_invariant();
}

void Editor::select(const Selection& selection, SelectMode mode)
{
    if (mode == SelectMode::Replace)
        m_selections = SelectionList{ selection };
    else if (mode == SelectMode::Extend)
    {
        for (auto& sel : m_selections)
            sel.merge_with(selection);
        sort_and_merge_overlapping(m_selections);
    }
    else if (mode == SelectMode::Append)
    {
        m_selections.push_back(selection);
        sort_and_merge_overlapping(m_selections);
    }
    check_invariant();
}

void Editor::select(SelectionList selections)
{
    if (selections.empty())
        throw runtime_error("no selections");
    m_selections = std::move(selections);
    check_invariant();
}

void Editor::select(const Selector& selector, SelectMode mode)
{
    if (mode == SelectMode::Append)
    {
        auto& sel = m_selections.back();
        Selection res = selector(sel);
        if (res.captures().empty())
            res.captures() = sel.captures();
        m_selections.push_back(res);
    }
    else
    {
        for (auto& sel : m_selections)
        {
            Selection res = selector(sel);
            if (mode == SelectMode::Extend)
                sel.merge_with(res);
            else
                sel = std::move(res);

            if (not res.captures().empty())
                sel.captures() = std::move(res.captures());
        }
    }
    sort_and_merge_overlapping(m_selections);
    check_invariant();
}

struct nothing_selected : public runtime_error
{
    nothing_selected() : runtime_error("nothing was selected") {}
};

void Editor::multi_select(const MultiSelector& selector)
{
    SelectionList new_selections;
    for (auto& sel : m_selections)
    {
        SelectionList res = selector(sel);
        for (auto& new_sel : res)
        {
            // preserve captures when selectors captures nothing.
            if (new_sel.captures().empty())
                new_selections.emplace_back(new_sel.first(), new_sel.last(),
                                            sel.captures());
            else
                new_selections.push_back(std::move(new_sel));
        }
    }
    if (new_selections.empty())
        throw nothing_selected();
    sort_and_merge_overlapping(new_selections);
    m_selections = std::move(new_selections);
    check_invariant();
}

class LastModifiedRangeListener : public BufferChangeListener
{
public:
    LastModifiedRangeListener(Buffer& buffer)
       : m_buffer(buffer)
    { m_buffer.add_change_listener(*this); }

    ~LastModifiedRangeListener()
    { m_buffer.remove_change_listener(*this); }

    void on_insert(const BufferIterator& begin, const BufferIterator& end)
    {
        m_first = begin;
        m_last = utf8::previous(end);
    }

    void on_erase(const BufferIterator& begin, const BufferIterator& end)
    {
        m_first = begin;
        if (m_first >= m_buffer.end())
            m_first = utf8::previous(m_buffer.end());
        m_last = m_first;
    }

    const BufferIterator& first() const { return m_first; }
    const BufferIterator& last() const { return m_last; }

private:
    BufferIterator m_first;
    BufferIterator m_last;
    Buffer& m_buffer;
};

bool Editor::undo()
{
    LastModifiedRangeListener listener(buffer());
    bool res = m_buffer->undo();
    if (res)
        m_selections = SelectionList{ {listener.first(), listener.last()} };
    check_invariant();
    return res;
}

bool Editor::redo()
{
    LastModifiedRangeListener listener(buffer());
    bool res = m_buffer->redo();
    if (res)
        m_selections = SelectionList{ {listener.first(), listener.last()} };
    check_invariant();
    return res;
}

void Editor::check_invariant() const
{
    assert(not m_selections.empty());
    m_selections.check_invariant();
}

void Editor::begin_edition()
{
    ++m_edition_level;

    if (m_edition_level == 1)
        m_buffer->begin_undo_group();
}

void Editor::end_edition()
{
    assert(m_edition_level > 0);
    if (m_edition_level == 1)
        m_buffer->end_undo_group();

    --m_edition_level;
}

using utf8_it = utf8::utf8_iterator<BufferIterator, utf8::InvalidBytePolicy::Pass>;

IncrementalInserter::IncrementalInserter(Editor& editor, InsertMode mode)
    : m_editor(editor), m_edition(editor), m_mode(mode)
{
    m_editor.on_incremental_insertion_begin();
    Buffer& buffer = *editor.m_buffer;

    if (mode == InsertMode::Replace)
    {
        for (auto& sel : editor.m_selections)
            buffer.erase(sel.begin(), sel.end());
    }

    for (auto& sel : m_editor.m_selections)
    {
        utf8_it first, last;
        switch (mode)
        {
        case InsertMode::Insert:  first = utf8_it(sel.end()) - 1; last = sel.begin(); break;
        case InsertMode::Replace: first = utf8_it(sel.end()) - 1; last = sel.begin(); break;
        case InsertMode::Append:
        {
            first = sel.begin();
            last  = std::max(sel.first(), sel.last());
            // special case for end of lines, append to current line instead
            auto coord = last.underlying_iterator().coord();
            if (coord.column != buffer.line_length(coord.line) - 1)
                ++last;
            break;
        }

        case InsertMode::OpenLineBelow:
        case InsertMode::AppendAtLineEnd:
            first = utf8_it(buffer.iterator_at_line_end(utf8::previous(sel.end()))) - 1;
            last  = first;
            break;

        case InsertMode::OpenLineAbove:
        case InsertMode::InsertAtLineBegin:
            first = buffer.iterator_at_line_begin(sel.begin());
            if (mode == InsertMode::OpenLineAbove)
                --first;
            else
            {
                auto first_non_blank = first;
                while (*first_non_blank == ' ' or *first_non_blank == '\t')
                    ++first_non_blank;
                if (*first_non_blank != '\n')
                    first = first_non_blank;
            }
            last = first;
            break;
        }
        if (first.underlying_iterator().is_end())
           --first;
        if (last.underlying_iterator().is_end())
           --last;
        sel.first() = first.underlying_iterator();
        sel.last()  = last.underlying_iterator();
    }
    if (mode == InsertMode::OpenLineBelow or mode == InsertMode::OpenLineAbove)
    {
        insert("\n");
        if (mode == InsertMode::OpenLineAbove)
        {
            for (auto& sel : m_editor.m_selections)
            {
                // special case, the --first line above did nothing, so we need to compensate now
                if (sel.first() == utf8::next(buffer.begin()))
                    sel.first() = sel.last() = buffer.begin();
            }
        }
    }
    editor.check_invariant();
}

IncrementalInserter::~IncrementalInserter()
{
    for (auto& sel : m_editor.m_selections)
    {
        if (m_mode == InsertMode::Append and sel.last().column() > 0)
            sel.last() = utf8::previous(sel.last());
         sel.avoid_eol();
    }

    m_editor.on_incremental_insertion_end();
}

void IncrementalInserter::insert(String content)
{
    Buffer& buffer = m_editor.buffer();
    for (auto& sel : m_editor.m_selections)
    {
        m_editor.filters()(buffer, sel, content);
        buffer.insert(sel.last(), content);
    }
}

void IncrementalInserter::insert(const memoryview<String>& strings)
{
    for (size_t i = 0; i < m_editor.m_selections.size(); ++i)
    {
        size_t index = std::min(i, strings.size()-1);
        m_editor.buffer().insert(m_editor.m_selections[i].last(),
                                 strings[index]);
    }
}

void IncrementalInserter::erase()
{
    for (auto& sel : m_editor.m_selections)
    {
        BufferIterator pos = sel.last();
        m_editor.buffer().erase(utf8::previous(pos), pos);
    }
}

void IncrementalInserter::move_cursors(CharCount move)
{
    m_editor.move_selections(move, SelectMode::Replace);
}

void IncrementalInserter::move_cursors(LineCount move)
{
    m_editor.move_selections(move, SelectMode::Replace);
}

}
<|endoftext|>