/*
    This file is part of TON Blockchain Library.
    TON Blockchain Library is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 2 of the License, or
    (at your option) any later version.
    TON Blockchain Library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.
    You should have received a copy of the GNU Lesser General Public License
    along with TON Blockchain Library.  If not, see .
    Copyright 2017-2020 Telegram Systems LLP
*/
#include "srcread.h"
#include 
namespace src {
/*
 *
 *   SOURCE FILE READER
 *
 */
std::ostream& operator<<(std::ostream& os, const FileDescr* fdescr) {
  return os << (fdescr ? (fdescr->is_stdin ? "stdin" : fdescr->filename) : "unknown-location");
}
std::ostream& operator<<(std::ostream& os, const Fatal& fatal) {
  return os << fatal.get_msg();
}
const char* FileDescr::convert_offset(long offset, long* line_no, long* line_pos, long* line_size) const {
  long lno = 0, lpos = -1, lsize = 0;
  const char* lstart = nullptr;
  if (offset >= 0 && offset < (long)text.size()) {
    auto it = std::upper_bound(line_offs.begin(), line_offs.end(), offset);
    lno = it - line_offs.begin();
    if (lno && it != line_offs.end()) {
      lsize = it[0] - it[-1];
      lpos = offset - it[-1];
      lstart = text.data() + it[-1];
    }
  } else {
    lno = (long)line_offs.size();
  }
  if (line_no) {
    *line_no = lno;
  }
  if (line_pos) {
    *line_pos = lpos;
  }
  if (line_size) {
    *line_size = lsize;
  }
  return lstart;
}
const char* FileDescr::push_line(std::string new_line) {
  if (line_offs.empty()) {
    line_offs.push_back(0);
  }
  std::size_t cur_size = text.size();
  text += new_line;
  text += '\0';
  line_offs.push_back((long)text.size());
  return text.data() + cur_size;
}
void SrcLocation::show(std::ostream& os) const {
  os << fdescr;
  long line_no, line_pos;
  if (fdescr && convert_pos(&line_no, &line_pos)) {
    os << ':' << line_no;
    if (line_pos >= 0) {
      os << ':' << (line_pos + 1);
    }
  }
}
bool SrcLocation::show_context(std::ostream& os) const {
  long line_no, line_pos, line_size;
  if (!fdescr || !convert_pos(&line_no, &line_pos, &line_size)) {
    return false;
  }
  bool skip_left = (line_pos > 200), skip_right = (line_pos + 200u < line_size);
  const char* here = fdescr->text.data() + char_offs;
  const char* base = here - line_pos;
  const char* start = skip_left ? here - 100 : base;
  const char* end = skip_right ? here + 100 : base + line_size;
  os << "  ";
  if (skip_left) {
    os << "... ";
  }
  for (const char* ptr = start; ptr < end; ptr++) {
    os << (char)*ptr;
  }
  if (skip_right) {
    os << " ...";
  }
  os << std::endl;
  os << "  ";
  if (skip_left) {
    os << "... ";
  }
  for (const char* ptr = start; ptr < here; ptr++) {
    char c = *ptr;
    os << (c == 9 || c == 10 ? c : ' ');
  }
  os << '^' << std::endl;
  return true;
}
std::ostream& operator<<(std::ostream& os, const SrcLocation& loc) {
  loc.show(os);
  return os;
}
void SrcLocation::show_gen_error(std::ostream& os, std::string message, std::string err_type) const {
  show(os);
  if (!err_type.empty()) {
    os << ": " << err_type;
  }
  os << ": " << message << std::endl;
  show_context(os);
}
std::ostream& operator<<(std::ostream& os, const Error& error) {
  error.show(os);
  return os;
}
void ParseError::show(std::ostream& os) const {
  os << where << ": error: " << message << std::endl;
  where.show_context(os);
}
SourceReader::SourceReader(std::istream* _is, FileDescr* _fdescr)
    : ifs(_is), fdescr(_fdescr), loc(_fdescr), eof(false), cur_line_len(0), start(0), cur(0), end(0) {
  load_line();
}
void SourceReader::set_eof() {
  if (!eof) {
    eof = true;
    start = cur = end = 0;
  }
}
int SourceReader::skip_spc() {
  if (!cur) {
    return 0;
  }
  const char* ptr = cur;
  int res = 0;
  while (*ptr == ' ' || *ptr == 9) {
    ++ptr;
    ++res;
  }
  set_ptr(ptr);
  return res;
}
bool SourceReader::seek_eof() {
  while (seek_eoln()) {
    if (!load_line()) {
      return true;
    }
  }
  return false;
}
const char* SourceReader::set_ptr(const char* ptr) {
  if (ptr != cur) {
    if (ptr < cur || ptr > end) {
      error("parsing position went outside of line");
    }
    loc.char_offs += ptr - cur;
    cur = ptr;
  }
  return ptr;
}
bool SourceReader::load_line() {
  if (eof) {
    return false;
  }
  loc.set_eof();
  if (ifs->eof()) {
    set_eof();
    return false;
  }
  std::getline(*ifs, cur_line);
  if (ifs->fail()) {
    set_eof();
    if (!ifs->eof()) {
      error("cannot read line from source stream");
    }
    return false;
  }
  std::size_t len = cur_line.size();
  if (len > 0xffffff) {
    set_eof();
    error("line too long");
    return false;
  }
  if (len && cur_line.back() == '\r') {
    // CP/M line breaks support
    cur_line.pop_back();
    --len;
  }
  cur_line_len = (int)len;
  if (fdescr) {
    cur = start = fdescr->push_line(std::move(cur_line));
    end = start + len;
    loc.char_offs = (std::size_t)(cur - fdescr->text.data());
    cur_line.clear();
  } else {
    cur = start = cur_line.c_str();
    end = start + cur_line_len;
  }
  return true;
}
}  // namespace src