#region License
/* **********************************************************************************
 * Copyright (c) Roman Ivantsov
 * This source code is subject to terms and conditions of the MIT License
 * for Irony. A copy of the license can be found in the License.txt file
 * at the root of this distribution. 
 * By using this source code in any fashion, you are agreeing to be bound by the terms of the 
 * MIT License.
 * You must not remove this notice from this software.
 * **********************************************************************************/
#endregion

using System;
using System.Collections.Generic;
using System.Text;
//using System.Text.RegularExpressions;

namespace Irony.Compiler {

  public interface ISourceStream {
    int Position { get;set;}
    char CurrentChar { get;} //char at Position
    char NextChar { get;}    //preview char at Position+1
    bool MatchSymbol(string symbol, bool ignoreCase);

    string Text { get;} //returns entire text buffer
    //returns substring from TokenStart.Position till (Position - 1)
    string GetLexeme();
    SourceLocation TokenStart { get; set;}
    int TabWidth { get;}
    bool EOF();
  }


  #region SourceLocation, SourceSpan structs
  public struct SourceLocation {
    public int Position;
    public int Line;
    public int Column;
    public SourceLocation(int position, int line, int column) {
      Position = position;
      Line = line;
      Column = column;
    }
    public override string ToString() {
      return "L" + Line + ":" + "C" + Column;
    }
  }//SourceLocation

  public struct SourceSpan {
    public readonly SourceLocation Start;
    public readonly int Length;
    public SourceSpan(SourceLocation start, int length) {
      Start = start;
      Length = length;
    }
    public int EndPos {
      get { return Start.Position + Length; }
    }
  }
  #endregion

  #region SourceFile class
  public class SourceFile : ISourceStream {
    public SourceFile(string text, string fileName, int tabWidth) {
      _text = text;
      _fileName = fileName;
      _tabWidth = tabWidth;
    }
    public SourceFile(string text, string fileName): this(text, fileName, 8) {
    }

    public string FileName {
      [System.Diagnostics.DebuggerStepThrough]
      get { return _fileName; }
    } string _fileName;

    public int TabWidth {
      [System.Diagnostics.DebuggerStepThrough]
      get { return _tabWidth; }
      set {_tabWidth = value;}
    } int  _tabWidth; // = 8;

    #region ISourceFile Members
    public int Position {
      [System.Diagnostics.DebuggerStepThrough]
      get { return _position; }
      set {_position = value;}
    } int _position;

    [System.Diagnostics.DebuggerStepThrough]
    public bool EOF() {
      return _position >= Text.Length;
    }
    public char CurrentChar {
      [System.Diagnostics.DebuggerStepThrough]
      get {
        try {
          return _text[_position];
        } catch { return '\0'; }
      }
    }

    public char NextChar {
      [System.Diagnostics.DebuggerStepThrough]
      get {
        try {
          return _text[_position + 1];
        } catch { return '\0'; }
      }
    }

    public bool MatchSymbol(string symbol, bool ignoreCase) {
      try {
#if !SILVERLIGHT
        int cmp = string.Compare(_text, _position, symbol, 0, symbol.Length, ignoreCase);
#else
        int cmp = 0;
        if (ignoreCase)
        {
          cmp = string.Compare(_text, _position, symbol, 0, symbol.Length, StringComparison.InvariantCultureIgnoreCase);
        }
        else
        {
          cmp = string.Compare(_text, _position, symbol, 0, symbol.Length, StringComparison.InvariantCulture);
        }
#endif 
        return cmp == 0;
      } catch { 
        //exception may be thrown if Position + symbol.length > text.Length; 
        // this happens not often, only at the very end of the file, so we don't check this explicitly
        //but simply catch the exception and return false. Remember, try/catch block is free (no overhead)
        // if exception is not thrown. 
        return false;
      }
    }

    public string Text {
      [System.Diagnostics.DebuggerStepThrough]
      get { return _text; }
    }  string _text;

    //returns substring from TokenStart.Position till (Position - 1)
    [System.Diagnostics.DebuggerStepThrough]
    public string GetLexeme() {
      string text = _text.Substring(_tokenStart.Position, _position - _tokenStart.Position);
      return text;
    }
    public SourceLocation TokenStart {
      [System.Diagnostics.DebuggerStepThrough]
      get { return _tokenStart; }
      set { _tokenStart = value; }
    } SourceLocation  _tokenStart;

    #endregion

    public override string ToString() {
      //show just 30 chars from current position
      string result;
      if (Position + 30 < Text.Length)
        result = Text.Substring(Position, 30);
      else
        result = Text.Substring(Position);
      return result;
    }
    

  }//class
  #endregion

}//namespace
