//===--- RawSyntax.cpp - Swift Raw Syntax Implementation ------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "swift/Syntax/RawSyntax.h"
#include "swift/Syntax/TokenSyntax.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/raw_ostream.h"

#include <algorithm>

using llvm::dyn_cast;
using namespace swift::syntax;

namespace {
void dumpSyntaxKind(llvm::raw_ostream &OS, const SyntaxKind Kind) {
  switch (Kind) {
#define SYNTAX(Id, Parent)                                                     \
  case SyntaxKind::Id:                                                         \
    OS << #Id;                                                                 \
    break;
#define MISSING_SYNTAX(Id, Parent) SYNTAX(Id, Parent)
#define SYNTAX_COLLECTION(Id, Element) SYNTAX(Id, {})
#include "swift/Syntax/SyntaxKinds.def"
  case SyntaxKind::Token: OS << "Token"; break;
  }
}
} // end anonymous namespace

void RawSyntax::print(llvm::raw_ostream &OS) const {
  if (const auto Tok = dyn_cast<TokenSyntax>(this)) {
    Tok->print(OS);
  }

  for (const auto &LE : Layout) {
    LE->print(OS);
  }
}

void RawSyntax::dump() const {
  return RawSyntax::dump(llvm::errs(), /*Indent*/ 0);
}

void RawSyntax::dump(llvm::raw_ostream &OS, unsigned Indent) const {
  auto indent = [&](unsigned Amount) {
    for (decltype(Amount) i = 0; i < Amount; ++i) {
      OS << ' ';
    }
  };

  indent(Indent);
  OS << '(';

  dumpSyntaxKind(OS, Kind);

  if (isMissing())
    OS << " [missing] ";

  OS << '\n';
  for (auto LE = Layout.begin(); LE != Layout.end(); ++LE) {
    if (LE != Layout.begin()) {
      OS << '\n';
    }
    switch ((*LE)->Kind) {
    case SyntaxKind::Token:
      llvm::cast<TokenSyntax>(*LE)->dump(OS, Indent + 1);
      break;
    default:
      (*LE)->dump(OS, Indent + 1);
      break;
    }
  }
  OS << ')';
}

bool RawSyntax::accumulateAbsolutePosition(
    AbsolutePosition &Pos, const RawSyntax *UpToTargetNode) const {
  auto Found = this == UpToTargetNode;
  for (auto LE : Layout) {
    switch (LE->Kind) {
    case SyntaxKind::Token: {
      auto Tok = llvm::cast<TokenSyntax>(LE);
      for (auto Leader : Tok->LeadingTrivia) {
        Leader.accumulateAbsolutePosition(Pos);
      }

      if (Found) {
        return true;
      }

      Pos.addText(Tok->getText());

      for (auto Trailer : Tok->TrailingTrivia) {
        Trailer.accumulateAbsolutePosition(Pos);
      }
      break;
    }
    default:
      if (Found)
        return true;
      LE->accumulateAbsolutePosition(Pos, UpToTargetNode);
      break;
    }
  }
  return false;
}

AbsolutePosition RawSyntax::getAbsolutePosition(RC<RawSyntax> Root) const {
  AbsolutePosition Pos;
  Root->accumulateAbsolutePosition(Pos, this);
  return Pos;
}

void AbsolutePosition::printLineAndColumn(llvm::raw_ostream &OS) const {
  OS << getLine() << ':' << getColumn();
}

void AbsolutePosition::dump(llvm::raw_ostream &OS) const {
  OS << "(absolute_position ";
  OS << "offset=" << getOffset() << " ";
  OS << "line=" << getLine() << " ";
  OS << "column=" << getColumn();
  OS << ')';
}

swift::RC<RawSyntax>
RawSyntax::append(RC<RawSyntax> NewLayoutElement) const {
  auto NewLayout = Layout;
  NewLayout.push_back(NewLayoutElement);
  return RawSyntax::make(Kind, NewLayout, SourcePresence::Present);
}
