//
// Created by rhetenor on 14.12.18.
//

#include "output/detail/DisplayBoardPinController.h"

#include <thread>
#include <string>

#include "utility/config.h"


#define DISPLAY_SLEEP_TIME_MICRO 10000
namespace flippR_driver
{
namespace output
{
namespace detail
{

DisplayBoardPinController::DisplayBoardPinController(std::map<std::string, uint8_t> & pins_display, std::chrono::microseconds update_time) :
  pins_display_board{}, update_time{update_time}
{
  this->set_pin_map(pins_display);
  initialize_pins_output();
  CLOG(DEBUG, OUTPUT_LOGGER) << "Created DisplayBoardPinController";
}

DisplayBoardPinController::DisplayBoardPinController(std::chrono::microseconds update_time) :
  pins_display_board{}, update_time{update_time}
{
  CLOG(DEBUG, OUTPUT_LOGGER) << "Created DisplayBoardPinController without pin map";
}

void DisplayBoardPinController::initDisplay(const items::OutputDisplay &display) const
{
  initialize_output_pin(display.get_address());
  write_pin(display.get_address(), DISPLAY_LOW);
}

void DisplayBoardPinController::activate_displays() const
{
  write_pin(pins_display_board.at("run"), DISPLAY_HIGH);
}

void DisplayBoardPinController::deactivate_displays() const
{
  write_pin(pins_display_board.at("run"), DISPLAY_LOW);
}

void DisplayBoardPinController::write_display(const items::OutputDisplay &display) const
{
  std::string content = display.get_content();

  for (uint8_t i = 0; i < content.size(); i++)
  {
    write_display_digit(display.get_address(), content.at(i) - '0', content.size() - i - 1);
  }
}

void DisplayBoardPinController::write_display_digit(uint8_t display_address, uint8_t content, uint8_t position) const
{
  select_display_segment(position);
  select_display_digit(content);
  run_display(display_address);
  std::this_thread::sleep_for(this->update_time);
}

void DisplayBoardPinController::select_display_segment(uint8_t segment) const
{
  write_pin(pins_display_board.at("segment_select_C"), !(segment & 0b001u));
  write_pin(pins_display_board.at("segment_select_B"), !(segment & 0b010u));
  write_pin(pins_display_board.at("segment_select_A"), !(segment & 0b100u));
}

void DisplayBoardPinController::select_display_digit(uint8_t content) const
{
  write_pin(pins_display_board.at("digit_select_A"), (content & 0b0001u));
  write_pin(pins_display_board.at("digit_select_B"), (content & 0b0010u));
  write_pin(pins_display_board.at("digit_select_C"), (content & 0b0100u));
  write_pin(pins_display_board.at("digit_select_D"), (content & 0b1000u));
}

void DisplayBoardPinController::run_display(uint8_t address) const
{
  write_pin(address, DISPLAY_HIGH);
  write_pin(address, DISPLAY_LOW);
}

void DisplayBoardPinController::set_pin_map(std::map<std::string, uint8_t> & pins_display)
{
  this->pins_display_board.insert(pins_display.begin(), pins_display.end());
  this->initialize_pins_output();
  this->init_pin_states();
}

void DisplayBoardPinController::initialize_pins_output()
{
  for(auto& pin : this->pins_display_board)
  {
    initialize_output_pin(pin.second);
  }
}

void DisplayBoardPinController::init_pin_states()
{
  for(auto& pin : this->pins_display_board)
  {
    write_pin(pin.second, DISPLAY_LOW);
  }
  write_pin(this->pins_display_board["run"], DISPLAY_HIGH);
}

}
}
}