/*******************************************************************************
 * Copyright (c) 2017 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:
 *   Monika Wenger
 *    - initial API and implementation and/or initial documentation
 *******************************************************************************/

#include "ForteBootFileLoader.h"
#include "../../arch/devlog.h"
#include "../../../src/core/datatypes/forte_string.h"
#include "IBootFileCallback.h"
#include <mgmcmd.h>
#include <mgmcmdstruct.h>
#include "../../core/device.h"

char* gCommandLineBootFile = 0;

ForteBootFileLoader::ForteBootFileLoader(IBootFileCallback &paCallback) : mBootfile(0), mCallback(paCallback){
  openBootFile();
}

ForteBootFileLoader::~ForteBootFileLoader() {
  if(0 != mBootfile){
    DEVLOG_INFO("Closing bootfile\n");
    fclose(mBootfile);
  }
}

bool ForteBootFileLoader::openBootFile() {
  bool retVal = false;
  CIEC_STRING bootFileName;
  if(gCommandLineBootFile) {
    DEVLOG_INFO("Using provided bootfile location set in the command line: %s\n", gCommandLineBootFile);
    bootFileName = gCommandLineBootFile;
  } else {
    // select provided or default boot file name
    char * envBootFileName = getenv("FORTE_BOOT_FILE");
    if(0 != envBootFileName) {
      DEVLOG_INFO("Using provided bootfile location from environment variable: %s\n", envBootFileName);
      bootFileName = envBootFileName;
    } else {
      DEVLOG_INFO("Using provided bootfile location set in CMake: %s\n", FORTE_BOOT_FILE_LOCATION);
      bootFileName = FORTE_BOOT_FILE_LOCATION;
    }
  }

  // check if we finally have a boot file name
  if("" == bootFileName){
    DEVLOG_INFO("No bootfile specified and no default bootfile configured during build\n");
  }else{
    mBootfile = fopen(bootFileName.getValue(), "r");
    if(0 != mBootfile){
      DEVLOG_INFO("Boot file %s opened\n", bootFileName.getValue());
      retVal = true;
    }
    else{
      if(0 != getenv("FORTE_BOOT_FILE_FAIL_MISSING")){
        DEVLOG_ERROR("Boot file %s could not be opened and FORTE_BOOT_FILE_FAIL_MISSING is set. Failing...\n", bootFileName.getValue());
        exit(2);
      }
      else{
        DEVLOG_INFO("Boot file %s could not be opened. Skipping...\n", bootFileName.getValue());
      }
    }
  }
  return retVal;
}

LoadBootResult ForteBootFileLoader::loadBootFile(){
  LoadBootResult eResp = FILE_NOT_OPENED;
  if(0 != mBootfile){
    //we could open the file try to load it
    int nLineCount = 1;
    eResp = LOAD_RESULT_OK;
    CIEC_STRING line;
    while(readLine(line) && LOAD_RESULT_OK == eResp) {
      char *cmdStart = strchr(line.getValue(), ';');
      if(0 == cmdStart){
        eResp = MISSING_COLON;
        DEVLOG_ERROR("Boot file line does not contain separating ';'. Line: %d\n", nLineCount);
      } else {
        *cmdStart = '\0';
        cmdStart++;
        if(!mCallback.executeCommand(line.getValue(), cmdStart)) {
          //command was not successful
          DEVLOG_ERROR("Boot file command could not be executed. Line: %d: %s\n", nLineCount, cmdStart);
          eResp = EXTERNAL_ERROR;
        } else {
          nLineCount++;
        }
      }
    }
  }else{
    DEVLOG_ERROR("Loading cannot proceed because the boot file is no opened\n");
  }
  return eResp;
}

bool ForteBootFileLoader::readLine(CIEC_STRING &line){
  const unsigned int size = 100;
  line.clear();
  char acLineBuf[size];
  do{
    if(0 != fgets(acLineBuf, size, mBootfile)){
      line.append(acLineBuf);
    }else{
      return 0 != line.length();
    }
  }while(!hasCommandEnded(line));
  return true;
}

bool ForteBootFileLoader::hasCommandEnded(const CIEC_STRING &line) const{
  return (0 == strcmp(line.getValue() + line.length() - 11, "</Request>\n") || 0 == strcmp(line.getValue() + line.length() - 3, "/>\n"));
}
