// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

library services.src.correction.source_buffer;

import 'package:analysis_server/plugin/protocol/protocol.dart';
import 'package:analyzer/src/generated/source.dart';

/**
 * Helper for building Dart source with linked positions.
 */
class SourceBuilder {
  final String file;
  final int offset;
  final StringBuffer _buffer = new StringBuffer();

  final Map<String, LinkedEditGroup> linkedPositionGroups =
      <String, LinkedEditGroup>{};
  LinkedEditGroup _currentLinkedPositionGroup;
  int _currentPositionStart;
  int _exitOffset;

  SourceBuilder(this.file, this.offset);

  SourceBuilder.buffer()
      : file = null,
        offset = 0;

  /**
   * Returns the exit offset, maybe `null` if not set.
   */
  int get exitOffset {
    if (_exitOffset == null) {
      return null;
    }
    return offset + _exitOffset;
  }

  int get length => _buffer.length;

  void addSuggestion(LinkedEditSuggestionKind kind, String value) {
    var suggestion = new LinkedEditSuggestion(value, kind);
    _currentLinkedPositionGroup.addSuggestion(suggestion);
  }

  void addSuggestions(LinkedEditSuggestionKind kind, List<String> values) {
    values.forEach((value) => addSuggestion(kind, value));
  }

  /**
   * Appends [s] to the buffer.
   */
  SourceBuilder append(String s) {
    _buffer.write(s);
    return this;
  }

  /**
   * Ends position started using [startPosition].
   */
  void endPosition() {
    assert(_currentLinkedPositionGroup != null);
    _addPosition();
    _currentLinkedPositionGroup = null;
  }

  /**
   * Marks the current offset as an "exit" one.
   */
  void setExitOffset() {
    _exitOffset = _buffer.length;
  }

  /**
   * Marks start of a new linked position for the group with the given ID.
   */
  void startPosition(String id) {
    assert(_currentLinkedPositionGroup == null);
    _currentLinkedPositionGroup = linkedPositionGroups[id];
    if (_currentLinkedPositionGroup == null) {
      _currentLinkedPositionGroup = new LinkedEditGroup.empty();
      linkedPositionGroups[id] = _currentLinkedPositionGroup;
    }
    _currentPositionStart = _buffer.length;
  }

  @override
  String toString() => _buffer.toString();

  /**
   * Adds position location [SourceRange] using current fields.
   */
  void _addPosition() {
    int start = offset + _currentPositionStart;
    int end = offset + _buffer.length;
    int length = end - start;
    Position position = new Position(file, start);
    _currentLinkedPositionGroup.addPosition(position, length);
  }
}
