/***** BEGIN LICENSE BLOCK *****
 * Version: CPL 1.0/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Common Public
 * License Version 1.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.eclipse.org/legal/cpl-v10.html
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
 * Copyright (C) 2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
 * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
 * 
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the CPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the CPL, the GPL or the LGPL.
 ***** END LICENSE BLOCK *****/
 package org.jruby.lexer.yacc;

import java.io.Serializable;

/**
 * 
 * Position within a source.  This could have column as well, but it currently
 * does not.  A normal ruby intrepretter does not use this information in 
 * error/warning information.  An IDE using this may need it though.  This is
 * trivially added if need be.
 * 
 * @see org.jruby.lexer.yacc.ISourcePosition
 */
public class SourcePosition implements ISourcePosition, Serializable {
    private static final long serialVersionUID = 3762529027281400377L;
    
    // The file of the source
    private final String file;
    
    // The state/end rows of the source
    private final int startLine;
    private final int endLine;
    
    // The start/end offsets of the source
    private int startOffset;
    private final int endOffset;

    /**
     * Creates a default source position - required for serialization.
     */
    public SourcePosition() {
    	this("", 0, 0);
    }
    
    /**
     * Creates a new source position.
     * 
     * @param file location of the source (must not be null)
     * @param endLine what line within the source
     */
	public SourcePosition(String file, int startLine, int endLine) {
		if (file == null) { //otherwise equals() and getInstance() will fail
			throw new NullPointerException();  
		}
		this.file = file;
		this.startLine = startLine;
		this.endLine = endLine;
		this.startOffset = 0;
		this.endOffset = 0;
	}

    /**
     * Creates a new source position.
     * 
     * @param file location of the source (must not be null)
     * @param line what line within the source
     */
	public SourcePosition(String file, int startLine, int endLine, int startOffset, int endOffset) {
		if (file == null) { //otherwise equals() and getInstance() will fail
			throw new NullPointerException();  
		}
		this.file = file;
		this.startLine = startLine;
		this.endLine = endLine;
		this.startOffset = startOffset;
		this.endOffset = endOffset;
	}

	/**
	 * @see org.jruby.lexer.yacc.ISourcePosition#getFile()
	 */
    public String getFile() {
        return file;
    }
    
    /**
     * Not used in interpreter
     * 
     * @see org.jruby.lexer.yacc.ISourcePosition#getStartLine()
     */
    public int getStartLine() {
    	return startLine;
    }

    /**
     * @see org.jruby.lexer.yacc.ISourcePosition#getEndLine()
     */
    public int getEndLine() {
        return endLine;
    }

    /**
     * @param object the object which should be compared
     * @return simple Object.equals() implementation
     */
    public boolean equals(Object object) {
    	if (object == this) {
    		return true;
    	}
        if (!(object instanceof SourcePosition)) {
        	return false;
        }
        
        SourcePosition other = (SourcePosition) object;

        return file.equals(other.file) && endLine == other.endLine;
    }

    /**
     * Something we can use for identity in hashing, etc...
     * 
     * @see java.lang.Object#hashCode()
     */
    public int hashCode() {
        return file.hashCode() ^ endLine;
    }

    /**
     * @see java.lang.Object#toString()
     */
    public String toString() {
        return file + ":[" + startLine + "," + endLine + "]:[" + 
            getStartOffset() + "," + getEndOffset() + "]";
    }
    
    /**
     * @see org.jruby.lexer.yacc.ISourcePosition#adjustStartOffset(int)
     */
    public void adjustStartOffset(int relativeValue) {
        startOffset += relativeValue;
        if(startOffset < 0) startOffset = 0;
    }
    
    /**
     * Not used in interpreter
     * 
     * @see org.jruby.lexer.yacc.ISourcePosition#getStartOffset()
     */
    public int getStartOffset() {
    	return startOffset;
    }
    
    /**
     * Not used in interpreter 
     * 
     * @see org.jruby.lexer.yacc.ISourcePosition#getEndOffset()
     */
    public int getEndOffset() {
    	return endOffset;
    }
    
    /**
     * @see org.jruby.lexer.yacc.ISourcePosition#union(ISourcePosition)
     */
    public ISourcePosition union(ISourcePosition other) {
        // Enebo: All AST nodes but IterNode are in ascending order position-wise.  We should not 
        // need to safe-guard that other is a smaller source position
        return new SourcePosition(file, startLine, other.getEndLine(), startOffset, other.getEndOffset());
    }
    
    /**
     * Not used in interpreter 
     * Creates a new position the encloses both parameter positions.
     * 
     * @param the positions providing the boundaries for the new position.
     */
    public static SourcePosition combinePosition(ISourcePosition firstPos, ISourcePosition secondPos){
        String fileName = firstPos.getFile();
        int startOffset = firstPos.getStartOffset();
        int endOffset = firstPos.getEndOffset();
        int startLine = firstPos.getStartLine();
        int endLine = firstPos.getEndLine();
        
        if(startOffset > secondPos.getStartOffset()){
            startOffset = secondPos.getStartOffset();
            startLine = secondPos.getStartLine();
        }
        
        if(endOffset < secondPos.getEndOffset()){
            endOffset = secondPos.getEndOffset();
            endLine = secondPos.getEndLine();
        }
        
        SourcePosition combinedPosition = new SourcePosition(fileName, startLine, endLine, startOffset, endOffset);
        
        return combinedPosition;             
    }
}
