/*******************************************************************************
 * Copyright (c) 2016 - 2018 Johannes Messmer (admin@jomess.com), fortiss GmbH
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   Johannes Messmer - initial API and implementation and/or initial documentation
 *   Jose Cabral - Cleaning of namespaces
 *******************************************************************************/

#include "pin.h"
#include <devlog.h>

const char * const EmbrickPinHandler::scmFailedToOpenFile =
    "Failed to open sysfs file.";
const char * const EmbrickPinHandler::scmFailedToWriteFile =
    "Failed to write sysfs file.";
const char * const EmbrickPinHandler::scmNotInitialised =
    "Failed to write to not initialised sysfs stream.";

EmbrickPinHandler::EmbrickPinHandler(unsigned int pin) : pinStr(static_cast<std::ostringstream &>((std::ostringstream() << std::dec  // Convert pin int to string
    << pin)).str()),
    error(0) {
  // Disable buffer to avoid latency
  stream.rdbuf()->pubsetbuf(0, 0);

  // Init pin
  init();
}

EmbrickPinHandler::~EmbrickPinHandler() {
  deInit();
}

void EmbrickPinHandler::init() {
  std::string fileName;
  stream.clear();

  // Enable pin
  fileName = "/sys/class/gpio/export";
  stream.open(fileName.c_str(), std::fstream::out);
  if (!stream.is_open())
    return fail(scmFailedToOpenFile);

  stream << pinStr;
  if (stream.fail())
    return fail(scmFailedToWriteFile);
  stream.close();

  // Use pin as output
  fileName = "/sys/class/gpio/gpio" + pinStr + "/direction";
  stream.open(fileName.c_str(), std::fstream::out);
  if (!stream.is_open())
    return fail(scmFailedToOpenFile);
  stream.clear();

  stream << "out";
  if (stream.fail())
    return fail(scmFailedToWriteFile);
  stream.close();

  // Prepare pin stream for usage
  fileName = "/sys/class/gpio/gpio" + pinStr + "/value";
  stream.open(fileName.c_str(), std::fstream::out);
  if (!stream.is_open())
    return fail(scmFailedToOpenFile);

  DEVLOG_INFO("emBrick[PinHandler]: GPIO %s ready.\n", pinStr.data());
}

void EmbrickPinHandler::deInit() {
  std::string fileName;

  // Close pin stream
  if (stream.is_open()) {
    stream.clear();
    stream.close();
  }

  // Disable pin
  fileName = "/sys/class/gpio/unexport";
  stream.open(fileName.c_str(), std::fstream::out);
  if (!stream.is_open())
    return fail(scmFailedToOpenFile);

  stream << pinStr;
  if (stream.fail())
    return fail(scmFailedToWriteFile);
  stream.close();

  DEVLOG_INFO("emBrick[PinHandler]: GPIO %s stopped.\n", pinStr.data());
}

bool EmbrickPinHandler::set(bool state) {
  if (!stream.is_open()) {
    fail(scmNotInitialised);
    return false;
  }

  stream.clear();
  stream.seekp(0, std::ios::beg);

  unsigned int val = state ? 1 : 0;
  stream << val;
  if (stream.fail()) {
    fail(scmFailedToWriteFile);
    return false;
  }

  return true;
}

void EmbrickPinHandler::fail(const char* reason) {
  error = reason;
  DEVLOG_ERROR("emBrick[PinHandler]: %s\n", reason);
}


