/*
 * Copyright (c) 2002 - 2006 IBM Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 */
package com.ibm.wala.cast.util;

import com.ibm.wala.cast.tree.CAstSourcePositionMap.Position;
import com.ibm.wala.cast.tree.impl.AbstractSourcePosition;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

public class SourceBuffer {
  private String[] lines;
  private final Position p;

  public SourceBuffer(Position p) throws IOException {
    this.p = p;

    BufferedReader reader = new BufferedReader(p.getReader());

    String currentLine = null;
    List<String> lines = new ArrayList<>();
    int offset = 0, line = 0;
    do {
      currentLine = reader.readLine();
      if (currentLine == null) {
        this.lines = new String[0];
        return;
      }
      offset += (currentLine.length() + 1);
      line++;
    } while (p.getLastOffset() >= 0 ? p.getFirstOffset() > offset : p.getFirstLine() > line);

    // partial first line
    if (p.getLastOffset() >= 0) {
      if (p.getFirstOffset() == offset) {
        lines.add("\n");
      } else {
        int startOffset = p.getFirstOffset() - (offset - currentLine.length() - 1);
        if (offset > p.getLastOffset()) {
          int endOffset = p.getLastOffset() - (offset - currentLine.length() - 1);
          lines.add(currentLine.substring(startOffset, endOffset));
        } else {
          lines.add(currentLine.substring(startOffset));
        }
      }
    } else {
      lines.add(currentLine.substring(Math.max(p.getFirstCol(), 0)));
    }

    while (p.getLastOffset() >= 0 ? p.getLastOffset() >= offset : p.getLastLine() >= line) {
      currentLine = reader.readLine();

      if (currentLine == null) {
        offset = p.getLastOffset();
        break;
      } else {
        offset += currentLine.length() + 1;
      }
      line++;
      if (p.getLastOffset() >= 0) {
        if (offset > p.getLastOffset()) {
          lines.add(
              currentLine.substring(0, currentLine.length() - (offset - p.getLastOffset()) + 1));
        } else {
          lines.add(currentLine);
        }
      } else {
        if (p.getLastLine() == line) {
          lines.add(currentLine.substring(0, p.getLastCol()));
        } else {
          lines.add(currentLine);
        }
      }
    }

    this.lines = lines.toArray(new String[0]);
  }

  @Override
  public String toString() {
    StringBuilder result = new StringBuilder();
    for (int i = 0; i < lines.length; i++) {
      if (i == lines.length - 1) {
        result.append(lines[i]);
      } else {
        result.append(lines[i]).append('\n');
      }
    }

    return result.toString();
  }

  public void substitute(Position range, String newText) {
    int startLine = range.getFirstLine() - p.getFirstLine();
    int endLine = range.getLastLine() - p.getFirstLine();

    if (startLine != endLine) {
      String newLines[] = new String[lines.length - (endLine - startLine)];
      int i = 0;
      while (i < startLine) {
        newLines[i] = lines[i];
        i++;
      }
      newLines[i++] =
          lines[startLine].substring(0, range.getFirstCol())
              + lines[endLine].substring(range.getLastCol());
      while (i < newLines.length) {
        newLines[i] = lines[i + (endLine - startLine)];
        i++;
      }

      lines = newLines;
      endLine = startLine;

      final Position hack = range;
      range =
          new AbstractSourcePosition() {
            @Override
            public int getFirstLine() {
              return hack.getFirstLine();
            }

            @Override
            public int getLastLine() {
              return hack.getFirstLine();
            }

            @Override
            public int getFirstCol() {
              return hack.getFirstCol();
            }

            @Override
            public int getLastCol() {
              return hack.getFirstCol();
            }

            @Override
            public int getFirstOffset() {
              return hack.getFirstOffset();
            }

            @Override
            public int getLastOffset() {
              return hack.getFirstOffset();
            }

            @Override
            public URL getURL() {
              return hack.getURL();
            }

            @Override
            public Reader getReader() throws IOException {
              return hack.getReader();
            }
          };
    }

    String[] newTextLines = newText.split("\n");

    if (newTextLines.length == 1) {
      lines[startLine] =
          lines[startLine].substring(0, range.getFirstCol())
              + newTextLines[0]
              + lines[startLine].substring(range.getLastCol() + 1);
    } else {
      String[] newLines = new String[lines.length + newTextLines.length - 1];
      int i = 0;
      while (i < startLine) {
        newLines[i] = lines[i];
        i++;
      }

      newLines[i++] = lines[startLine].substring(0, range.getFirstCol()) + newTextLines[0];

      for (int j = 1; j < newTextLines.length - 1; j++) {
        lines[i++] = newTextLines[j];
      }

      newLines[i++] =
          newTextLines[newTextLines.length - 1] + lines[endLine].substring(range.getLastCol() + 1);

      while (i < newLines.length) {
        newLines[i] = lines[i - newTextLines.length + 1];
        i++;
      }

      lines = newLines;
    }
  }
}
