/*******************************************************************************
 * Copyright (c) 2016 - 2018 Johannes Messmer (admin@jomess.com), fortiss GmbH
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * 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 paPin) :
    paError(0), mPinStr(static_cast<std::ostringstream &>((std::ostringstream() << std::dec // Convert pin int to string
      << paPin)).str()) {
  // Disable buffer to avoid latency
  paStream.rdbuf()->pubsetbuf(0, 0);

  // Init pin
  init();
}

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

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

  // Enable pin
  fileName = "/sys/class/gpio/export";
  paStream.open(fileName.c_str(), std::fstream::out);
  if(!paStream.is_open()) {
    return fail(scmFailedToOpenFile);
  }
  paStream << mPinStr;
  if(paStream.fail()) {
    return fail(scmFailedToWriteFile);
  }
  paStream.close();

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

  paStream << "out";
  if(paStream.fail()) {
    return fail(scmFailedToWriteFile);
  }
  paStream.close();

  // Prepare pin stream for usage
  fileName = "/sys/class/gpio/gpio" + mPinStr + "/value";
  paStream.open(fileName.c_str(), std::fstream::out);
  if(!paStream.is_open()) {
    return fail(scmFailedToOpenFile);
  }
  DEVLOG_INFO("emBrick[PinHandler]: GPIO %s ready.\n", mPinStr.data());
}

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

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

  // Disable pin
  fileName = "/sys/class/gpio/unexport";
  paStream.open(fileName.c_str(), std::fstream::out);
  if(!paStream.is_open()) {
    return fail(scmFailedToOpenFile);
  }
  paStream << mPinStr;
  if(paStream.fail()) {
    return fail(scmFailedToWriteFile);
  }
  paStream.close();

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

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

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

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

  return true;
}

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

